Compare commits
4 commits
fb29e197c0
...
1963659673
Author | SHA1 | Date | |
---|---|---|---|
1963659673 | |||
6648926f08 | |||
d7a58d7352 | |||
7253251e9d |
59 changed files with 190 additions and 5698 deletions
|
@ -12,7 +12,6 @@ members = [
|
|||
"engine",
|
||||
"entity_manager",
|
||||
"examples/simple_window",
|
||||
"gavania-core",
|
||||
"gltf-loader",
|
||||
"loading-screen",
|
||||
"lua-wrapper",
|
||||
|
@ -45,7 +44,10 @@ reqwest = { version = "0.12.5", features = ["blocking"] }
|
|||
shared_library = "0.1.9"
|
||||
gltf = { version = "1.4.1", features = ["extras", "names"] }
|
||||
mlua = { version = "0.9.9", features = ["lua54", "send", "vendored"] }
|
||||
public-ip = { version = "0.2.2", default-features = false, features = ["all-providers", "tokio-dns-resolver"] }
|
||||
public-ip = { version = "0.2.2", default-features = false, features = [
|
||||
"all-providers",
|
||||
"tokio-dns-resolver",
|
||||
] }
|
||||
async-std = { version = "1.12.0" }
|
||||
if-addrs = { version = "0.13.0" }
|
||||
hostname = { version = "0.4.0" }
|
||||
|
|
|
@ -87,6 +87,10 @@ impl World {
|
|||
self.entity_object_manager.create_entity()
|
||||
}
|
||||
|
||||
pub fn clone_without_components(&mut self, entity: &EntityObject) -> EntityObject {
|
||||
entity.clone_without_components(self.entity_object_manager.fetch_add_entity_id())
|
||||
}
|
||||
|
||||
pub fn entities(&self) -> impl Iterator<Item = &EntityObject> {
|
||||
self.entities.values()
|
||||
}
|
||||
|
|
|
@ -5,7 +5,11 @@ use crate::prelude::*;
|
|||
use anyhow::Result;
|
||||
|
||||
pub trait AssetLoader {
|
||||
fn load_entity(&mut self, assets: AssetHandler<'_>, entity_file: &str) -> Result<EntityObject>;
|
||||
fn load_entity(
|
||||
&mut self,
|
||||
assets: &mut AssetHandler<'_>,
|
||||
entity_file: &str,
|
||||
) -> Result<EntityObject>;
|
||||
}
|
||||
|
||||
pub struct AssetHandler<'a> {
|
||||
|
|
|
@ -168,21 +168,13 @@ impl Engine {
|
|||
|
||||
let asset_manager = AssetManager::new(&engine_settings)?;
|
||||
|
||||
if let Some(mut gui_info) = create_info.gui_info {
|
||||
if let Font::Path(path) = &mut gui_info.font {
|
||||
path.set_prefix(&create_info.resource_base_path);
|
||||
}
|
||||
gui_info.resource_directory.assume_prefix_free();
|
||||
let gui_handler = GuiHandler::new(create_info.gui_info, &context)?;
|
||||
let gui_post_process = Arc::new(GuiPostProcess(gui_handler.clone()));
|
||||
context
|
||||
.render_core()
|
||||
.add_post_processing_routine(gui_post_process.clone());
|
||||
|
||||
let gui_handler = GuiHandler::new(gui_info, &context)?;
|
||||
|
||||
let gui_post_process = Arc::new(GuiPostProcess(gui_handler.clone()));
|
||||
context
|
||||
.render_core()
|
||||
.add_post_processing_routine(gui_post_process.clone());
|
||||
|
||||
world.resources.insert(gui_handler);
|
||||
}
|
||||
world.resources.insert(gui_handler);
|
||||
|
||||
// default keyboard navigation
|
||||
let mut direction_mapping = HashMap::new();
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct EngineCreateInfo<'a> {
|
|||
pub vulkan_debug_info: VulkanDebugInfo,
|
||||
pub volume_info: HashMap<String, f32>,
|
||||
|
||||
pub gui_info: Option<GuiHandlerCreateInfo<'a>>,
|
||||
pub gui_info: GuiHandlerCreateInfo<'a>,
|
||||
|
||||
pub enable_backtrace: bool,
|
||||
pub enable_mouse: bool,
|
||||
|
@ -60,7 +60,7 @@ impl<'a> Default for EngineCreateInfo<'a> {
|
|||
renderdoc: false,
|
||||
},
|
||||
volume_info: HashMap::new(),
|
||||
gui_info: None,
|
||||
gui_info: GuiHandlerCreateInfo::new(),
|
||||
enable_backtrace: true,
|
||||
enable_mouse: true,
|
||||
enable_keyboard: true,
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
use std::any::Any;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use anyhow::Result;
|
||||
use utilities::prelude::cgmath::{Vector2, Vector3};
|
||||
|
||||
pub trait Map: Any + Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
fn check_walkability(
|
||||
&self,
|
||||
position: Vector2<f32>,
|
||||
direction: Vector2<f32>,
|
||||
radius: f32,
|
||||
) -> Result<bool>;
|
||||
|
||||
fn get_height(&self, x: f32, y: f32) -> Result<f32>;
|
||||
|
||||
fn spawn_positions(&self) -> Result<Vec<Vector3<f32>>>;
|
||||
fn leave_markers(&self) -> Result<Vec<Entity>>;
|
||||
|
||||
fn disable(&self, scene: &mut Scene) -> Result<()>;
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
pub mod components;
|
||||
|
||||
pub mod map;
|
||||
pub mod prelude;
|
||||
// mod query;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
pub use super::map::Map;
|
||||
|
||||
pub use super::components::{animation::Animation, audio::Audio, draw::Draw};
|
||||
|
||||
pub use super::components::{
|
||||
|
|
|
@ -126,7 +126,7 @@ impl AssetLoader for EntityManager {
|
|||
/// Loads an entity file and creates an Entity
|
||||
fn load_entity(
|
||||
&mut self,
|
||||
mut assets: AssetHandler<'_>,
|
||||
assets: &mut AssetHandler<'_>,
|
||||
entity_file: &str,
|
||||
) -> Result<EntityObject> {
|
||||
// load entity file
|
||||
|
|
7
gavania-core/.vscode/settings.json
vendored
7
gavania-core/.vscode/settings.json
vendored
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.background": "#4F1E1C",
|
||||
"titleBar.activeBackground": "#6F2A27",
|
||||
"titleBar.activeForeground": "#FEFCFC"
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
[package]
|
||||
name = "gavania-core"
|
||||
version = "0.1.0"
|
||||
authors = ["hodasemi <superschneider@t-online.de>"]
|
||||
build = "src/osbuild.rs"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
rusqlite = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
cgmath = { workspace = true }
|
||||
reqwest = { workspace = true }
|
||||
http = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
destructure_traitobject = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
assetpath = { workspace = true }
|
||||
iterchunks = { workspace = true }
|
||||
|
||||
engine = { path = "../engine" }
|
||||
promise = { path = "../promise" }
|
||||
lua-wrapper = { path = "../lua-wrapper" }
|
||||
controllable_thread = { path = "../controllable_thread" }
|
||||
map = { path = "../map" }
|
||||
rpg_components = { path = "../rpg_components" }
|
||||
entity_manager = { path = "../entity_manager" }
|
||||
|
||||
[features]
|
||||
wayland = []
|
|
@ -1,38 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
|
||||
create_settings_section!(
|
||||
GeneralSettings,
|
||||
"General",
|
||||
{
|
||||
special_chance: f32,
|
||||
radius: f32,
|
||||
boss_hp_bar_radius: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
BasicMobSettings,
|
||||
"BasicMobs",
|
||||
{
|
||||
min_count: u32,
|
||||
max_count: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
EliteMobSettings,
|
||||
"EliteMobs",
|
||||
{
|
||||
min_count: u32,
|
||||
max_count: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_container!(
|
||||
MobSettings,
|
||||
{
|
||||
general: GeneralSettings,
|
||||
basic_mobs: BasicMobSettings,
|
||||
elite_mobs: EliteMobSettings,
|
||||
}
|
||||
);
|
|
@ -1,2 +0,0 @@
|
|||
pub mod mobs;
|
||||
pub use mobs::*;
|
|
@ -1,139 +0,0 @@
|
|||
pub mod on_hit_particles;
|
||||
pub mod particle_spawn;
|
||||
pub mod prelude;
|
||||
pub mod projectile;
|
||||
pub mod selfcast;
|
||||
|
||||
use anyhow::Result;
|
||||
use cgmath::Vector2;
|
||||
use engine::prelude::*;
|
||||
use entity_manager::*;
|
||||
use rpg_components::components::character_status::CharacterStatus;
|
||||
use rpg_components::components::level::{Level, LevelUpEvent};
|
||||
use rpg_components::components::npc_type::{NPCBoss, NPCElite, NPCNormal, NPCType};
|
||||
use rpg_components::components::statistics::Statistics;
|
||||
use rpg_components::damage_type::DamageType;
|
||||
|
||||
use crate::game::content::prelude::*;
|
||||
|
||||
use crate::Game;
|
||||
|
||||
pub fn handle_npc_death<'a>(
|
||||
game: &Game,
|
||||
s: &mut impl SceneEntities,
|
||||
collider: Entity,
|
||||
mut collider_components: MultiMut<'a>,
|
||||
) -> Result<()> {
|
||||
// check if collider is dead, if so potentially create a loot chest and destroy the entity
|
||||
|
||||
if let Ok(collider_status) = collider_components.get::<CharacterStatus>() {
|
||||
if collider_status.is_dead() {
|
||||
let collider_level = collider_components.get::<Level>()?;
|
||||
let collider_location = collider_components.get::<Location>()?;
|
||||
|
||||
if collider_components.get::<FactionPlayer>().is_ok() {
|
||||
let npc_movement = collider_components.get::<Movement>()?;
|
||||
npc_movement.set_direction(Vector2::zero());
|
||||
|
||||
let mut grave_stone = game
|
||||
.entity_manager()
|
||||
.load_entity(game.engine().assets(), "Grave Stone")?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
grave_stone.debug_name = Some(format!("Player Grave Stone ({})", collider));
|
||||
}
|
||||
|
||||
Location::new_and_setup(&mut grave_stone)?
|
||||
.set_position(collider_location.position());
|
||||
|
||||
s.add_entity(grave_stone)?;
|
||||
} else if collider_components.get::<FactionNPC>().is_ok() {
|
||||
let npc_hitbox = collider_components.get::<HitBox>()?;
|
||||
|
||||
let ghost = Ghost::new(
|
||||
&game,
|
||||
collider_location,
|
||||
npc_hitbox.radius(),
|
||||
npc_hitbox.height(),
|
||||
s.now(),
|
||||
)?;
|
||||
|
||||
s.add_entity(ghost)?;
|
||||
s.remove_entity(collider)?;
|
||||
|
||||
if let Some(loot_chest) = LootStash::handle_npc_death(
|
||||
&game,
|
||||
collider_level,
|
||||
collider_location,
|
||||
if collider_components.get::<NPCNormal>().is_ok() {
|
||||
NPCType::Normal
|
||||
} else if collider_components.get::<NPCElite>().is_ok() {
|
||||
NPCType::Elite
|
||||
} else if collider_components.get::<NPCBoss>().is_ok() {
|
||||
NPCType::Boss
|
||||
} else {
|
||||
unreachable!("missing component!");
|
||||
},
|
||||
)? {
|
||||
s.add_entity(loot_chest)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn damage_and_experience(
|
||||
base_damage: u32,
|
||||
damage_type: DamageType,
|
||||
collider_multi_mut: &mut MultiMut<'_>,
|
||||
mut owner_components: MultiMut<'_>,
|
||||
scene: &mut SceneContents<'_>,
|
||||
) -> Result<()> {
|
||||
if let Ok(collider_current_status) = collider_multi_mut.get::<CharacterStatus>() {
|
||||
if let Ok(collider_stats) = collider_multi_mut.get::<Statistics>() {
|
||||
if let Ok(collider_level) = collider_multi_mut.get::<Level>() {
|
||||
let resistance = collider_stats.calculate_resistance(damage_type);
|
||||
|
||||
if resistance < base_damage {
|
||||
let damage = base_damage - resistance;
|
||||
let owner_status = owner_components.get::<CharacterStatus>()?;
|
||||
|
||||
collider_current_status.apply_damage(damage as f32);
|
||||
scene.write_event(DamageEvent {
|
||||
damage,
|
||||
damage_type,
|
||||
position: collider_multi_mut.get::<Location>()?.position(),
|
||||
});
|
||||
|
||||
if collider_current_status.is_dead() {
|
||||
if owner_components.get::<FactionPlayer>().is_ok() {
|
||||
if !owner_status.is_dead() {
|
||||
let level = owner_components.get::<Level>()?;
|
||||
|
||||
if level.add_experience(
|
||||
collider_level.level(),
|
||||
if collider_multi_mut.get::<NPCNormal>().is_ok() {
|
||||
NPCType::Normal
|
||||
} else if collider_multi_mut.get::<NPCElite>().is_ok() {
|
||||
NPCType::Elite
|
||||
} else if collider_multi_mut.get::<NPCBoss>().is_ok() {
|
||||
NPCType::Boss
|
||||
} else {
|
||||
unreachable!("missing component!");
|
||||
},
|
||||
)? {
|
||||
scene.write_event(LevelUpEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
use anyhow::Result;
|
||||
use cgmath::{Deg, Vector3};
|
||||
use rpg_components::{components::abilityloader::AbilityLoader, damage_type::DamageType};
|
||||
|
||||
pub struct ArcInfo {
|
||||
pub radius: f32,
|
||||
pub base_damage: u32,
|
||||
pub damage_type: DamageType,
|
||||
pub owner: Entity,
|
||||
}
|
||||
|
||||
pub struct OnHitParticles {
|
||||
pub particle_system_info: ParticleSystemInfo,
|
||||
pub collision_sound: String,
|
||||
|
||||
pub position: Vector3<f32>,
|
||||
pub offset: Vector3<f32>,
|
||||
|
||||
pub arc_info: Option<ArcInfo>,
|
||||
}
|
||||
|
||||
impl OnHitParticles {
|
||||
pub fn setup(game_handle: &GameHandle, scene: &mut Scene) {
|
||||
scene.register_event::<Self>();
|
||||
|
||||
scene.add_event_reader({
|
||||
let game_handle = game_handle.clone();
|
||||
|
||||
move |scene, on_hit_particles: &Self| on_hit_particles.spawn(scene, &game_handle)
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn(&self, scene: &mut SceneContents<'_>, game_handle: &GameHandle) -> Result<()> {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
let mut entity = AbilityLoader::create_on_hit_particles(
|
||||
game.engine(),
|
||||
self.particle_system_info.clone(),
|
||||
&self.collision_sound,
|
||||
self.position,
|
||||
self.offset,
|
||||
scene.particle_system_vulkan_objects(),
|
||||
|s| game.build_data_path(s),
|
||||
)?;
|
||||
|
||||
if let Some(arc_info) = &self.arc_info {
|
||||
entity.insert_component(AreaOfEffectArc::new(
|
||||
Deg(180.0),
|
||||
arc_info.radius,
|
||||
arc_info.base_damage,
|
||||
arc_info.damage_type,
|
||||
arc_info.owner,
|
||||
));
|
||||
|
||||
Faction::copy(
|
||||
&mut scene.entity_mut(arc_info.owner)?.multi_mut(),
|
||||
&mut entity,
|
||||
);
|
||||
}
|
||||
|
||||
scene.add_entity(entity)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
use rpg_components::components::abilityloader::AbilityLoader;
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ParticleSpawnExecutor {
|
||||
AoE,
|
||||
Projectile,
|
||||
}
|
||||
|
||||
pub struct ParticleSpawn {
|
||||
pub entity: Entity,
|
||||
pub info: AbilityLoader,
|
||||
pub executor: ParticleSpawnExecutor,
|
||||
}
|
||||
|
||||
impl ParticleSpawn {
|
||||
pub fn setup_event(game_handle: &GameHandle, scene: &mut Scene) {
|
||||
scene.register_event::<Self>();
|
||||
|
||||
scene.add_event_reader({
|
||||
let game_handle = game_handle.clone();
|
||||
|
||||
move |scene, particle_spawn: &Self| {
|
||||
let particle_system_vulkan_objects =
|
||||
unsafe { remove_life_time_mut(scene) }.particle_system_vulkan_objects();
|
||||
|
||||
let particle_system = {
|
||||
let owner = scene.entity_mut(particle_spawn.entity)?;
|
||||
|
||||
let mut multi_mut = owner.multi_mut();
|
||||
let draw = multi_mut.get::<Draw>()?;
|
||||
|
||||
match particle_spawn.executor {
|
||||
ParticleSpawnExecutor::AoE => {
|
||||
let aoe = multi_mut.get::<AreaOfEffect>()?;
|
||||
|
||||
aoe.create_particle_effect(
|
||||
&game_handle,
|
||||
&particle_spawn.info,
|
||||
draw,
|
||||
particle_system_vulkan_objects,
|
||||
)?
|
||||
}
|
||||
ParticleSpawnExecutor::Projectile => Projectile::create_particles(
|
||||
&particle_spawn.info,
|
||||
&game_handle,
|
||||
draw,
|
||||
particle_system_vulkan_objects,
|
||||
)?,
|
||||
}
|
||||
};
|
||||
|
||||
scene.insert_component(particle_spawn.entity, particle_system)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
pub use super::on_hit_particles::{ArcInfo, OnHitParticles};
|
||||
pub use super::particle_spawn::{ParticleSpawn, ParticleSpawnExecutor};
|
||||
pub use super::projectile::{Projectile, ProjectileMarker};
|
||||
pub use super::selfcast::SelfCast;
|
|
@ -1,371 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
use entity_manager::*;
|
||||
use rpg_components::{
|
||||
components::{abilityloader::AbilityLoader, statistics::Statistics},
|
||||
damage_type::DamageType,
|
||||
items::{ability_addon::AbilityAddonCollection, ability_book::AbilityBook},
|
||||
};
|
||||
|
||||
use crate::game::content::abilities::handle_npc_death;
|
||||
|
||||
use anyhow::Result;
|
||||
use cgmath::{vec3, Deg, Matrix2, Vector2};
|
||||
|
||||
use super::{
|
||||
super::prelude::*,
|
||||
particle_spawn::{ParticleSpawn, ParticleSpawnExecutor},
|
||||
};
|
||||
|
||||
use crate::game::game::GameHandle;
|
||||
use std::time::Duration;
|
||||
|
||||
const ODD_ANGLES: [Deg<f32>; 11] = [
|
||||
Deg(0.0),
|
||||
Deg(10.0),
|
||||
Deg(-10.0),
|
||||
Deg(20.0),
|
||||
Deg(-20.0),
|
||||
Deg(30.0),
|
||||
Deg(-30.0),
|
||||
Deg(40.0),
|
||||
Deg(-40.0),
|
||||
Deg(50.0),
|
||||
Deg(-50.0),
|
||||
];
|
||||
|
||||
const EVEN_ANGLES: [Deg<f32>; 10] = [
|
||||
Deg(5.0),
|
||||
Deg(-5.0),
|
||||
Deg(15.0),
|
||||
Deg(-15.0),
|
||||
Deg(25.0),
|
||||
Deg(-25.0),
|
||||
Deg(35.0),
|
||||
Deg(-35.0),
|
||||
Deg(45.0),
|
||||
Deg(-45.0),
|
||||
];
|
||||
|
||||
pub struct ProjectileMarker;
|
||||
|
||||
impl EntityComponent for ProjectileMarker {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for ProjectileMarker {
|
||||
fn debug_name() -> &'static str {
|
||||
"ProjectileMarker"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Projectile;
|
||||
|
||||
impl Projectile {
|
||||
fn hit_event(
|
||||
me: Entity,
|
||||
collider: Entity,
|
||||
owner: Entity,
|
||||
level: u32,
|
||||
addons: &AbilityAddonCollection,
|
||||
game_handle: &GameHandle,
|
||||
scene: &mut SceneContents<'_>,
|
||||
) -> Result<CollisionEventType> {
|
||||
// this handle is only needed for calculating loot bonus when creating a loot stash
|
||||
// it only queries all users and their loot bonus passive
|
||||
let s = unsafe { remove_life_time_mut(scene) };
|
||||
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
let mut entity_multi_mut = scene.entities_multi_mut();
|
||||
|
||||
let collider_object = match entity_multi_mut.get(collider) {
|
||||
Ok(collider) => collider,
|
||||
Err(_) => return Ok(CollisionEventType::Ignore),
|
||||
};
|
||||
|
||||
let projectile_object = match entity_multi_mut.get(me) {
|
||||
Ok(projectile_object) => projectile_object,
|
||||
Err(_) => return Ok(CollisionEventType::Ignore),
|
||||
};
|
||||
|
||||
let mut collider_components = collider_object.multi_mut();
|
||||
let mut projectile_components = projectile_object.multi_mut();
|
||||
|
||||
let my_faction = Faction::get(&mut projectile_components).unwrap();
|
||||
let ability_info = projectile_components.get::<AbilityLoader>()?;
|
||||
|
||||
// don't collide with same 'Team' or neutral NPC's
|
||||
let collider_faction = Faction::get(&mut collider_components);
|
||||
|
||||
if !my_faction.check_collision(collider_faction) {
|
||||
// if there is no entity faction, that means we hit an environment object
|
||||
// e.g. a tree or a stone.
|
||||
// we then bounce or destroy the projectile
|
||||
if addons.bounce() {
|
||||
let position = projectile_components.get::<Location>().unwrap().position();
|
||||
let movement = projectile_components.get::<Movement>().unwrap();
|
||||
|
||||
let direction = movement.direction();
|
||||
let inverted = -direction;
|
||||
let random_angle = Deg(Random::range_f32(-60.0, 60.0));
|
||||
|
||||
let new_direction = Matrix2::from_angle(random_angle) * inverted;
|
||||
|
||||
movement.set_direction(new_direction);
|
||||
|
||||
let location_info = projectile_components.get::<AbilityLocationInfo>()?;
|
||||
|
||||
location_info.new_direction_at(position);
|
||||
}
|
||||
|
||||
return Ok(CollisionEventType::Ignore);
|
||||
}
|
||||
|
||||
// skip any damage related calculation when the owner isn't present anymore
|
||||
if let Ok(owner_object) = entity_multi_mut.get(owner) {
|
||||
let owner_stats = owner_object.get_component::<Statistics>()?;
|
||||
|
||||
let mut base_damage = ability_info.damage(level, addons, owner_stats);
|
||||
|
||||
let explosion_radius =
|
||||
ability_info.settings.parameter.target_radius + addons.explosion_radius();
|
||||
|
||||
let npc_location = projectile_components.get::<Location>().unwrap();
|
||||
|
||||
// don't cast an explosion when the radius is 0
|
||||
if explosion_radius == 0.0 {
|
||||
base_damage = AbilityLoader::check_for_crit(base_damage, owner_stats);
|
||||
|
||||
super::damage_and_experience(
|
||||
base_damage,
|
||||
ability_info.settings.parameter.damage_type,
|
||||
&mut collider_components,
|
||||
owner_object.multi_mut(),
|
||||
s,
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
collider_components.clear_all_usages();
|
||||
}
|
||||
|
||||
// spawn on hit particle effects
|
||||
if !ability_info.settings.parameter.on_hit_particles.is_empty() {
|
||||
let file_name =
|
||||
game.build_data_path(&ability_info.settings.parameter.on_hit_particles);
|
||||
let info = ParticleSystemInfo::load(file_name)?;
|
||||
|
||||
s.write_event(OnHitParticles {
|
||||
particle_system_info: info,
|
||||
collision_sound: ability_info.settings.parameter.on_collision_sound.clone(),
|
||||
position: npc_location.position(),
|
||||
offset: npc_location.offset(),
|
||||
|
||||
arc_info: None,
|
||||
});
|
||||
}
|
||||
|
||||
unsafe {
|
||||
collider_components.clear_all_usages();
|
||||
}
|
||||
|
||||
handle_npc_death(&game, s, collider, collider_components)?;
|
||||
}
|
||||
// if there is a explosion radius, create an explosion according to its radius
|
||||
else {
|
||||
let file_name = match ability_info.settings.parameter.damage_type {
|
||||
DamageType::Physical => todo!(),
|
||||
DamageType::Fire => game.build_data_path("particles/fire_explosion.particle"),
|
||||
DamageType::Water => game.build_data_path("particles/ice_explosion.particle"),
|
||||
DamageType::Air => todo!(),
|
||||
};
|
||||
|
||||
let mut info = ParticleSystemInfo::load(file_name)?;
|
||||
info.particles.minimum_velocity =
|
||||
explosion_radius / info.system.duration.as_secs_f32();
|
||||
info.particles.maximum_velocity = info.particles.minimum_velocity;
|
||||
|
||||
// spawn on hit particle effects
|
||||
s.write_event(OnHitParticles {
|
||||
particle_system_info: info,
|
||||
collision_sound: ability_info.settings.parameter.on_collision_sound.clone(),
|
||||
position: npc_location.position(),
|
||||
offset: npc_location.offset(),
|
||||
|
||||
arc_info: Some(ArcInfo {
|
||||
radius: explosion_radius,
|
||||
base_damage,
|
||||
damage_type: ability_info.settings.parameter.damage_type,
|
||||
owner,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
scene.remove_entity(me)?;
|
||||
|
||||
Ok(CollisionEventType::Ignore)
|
||||
}
|
||||
|
||||
pub fn create_particles(
|
||||
info: &AbilityLoader,
|
||||
game_handle: &GameHandle,
|
||||
draw: &mut Draw,
|
||||
particle_system_vulkan_objects: &ParticleSystemVulkanObjects,
|
||||
) -> Result<ParticleSystem> {
|
||||
let game = game_handle.upgrade();
|
||||
let file_name = game.build_data_path(&info.settings.parameter.trail);
|
||||
|
||||
let mut particle_info = ParticleSystemInfo::load(file_name)?;
|
||||
particle_info.system.duration = Duration::from_secs(300).into();
|
||||
particle_info.particles.minimum_velocity = info.settings.parameter.speed / 3.0;
|
||||
particle_info.particles.maximum_velocity = info.settings.parameter.speed / 2.0;
|
||||
|
||||
ParticleSystem::new_with_vk_objects(
|
||||
particle_info,
|
||||
game.engine(),
|
||||
particle_system_vulkan_objects,
|
||||
draw,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Projectile {
|
||||
pub fn execute(
|
||||
ability: &AbilityLoader,
|
||||
_owner: Entity,
|
||||
owner_components: &mut MultiMut<'_>,
|
||||
direction: Vector2<f32>,
|
||||
book: &AbilityBook,
|
||||
game_handle: &GameHandle,
|
||||
entities: &mut Entities<'_>,
|
||||
events: &mut ContentEvents<'_>,
|
||||
) -> Result<()> {
|
||||
let mut projectile_count = 1 + book.addons().additional_projectiles() as usize;
|
||||
|
||||
let angles: &[Deg<f32>] = if (projectile_count % 2) == 0 {
|
||||
projectile_count = projectile_count.min(EVEN_ANGLES.len());
|
||||
|
||||
&EVEN_ANGLES
|
||||
} else {
|
||||
projectile_count = projectile_count.min(ODD_ANGLES.len());
|
||||
|
||||
&ODD_ANGLES
|
||||
};
|
||||
|
||||
let owner_hitbox = owner_components.get::<HitBox>()?;
|
||||
let owner_location = owner_components.get::<Location>()?;
|
||||
|
||||
let projectile_size = book.addons().size();
|
||||
let projectile_speed = book.addons().projectile_speed();
|
||||
let projectile_distance = ability.settings.parameter.distance + book.addons().distance();
|
||||
|
||||
let ability_data = book.ability().data();
|
||||
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
(0..projectile_count)
|
||||
.into_iter()
|
||||
.try_for_each(|i| -> Result<()> {
|
||||
let angle = &angles[i];
|
||||
|
||||
let mut projectile_entity = game
|
||||
.entity_manager()
|
||||
.load_entity(game.engine().assets(), &ability.settings.meta.entity)?;
|
||||
|
||||
{
|
||||
// alter the hitbox parameters
|
||||
let projectile_hitbox = projectile_entity.get_component_mut::<HitBox>()?;
|
||||
|
||||
// Multiply the radius with potential ability modifications
|
||||
projectile_hitbox.set_radius(projectile_size * projectile_hitbox.radius());
|
||||
|
||||
// set on hit event for the hitbox (only the server side will check for it)
|
||||
|
||||
projectile_hitbox.set_event({
|
||||
let owner = _owner;
|
||||
let level = book.level();
|
||||
let addons = book.addons().clone();
|
||||
let game_handle = game_handle.clone();
|
||||
|
||||
move |scene, me, collider| {
|
||||
Self::hit_event(
|
||||
me,
|
||||
collider,
|
||||
owner,
|
||||
level,
|
||||
&addons,
|
||||
&game_handle,
|
||||
scene,
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
let hitbox_radius = projectile_hitbox.radius();
|
||||
|
||||
let projectile_location = Location::new_and_setup(&mut projectile_entity)?;
|
||||
|
||||
// set projectile to owner location and set rotation accordingly
|
||||
projectile_location.set_scale_uniform(projectile_size);
|
||||
|
||||
let direction = Matrix2::from_angle(*angle) * direction;
|
||||
let position = owner_location.position()
|
||||
+ direction.extend(0.0) * (hitbox_radius * 1.1 + owner_hitbox.radius());
|
||||
|
||||
projectile_location.update(position, direction);
|
||||
|
||||
// apply height offset
|
||||
projectile_location.set_offset(vec3(
|
||||
0.0,
|
||||
0.0,
|
||||
ability_data.settings.parameter.height_offset,
|
||||
));
|
||||
|
||||
let mut projectile_movement = Movement::new(
|
||||
ability.settings.parameter.speed + projectile_speed,
|
||||
projectile_entity.get_component_mut::<Audio>().ok(),
|
||||
);
|
||||
|
||||
if !ability_data.settings.parameter.idle_sound.is_empty() {
|
||||
let audio = projectile_entity.get_component_mut::<Audio>().unwrap();
|
||||
|
||||
audio.add_custom_sound(
|
||||
game.build_data_path(&ability_data.settings.parameter.idle_sound),
|
||||
"idle_sound",
|
||||
)?;
|
||||
};
|
||||
|
||||
// set movement
|
||||
projectile_movement.set_direction(direction);
|
||||
|
||||
let ability_location_info = AbilityLocationInfo::new(
|
||||
position,
|
||||
projectile_distance,
|
||||
projectile_entity.get_component_mut::<Audio>().unwrap(),
|
||||
);
|
||||
|
||||
if !ability_data.settings.parameter.trail.is_empty() {
|
||||
events.write_event(ParticleSpawn {
|
||||
entity: projectile_entity.as_entity(),
|
||||
info: ability_data.clone(),
|
||||
executor: ParticleSpawnExecutor::Projectile,
|
||||
});
|
||||
}
|
||||
|
||||
projectile_entity.insert_component(ability_location_info);
|
||||
projectile_entity.insert_component(ability.clone());
|
||||
projectile_entity.insert_component(projectile_movement);
|
||||
projectile_entity.insert_component(ProjectileMarker);
|
||||
Faction::copy(owner_components, &mut projectile_entity);
|
||||
}
|
||||
|
||||
entities.add_entity(projectile_entity)?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
#![allow(unused)]
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
use entity_manager::*;
|
||||
use rpg_components::{
|
||||
components::{abilityloader::AbilityLoader, statistics::Statistics},
|
||||
items::ability_book::AbilityBook,
|
||||
};
|
||||
|
||||
use crate::game::{
|
||||
content::prelude::*,
|
||||
game::{Game, GameHandle},
|
||||
};
|
||||
|
||||
use cgmath::{Deg, Vector2};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use super::particle_spawn::ParticleSpawn;
|
||||
|
||||
pub struct SelfCast;
|
||||
|
||||
impl SelfCast {
|
||||
pub fn execute(
|
||||
ability: &AbilityLoader,
|
||||
owner: Entity,
|
||||
owner_components: &mut MultiMut<'_>,
|
||||
direction: Vector2<f32>,
|
||||
book: &AbilityBook,
|
||||
game_handle: &GameHandle,
|
||||
entities: &mut Entities<'_>,
|
||||
events: &mut ContentEvents<'_>,
|
||||
) -> Result<()> {
|
||||
let location = owner_components.get::<Location>()?;
|
||||
let owner_stats = owner_components.get::<Statistics>()?;
|
||||
let hit_box = owner_components.get::<HitBox>()?;
|
||||
|
||||
let mut base_damage = ability.damage(book.level(), book.addons(), owner_stats);
|
||||
|
||||
let range = ability.settings.parameter.radius * book.addons().size();
|
||||
|
||||
let (mut entity_object, particle_spawn) = AreaOfEffect::new(
|
||||
direction,
|
||||
location.position(),
|
||||
Deg(ability.settings.parameter.arc),
|
||||
range,
|
||||
hit_box.height() * 0.75,
|
||||
owner,
|
||||
ability.clone(),
|
||||
base_damage,
|
||||
game_handle.clone(),
|
||||
)?;
|
||||
|
||||
Faction::copy(owner_components, &mut entity_object);
|
||||
|
||||
entities.add_entity(entity_object)?;
|
||||
|
||||
if let Some(particle_spawn) = particle_spawn {
|
||||
events.write_event(particle_spawn);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
use crate::game::game::Game;
|
||||
|
||||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use engine::prelude::gltf_loader::prelude::*;
|
||||
|
||||
use cgmath::{vec2, vec3, Matrix4, SquareMatrix};
|
||||
|
||||
use std::{
|
||||
sync::{Arc, Weak},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
create_settings_section!(
|
||||
SlideSettings,
|
||||
"Meta",
|
||||
{
|
||||
duration: f32,
|
||||
count: u32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_container!(
|
||||
SlideContainer,
|
||||
{
|
||||
settings: SlideSettings,
|
||||
}
|
||||
);
|
||||
|
||||
pub struct Slide {
|
||||
weak_scene: Weak<Scene>,
|
||||
|
||||
name: String,
|
||||
start: Duration,
|
||||
duration: Duration,
|
||||
count: u32,
|
||||
single_width: f32,
|
||||
|
||||
vertex_buffer: Arc<Buffer<PositionColorUV>>,
|
||||
}
|
||||
|
||||
impl Slide {
|
||||
pub fn from_create_info(info: SlideCreateInfo) -> Result<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn new(name: &str, game: &GameHandle, scene: &Arc<Scene>) -> Result<Arc<Entity>> {
|
||||
let info =
|
||||
SlideContainer::load(&format!("{}/{}/info.conf", game.slides_directory(), name))?
|
||||
.settings;
|
||||
|
||||
let image = {
|
||||
let mut slide_images = game.slide_images()?;
|
||||
|
||||
let image_path = format!("{}/{}/texture.png", game.slides_directory(), name);
|
||||
|
||||
match slide_images.get(&image_path) {
|
||||
Some(image) => image.clone(),
|
||||
None => {
|
||||
let image = Image::from_file(&image_path)?
|
||||
.attach_sampler(Sampler::pretty_sampler().build(game.engine().device())?)
|
||||
.build(game.engine().device(), game.engine().queue())?;
|
||||
|
||||
slide_images.insert(image_path, image.clone());
|
||||
|
||||
image
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (vertex_buffer, width_ratio) = {
|
||||
let total_width = image.width();
|
||||
|
||||
let single_width = total_width / info.count;
|
||||
let height = image.height();
|
||||
|
||||
let aspect_ratio = single_width as f32 / height as f32;
|
||||
let half = aspect_ratio / 2.0;
|
||||
let width_ratio = single_width as f32 / total_width as f32;
|
||||
|
||||
let data = [
|
||||
PositionColorUV::new(vec3(-half, 0.0, 0.0), vec2(0.0, 1.0)),
|
||||
PositionColorUV::new(vec3(half, 0.0, 0.0), vec2(width_ratio, 1.0)),
|
||||
PositionColorUV::new(vec3(half, 0.0, 1.0), vec2(width_ratio, 0.0)),
|
||||
PositionColorUV::new(vec3(half, 0.0, 1.0), vec2(width_ratio, 0.0)),
|
||||
PositionColorUV::new(vec3(-half, 0.0, 1.0), vec2(0.0, 0.0)),
|
||||
PositionColorUV::new(vec3(-half, 0.0, 0.0), vec2(0.0, 1.0)),
|
||||
];
|
||||
|
||||
let buffer = Buffer::builder()
|
||||
.set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
|
||||
.set_memory_usage(MemoryUsage::CpuOnly)
|
||||
.set_data(&data)
|
||||
.build(game.engine().device().clone())?;
|
||||
|
||||
(buffer, width_ratio)
|
||||
};
|
||||
|
||||
let width_ratio = 0.0;
|
||||
|
||||
let meshes = {
|
||||
let mut mesh = AssetMesh::new(game.engine().device(), scene.scene_type())?;
|
||||
|
||||
mesh.add_primitive(
|
||||
vertex_buffer.clone(),
|
||||
Some(image),
|
||||
None,
|
||||
PrimitiveMaterial {
|
||||
emissive_factor: [1.0, 1.0, 1.0],
|
||||
..PrimitiveMaterial::default()
|
||||
},
|
||||
false,
|
||||
)?;
|
||||
|
||||
vec![mesh]
|
||||
};
|
||||
|
||||
let entity_builder = Entity::new_custom(game.engine().context(), meshes);
|
||||
|
||||
let entity_builder = Entity::new_custom();
|
||||
|
||||
let entity = entity_builder.build()?;
|
||||
|
||||
let slide = Slide {
|
||||
weak_scene: Arc::downgrade(scene),
|
||||
|
||||
name: name.to_string(),
|
||||
start: Duration::default(),
|
||||
duration: Duration::from_secs_f32(info.duration),
|
||||
count: info.count,
|
||||
single_width: width_ratio,
|
||||
|
||||
vertex_buffer,
|
||||
};
|
||||
|
||||
entity.components()?.insert(slide);
|
||||
|
||||
Ok(entity)
|
||||
}
|
||||
|
||||
pub fn update(&self, now: Duration) -> Result<()> {
|
||||
let time_diff = now - self.start;
|
||||
|
||||
let time = time_diff.as_secs_f32() % self.duration.as_secs_f32();
|
||||
let time_slice = self.duration.as_secs_f32() / self.count as f32;
|
||||
|
||||
let slice = (time / time_slice).floor();
|
||||
|
||||
let mut mapped = self.vertex_buffer.map_complete()?;
|
||||
|
||||
mapped[0].uv.x = slice * self.single_width;
|
||||
mapped[1].uv.x = (slice + 1.0) * self.single_width;
|
||||
mapped[2].uv.x = (slice + 1.0) * self.single_width;
|
||||
mapped[3].uv.x = (slice + 1.0) * self.single_width;
|
||||
mapped[4].uv.x = slice * self.single_width;
|
||||
mapped[5].uv.x = slice * self.single_width;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for Slide {
|
||||
fn enable(&mut self, scene: &Scene) -> Result<()> {
|
||||
if let Some(scene) = self.weak_scene.upgrade() {
|
||||
self.start = scene.now();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"Slide"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SlideCreateInfo {
|
||||
// TODO
|
||||
}
|
||||
|
||||
impl<'a> ComponentCreateInfo<'a> for SlideCreateInfo {}
|
|
@ -1,38 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum AbilityType {
|
||||
Projectile,
|
||||
SelfCast,
|
||||
}
|
||||
|
||||
impl Default for AbilityType {
|
||||
fn default() -> Self {
|
||||
Self::Projectile
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for AbilityType {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"Projectile" => Ok(AbilityType::Projectile),
|
||||
"Self-Cast" => Ok(AbilityType::SelfCast),
|
||||
_ => Err(anyhow::Error::msg(format!(
|
||||
"Failed parsing AbilityType from {}",
|
||||
s
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AbilityType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
AbilityType::Projectile => write!(f, "Projectile"),
|
||||
AbilityType::SelfCast => write!(f, "Self-Cast"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
|
||||
use anyhow::Result;
|
||||
use cgmath::Vector3;
|
||||
|
||||
use cgmath::InnerSpace;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct AbilityLocationSplit {
|
||||
start: Vector3<f32>,
|
||||
end: Option<Vector3<f32>>,
|
||||
}
|
||||
|
||||
impl AbilityLocationSplit {
|
||||
fn new(p: Vector3<f32>) -> Self {
|
||||
Self {
|
||||
start: p,
|
||||
end: None,
|
||||
}
|
||||
}
|
||||
|
||||
// sets end point and creates a new split at this position
|
||||
|
||||
fn finalize(&mut self, p: Vector3<f32>) -> Self {
|
||||
self.end = Some(p);
|
||||
Self::new(p)
|
||||
}
|
||||
|
||||
// calculates length, if end is not set it uses `p`
|
||||
|
||||
fn length(&self, p: Vector3<f32>) -> f32 {
|
||||
let end = match self.end {
|
||||
Some(p) => p,
|
||||
None => p,
|
||||
};
|
||||
|
||||
(end - self.start).magnitude()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AbilityLocationInfo {
|
||||
splits: Vec<AbilityLocationSplit>,
|
||||
|
||||
allowed_distance: f32,
|
||||
|
||||
#[serde(skip)]
|
||||
audio: UnsafeComponentStore<Audio>,
|
||||
}
|
||||
|
||||
impl AbilityLocationInfo {
|
||||
pub fn new(position: Vector3<f32>, distance: f32, audio: &mut Audio) -> Self {
|
||||
AbilityLocationInfo {
|
||||
splits: vec![AbilityLocationSplit::new(position)],
|
||||
|
||||
allowed_distance: distance,
|
||||
|
||||
audio: audio.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn post_setup(&mut self, audio: &mut Audio) {
|
||||
self.audio = audio.into();
|
||||
}
|
||||
|
||||
pub fn new_direction_at(&mut self, p: Vector3<f32>) {
|
||||
let new_split = self.splits.last_mut().unwrap().finalize(p);
|
||||
self.splits.push(new_split);
|
||||
}
|
||||
|
||||
pub fn check_distance(&self, p: Vector3<f32>) -> bool {
|
||||
let mut total = 0.0;
|
||||
|
||||
for split in self.splits.iter() {
|
||||
total += split.length(p);
|
||||
}
|
||||
|
||||
total < self.allowed_distance
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for AbilityLocationInfo {
|
||||
fn enable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
unsafe { self.audio.as_mut() }.play("idle_sound", true)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for AbilityLocationInfo {
|
||||
fn debug_name() -> &'static str {
|
||||
"AbilityLocationInfo"
|
||||
}
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use assetpath::AssetPath;
|
||||
use engine::prelude::*;
|
||||
|
||||
use cgmath::Vector3;
|
||||
use std::path::Path;
|
||||
|
||||
const ABILITY_SUFFIX: &str = ".abil";
|
||||
|
||||
create_settings_section!(
|
||||
AbilityMetaSettings,
|
||||
"Meta",
|
||||
{
|
||||
icon: AssetPath,
|
||||
entity: String,
|
||||
particles: String,
|
||||
},
|
||||
Serialize, Deserialize,
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AbilityParameterSettings,
|
||||
"Parameter",
|
||||
{
|
||||
// shared
|
||||
ability_type: AbilityType,
|
||||
light: bool,
|
||||
idle_sound: String,
|
||||
|
||||
base_damage: u32,
|
||||
damage_per_level: u32,
|
||||
|
||||
base_mana_cost: u32,
|
||||
mana_cost_per_level: u32,
|
||||
|
||||
damage_type: DamageType,
|
||||
|
||||
cool_down: PersistentDuration,
|
||||
|
||||
// projectile
|
||||
trail: String,
|
||||
on_hit_particles: String,
|
||||
height_offset: f32,
|
||||
on_collision_sound: String,
|
||||
target_radius: f32,
|
||||
target_particles: String,
|
||||
speed: f32,
|
||||
distance: f32,
|
||||
|
||||
// self-cast
|
||||
t_min: f32,
|
||||
procedure_count: u32,
|
||||
delay: PersistentDuration,
|
||||
duration: PersistentDuration,
|
||||
arc: f32,
|
||||
radius: f32,
|
||||
},
|
||||
Serialize, Deserialize,
|
||||
);
|
||||
|
||||
create_settings_container!(
|
||||
AbilitySettings,
|
||||
{
|
||||
meta: AbilityMetaSettings,
|
||||
parameter: AbilityParameterSettings,
|
||||
},
|
||||
Serialize, Deserialize,
|
||||
);
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
||||
pub struct AbilityLoader {
|
||||
file_path: AssetPath,
|
||||
name: String,
|
||||
|
||||
pub settings: AbilitySettings,
|
||||
}
|
||||
|
||||
impl AbilityLoader {
|
||||
pub fn load(data_path: &str, file: AssetPath) -> Result<Self> {
|
||||
let mut settings = AbilitySettings::load(file.clone())?;
|
||||
|
||||
settings.meta.icon.set_prefix(data_path);
|
||||
|
||||
let mut ability_loader = AbilityLoader::default();
|
||||
ability_loader.file_path = file.clone();
|
||||
ability_loader.name = Path::new(&file.full_path())
|
||||
.file_stem()
|
||||
.expect("could not get file stem in ability loader")
|
||||
.to_str()
|
||||
.expect("failed converting OsStr to string")
|
||||
.to_string();
|
||||
|
||||
ability_loader.settings = settings;
|
||||
|
||||
Ok(ability_loader)
|
||||
}
|
||||
|
||||
pub fn save(&self) -> Result<()> {
|
||||
self.settings.store()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list_all_ability_files(ability_directory: &AssetPath) -> Result<Vec<AssetPath>> {
|
||||
Ok(search_dir_recursively(
|
||||
&ability_directory.full_path(),
|
||||
ABILITY_SUFFIX,
|
||||
)?)
|
||||
}
|
||||
|
||||
pub fn create_ability(&self) -> Ability {
|
||||
Ability { data: self.clone() }
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn set_name(&mut self, name: &str) {
|
||||
self.name = name.to_string();
|
||||
}
|
||||
|
||||
pub fn set_path(&mut self, ability_directory: &str) {
|
||||
self.file_path = AssetPath::from((
|
||||
ability_directory,
|
||||
format!("{}{}", self.name.clone(), ABILITY_SUFFIX),
|
||||
));
|
||||
self.settings.file_name = self.file_path.clone();
|
||||
}
|
||||
|
||||
pub fn icon_name(&self) -> &AssetPath {
|
||||
&self.settings.meta.icon
|
||||
}
|
||||
|
||||
pub fn entity_name(&self) -> &str {
|
||||
&self.settings.meta.entity
|
||||
}
|
||||
|
||||
pub fn check_for_crit(dmg: u32, owner_stats: &Statistics) -> u32 {
|
||||
if Coin::flip(owner_stats.critical_hit_chance.raw() / 100.0) {
|
||||
let crit_dmg = ((owner_stats.critical_hit_damage.raw() + 1.0) * dmg as f32) as u32;
|
||||
|
||||
println!("CRIT: {} from base {}", crit_dmg, dmg);
|
||||
|
||||
crit_dmg
|
||||
} else {
|
||||
dmg
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base_damage(&self, ability_level: u32) -> u32 {
|
||||
self.settings.parameter.base_damage
|
||||
+ self.settings.parameter.damage_per_level * (ability_level - 1)
|
||||
}
|
||||
|
||||
pub fn damage(
|
||||
&self,
|
||||
ability_level: u32,
|
||||
addons: &AbilityAddonCollection,
|
||||
owner_stats: &Statistics,
|
||||
) -> u32 {
|
||||
// calculate damage of base ability
|
||||
let ability_base_damage = self.base_damage(ability_level);
|
||||
|
||||
// get bonus damage from statistics
|
||||
let stats_damage = match self.settings.parameter.damage_type {
|
||||
DamageType::Air => owner_stats.air_damage.raw(),
|
||||
DamageType::Fire => owner_stats.fire_damage.raw(),
|
||||
DamageType::Water => owner_stats.water_damage.raw(),
|
||||
DamageType::Physical => owner_stats.physical_damage.raw(),
|
||||
};
|
||||
|
||||
// damage from addons multiplied with level
|
||||
let addon_damage = addons.damage() * ability_level;
|
||||
|
||||
// sum up
|
||||
ability_base_damage + stats_damage + addon_damage
|
||||
}
|
||||
|
||||
pub fn create_on_hit_particles2(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
position: Vector3<f32>,
|
||||
offset: Vector3<f32>,
|
||||
particle_system_vulkan_objects: &ParticleSystemVulkanObjects,
|
||||
build_path: impl Fn(&str) -> AssetPath,
|
||||
) -> Result<Option<EntityObject>> {
|
||||
Ok(if self.settings.parameter.on_hit_particles.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let file_name = build_path(&self.settings.parameter.on_hit_particles);
|
||||
let info = ParticleSystemInfo::load(file_name)?;
|
||||
|
||||
Some(Self::create_on_hit_particles(
|
||||
engine,
|
||||
info,
|
||||
&self.settings.parameter.on_collision_sound,
|
||||
position,
|
||||
offset,
|
||||
particle_system_vulkan_objects,
|
||||
build_path,
|
||||
)?)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_on_hit_particles(
|
||||
engine: &Engine,
|
||||
info: ParticleSystemInfo,
|
||||
on_collision_sound: &str,
|
||||
position: Vector3<f32>,
|
||||
offset: Vector3<f32>,
|
||||
particle_system_vulkan_objects: &ParticleSystemVulkanObjects,
|
||||
build_path: impl Fn(&str) -> AssetPath,
|
||||
) -> Result<EntityObject> {
|
||||
let mut particle_entity = engine.assets().empty_entity();
|
||||
|
||||
{
|
||||
let mut draw = Draw::new(Vec::new());
|
||||
|
||||
let particles = ParticleSystem::new_with_vk_objects(
|
||||
info,
|
||||
engine,
|
||||
particle_system_vulkan_objects,
|
||||
&mut draw,
|
||||
)?;
|
||||
|
||||
if !on_collision_sound.is_empty() {
|
||||
let mut audio = Audio::new(engine.context(), None)?;
|
||||
audio.set_sound(build_path(on_collision_sound), ON_ENABLE_SOUND)?;
|
||||
|
||||
particle_entity.insert_component(audio);
|
||||
}
|
||||
|
||||
particle_entity.insert_component(draw);
|
||||
particle_entity.insert_component(particles);
|
||||
|
||||
let location = Location::new_and_setup(&mut particle_entity)?;
|
||||
location.set_position(position);
|
||||
location.set_offset(offset);
|
||||
|
||||
particle_entity.insert_component(BoundingBox {
|
||||
min: [-0.5, -0.5, 0.0],
|
||||
max: [0.5, 0.5, 1.0],
|
||||
});
|
||||
}
|
||||
|
||||
Ok(particle_entity)
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for AbilityLoader {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for AbilityLoader {
|
||||
fn debug_name() -> &'static str {
|
||||
"AbilityLoader"
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
mod simple;
|
||||
|
||||
pub use simple::SimpleAI;
|
|
@ -1,244 +0,0 @@
|
|||
use crate::game::{content::prelude::*, game::GameHandle};
|
||||
use crate::Result;
|
||||
|
||||
use engine::prelude::*;
|
||||
|
||||
use cgmath::{vec2, InnerSpace, Vector2, Vector3};
|
||||
use entity_manager::*;
|
||||
use rpg_components::ability_type::AbilityType;
|
||||
use rpg_components::components::ability_slots::AbilitySlots;
|
||||
use rpg_components::components::character_status::CharacterStatus;
|
||||
use rpg_components::damage_type::DamageType;
|
||||
|
||||
// space between user and ai hitbox
|
||||
const SAFETY_RADIUS: f32 = 0.3;
|
||||
|
||||
const ZERO_MOVEMENT: Vector2<f32> = vec2(0.0, 0.0);
|
||||
|
||||
pub struct SimpleAI {
|
||||
pub aggro_radius: f32,
|
||||
pub deaggro_radius: f32,
|
||||
|
||||
pub owner: Entity,
|
||||
|
||||
pub target: Option<Entity>,
|
||||
}
|
||||
|
||||
impl SimpleAI {
|
||||
pub fn new(aggro_radius: f32, deaggro_radius: f32, owner: Entity) -> Self {
|
||||
Self {
|
||||
aggro_radius,
|
||||
deaggro_radius,
|
||||
|
||||
owner,
|
||||
|
||||
target: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
animation: &mut Animation,
|
||||
draw: &mut Draw,
|
||||
animation_info: &mut AnimationInfo,
|
||||
character_status: &mut CharacterStatus,
|
||||
movement: &mut Movement,
|
||||
location: &Location,
|
||||
abilities: &mut AbilitySlots,
|
||||
hitbox: &mut HitBox,
|
||||
scene_contents: &mut SceneContents<'_>,
|
||||
game: &GameHandle,
|
||||
user_entity: Entity,
|
||||
) -> Result<()> {
|
||||
match self.check_target(location, hitbox, scene_contents, user_entity)? {
|
||||
Some((direction, distance_to_target, min_distance)) => {
|
||||
let mut can_move = true;
|
||||
|
||||
for ability_opt in abilities.iter_mut() {
|
||||
if let Some(book) = ability_opt {
|
||||
// do nothing with this ability when user is out of range
|
||||
if distance_to_target > book.ability().data().settings.parameter.distance {
|
||||
continue;
|
||||
}
|
||||
|
||||
let now = scene_contents.now();
|
||||
|
||||
// check if ability can be cast
|
||||
if !animation_info.is_locked()
|
||||
&& book.validate_use(now, character_status, location)?
|
||||
{
|
||||
{
|
||||
// TODO: further separation of animation types (bows, ...)
|
||||
let animation_type =
|
||||
match book.ability().data().settings.parameter.damage_type {
|
||||
DamageType::Physical => AnimationType::Attack,
|
||||
_ => AnimationType::Cast,
|
||||
};
|
||||
|
||||
animation_info.set_animation(
|
||||
animation,
|
||||
draw,
|
||||
Some(animation_type),
|
||||
now,
|
||||
true,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
|
||||
let owner = unsafe {
|
||||
scene_contents.entities.entity_mut_unchecked(self.owner)?
|
||||
};
|
||||
|
||||
let mut multi_mut = owner.multi_mut();
|
||||
|
||||
// actually cast the ability
|
||||
println!("ai casts ability");
|
||||
|
||||
match book.ability().data().settings.parameter.ability_type {
|
||||
AbilityType::Projectile => Projectile::execute(
|
||||
book.ability().data(),
|
||||
self.owner,
|
||||
&mut multi_mut,
|
||||
location.direction(),
|
||||
book,
|
||||
game,
|
||||
&mut scene_contents.entities,
|
||||
&mut scene_contents.events,
|
||||
)?,
|
||||
AbilityType::SelfCast => todo!(),
|
||||
}
|
||||
|
||||
can_move = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if can_move {
|
||||
if distance_to_target > min_distance {
|
||||
movement.set_direction(direction);
|
||||
} else {
|
||||
movement.set_direction(ZERO_MOVEMENT);
|
||||
}
|
||||
} else {
|
||||
movement.set_direction(ZERO_MOVEMENT);
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
movement.set_direction(ZERO_MOVEMENT);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_target(
|
||||
&mut self,
|
||||
location: &Location,
|
||||
hitbox: &HitBox,
|
||||
scene: &impl SceneEntities,
|
||||
user_entity: Entity,
|
||||
) -> Result<Option<(Vector2<f32>, f32, f32)>> {
|
||||
let my_position = location.position();
|
||||
|
||||
match &self.target {
|
||||
Some(target) => {
|
||||
let distance = match scene.entity(*target).ok() {
|
||||
Some(object) => {
|
||||
let target_position = object.get_component::<Location>()?.position();
|
||||
|
||||
(target_position - my_position).magnitude()
|
||||
}
|
||||
|
||||
None => 1_000_000.0,
|
||||
};
|
||||
|
||||
if distance > self.deaggro_radius {
|
||||
self.target = None;
|
||||
self.find_target(my_position, scene, user_entity)?;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.find_target(my_position, scene, user_entity)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.target
|
||||
.as_ref()
|
||||
.map(|target| {
|
||||
let (position, radius) = {
|
||||
let object = scene.entity(*target)?;
|
||||
|
||||
(
|
||||
object.get_component::<Location>()?.position(),
|
||||
object.get_component::<HitBox>()?.radius(),
|
||||
)
|
||||
};
|
||||
|
||||
let direction = position - my_position;
|
||||
let magnitude = direction.magnitude();
|
||||
|
||||
Ok((
|
||||
direction.truncate().normalize(),
|
||||
magnitude,
|
||||
radius + hitbox.radius() + SAFETY_RADIUS,
|
||||
))
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
// If there is no target set, choose the closest user
|
||||
#[inline]
|
||||
fn find_target(
|
||||
&mut self,
|
||||
my_position: Vector3<f32>,
|
||||
scene: &impl SceneEntities,
|
||||
user_entity: Entity,
|
||||
) -> Result<()> {
|
||||
assert!(self.target.is_none());
|
||||
|
||||
let mut closest = None;
|
||||
|
||||
if let Some(user_position) = scene
|
||||
.entity(user_entity)
|
||||
.ok()
|
||||
.map(|user_object| user_object.get_component::<Location>().unwrap().position())
|
||||
{
|
||||
let distance = (user_position - my_position).magnitude();
|
||||
|
||||
if distance <= self.aggro_radius {
|
||||
match &closest {
|
||||
Some((_entity, current_distance)) => {
|
||||
if distance < *current_distance {
|
||||
closest = Some((user_entity, distance));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
closest = Some((user_entity, distance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((entity, _)) = closest {
|
||||
self.target = Some(entity);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for SimpleAI {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for SimpleAI {
|
||||
fn debug_name() -> &'static str {
|
||||
"SimpleAI"
|
||||
}
|
||||
}
|
|
@ -1,384 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use cgmath::{Deg, Vector2, Vector3};
|
||||
use rpg_components::components::abilityloader::AbilityLoader;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::game::{
|
||||
content::{
|
||||
abilities::particle_spawn::{ParticleSpawn, ParticleSpawnExecutor},
|
||||
prelude::*,
|
||||
},
|
||||
game::GameHandle,
|
||||
};
|
||||
use engine::prelude::*;
|
||||
|
||||
pub struct ParticleInfo {
|
||||
pub radius: f32,
|
||||
}
|
||||
|
||||
pub struct AreaOfEffect {
|
||||
direction: Vector2<f32>,
|
||||
|
||||
position: Vector3<f32>,
|
||||
|
||||
arc: Deg<f32>,
|
||||
radius: f32,
|
||||
// height: f32,
|
||||
ability_info: AbilityLoader,
|
||||
base_damage: u32,
|
||||
|
||||
start: Duration,
|
||||
|
||||
owner: Entity,
|
||||
|
||||
game: GameHandle,
|
||||
|
||||
light: Option<Light>,
|
||||
|
||||
proc_slots: Vec<(Duration, HashSet<Entity>)>,
|
||||
|
||||
audio: Option<UnsafeComponentStore<Audio>>,
|
||||
}
|
||||
|
||||
impl AreaOfEffect {
|
||||
pub fn post_setup(&mut self, audio: Option<&mut Audio>) {
|
||||
self.audio = audio.map(|audio| audio.into());
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(
|
||||
direction: Vector2<f32>,
|
||||
position: Vector3<f32>,
|
||||
arc: Deg<f32>,
|
||||
radius: f32,
|
||||
height: f32,
|
||||
owner: Entity,
|
||||
ability_info: AbilityLoader,
|
||||
base_damage: u32,
|
||||
game: GameHandle,
|
||||
) -> Result<(EntityObject, Option<ParticleSpawn>)> {
|
||||
let mut particle_spawn = None;
|
||||
|
||||
let proc_slots = Self::calculate_proc_slots(
|
||||
ability_info.settings.parameter.procedure_count,
|
||||
ability_info.settings.parameter.delay.into(),
|
||||
ability_info.settings.parameter.duration.into(),
|
||||
)?
|
||||
.into_iter()
|
||||
.map(|time| (time, HashSet::new()))
|
||||
.collect();
|
||||
|
||||
let me = Self {
|
||||
direction,
|
||||
position: position,
|
||||
arc,
|
||||
radius,
|
||||
// height,
|
||||
ability_info: ability_info.clone(),
|
||||
|
||||
start: Duration::default(),
|
||||
base_damage,
|
||||
|
||||
owner,
|
||||
game,
|
||||
|
||||
light: None,
|
||||
|
||||
audio: None,
|
||||
|
||||
proc_slots,
|
||||
};
|
||||
|
||||
let mut entity = me.game.upgrade().engine().assets().empty_entity();
|
||||
|
||||
// don't create an extra entity for now if duration is zero or if the ability has no delay
|
||||
if me.ability_info.settings.parameter.duration != PersistentDuration::from_secs(0)
|
||||
|| me.ability_info.settings.parameter.delay != PersistentDuration::from_secs(0)
|
||||
{
|
||||
entity.insert_component(Draw::new(vec![]));
|
||||
|
||||
if !me.ability_info.settings.parameter.idle_sound.is_empty() {
|
||||
let mut audio = Audio::new(me.game.upgrade().engine().context(), None)?;
|
||||
|
||||
audio.add_custom_sound(
|
||||
me.game
|
||||
.upgrade()
|
||||
.build_data_path(&me.ability_info.settings.parameter.idle_sound),
|
||||
"idle_sound",
|
||||
)?;
|
||||
|
||||
entity.insert_component(audio);
|
||||
};
|
||||
|
||||
if !ability_info.settings.meta.particles.is_empty() {
|
||||
particle_spawn = Some(ParticleSpawn {
|
||||
entity: entity.as_entity(),
|
||||
info: ability_info,
|
||||
executor: ParticleSpawnExecutor::AoE,
|
||||
});
|
||||
}
|
||||
|
||||
Location::new_and_setup(&mut entity)?.set_position(position);
|
||||
|
||||
entity.insert_component(me.create_aoe_arc()?);
|
||||
entity.insert_component(me);
|
||||
entity.insert_component(BoundingBox {
|
||||
min: [-radius, -radius, 0.0],
|
||||
max: [radius, radius, height],
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
entity.debug_name = Some("AoE".to_string());
|
||||
}
|
||||
|
||||
Ok((entity, particle_spawn))
|
||||
}
|
||||
|
||||
pub fn calculate_proc_slots(
|
||||
count: u32,
|
||||
delay: Duration,
|
||||
duration: Duration,
|
||||
) -> Result<Vec<Duration>> {
|
||||
if count == 0 {
|
||||
return Err(anyhow::Error::msg("Proc Count 0 in AoE is useless"));
|
||||
}
|
||||
|
||||
// first proc is at `delay` time
|
||||
let mut procs = vec![delay];
|
||||
|
||||
if count == 1 {
|
||||
if duration > Duration::from_secs(0) {
|
||||
return Err(anyhow::Error::msg(
|
||||
"Undefined when proc should happen if there is only 1 but duration is there",
|
||||
));
|
||||
}
|
||||
|
||||
// nothing else needs to be done here
|
||||
} else {
|
||||
if duration == Duration::from_secs(0) {
|
||||
return Err(anyhow::Error::msg("Multiple procs happen at the same time"));
|
||||
}
|
||||
|
||||
let time_slice = duration / (count - 1);
|
||||
|
||||
for i in 1..count {
|
||||
procs.push(delay + time_slice * i);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(procs)
|
||||
}
|
||||
|
||||
pub fn check_life_time(&mut self, now: Duration) -> bool {
|
||||
let duration: Duration = self.ability_info.settings.parameter.duration.into();
|
||||
let delay = self.ability_info.settings.parameter.delay.into();
|
||||
|
||||
(self.start + delay + duration) >= now
|
||||
}
|
||||
|
||||
pub fn check_entity(&mut self, now: Duration, collider: Entity) -> bool {
|
||||
// find next smallest time
|
||||
match self
|
||||
.proc_slots
|
||||
.iter_mut()
|
||||
.rev()
|
||||
.find(|(time, _)| (self.start + *time) <= now)
|
||||
{
|
||||
Some((_, entities)) => entities.insert(collider),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_aoe_arc(&self) -> Result<AreaOfEffectArc> {
|
||||
Ok(AreaOfEffectArc::new(
|
||||
self.arc,
|
||||
self.radius,
|
||||
self.base_damage,
|
||||
self.ability_info.settings.parameter.damage_type,
|
||||
self.owner,
|
||||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn create_lighting(
|
||||
engine: &Engine,
|
||||
ability_info: &AbilityLoader,
|
||||
mut position: Vector3<f32>,
|
||||
info: &ParticleSystemInfo,
|
||||
) -> Result<Option<Light>> {
|
||||
let mut light = if ability_info.settings.parameter.light {
|
||||
let mut light = engine.new_point_light()?;
|
||||
|
||||
position.z += 0.5;
|
||||
light.set_position(position)?;
|
||||
light.set_power(200.0)?;
|
||||
|
||||
Some(light)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(light) = &mut light {
|
||||
let slice: [f32; 3] = info.particles.end_color.into();
|
||||
light.set_color(slice.into())?;
|
||||
}
|
||||
|
||||
Ok(light)
|
||||
}
|
||||
|
||||
pub fn create_particles(
|
||||
mut info: ParticleSystemInfo,
|
||||
draw: &mut Draw,
|
||||
particle_system_vulkan_objects: &ParticleSystemVulkanObjects,
|
||||
engine: &Engine,
|
||||
particle_info: ParticleInfo,
|
||||
) -> Result<ParticleSystem> {
|
||||
info.system.particle_count = (particle_info.radius * 250.0) as usize;
|
||||
info.system.random_start_factor = particle_info.radius;
|
||||
|
||||
ParticleSystem::new_with_vk_objects(info, engine, particle_system_vulkan_objects, draw)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_particle_effect(
|
||||
&mut self,
|
||||
game: &GameHandle,
|
||||
loader: &AbilityLoader,
|
||||
draw: &mut Draw,
|
||||
particle_system_vulkan_objects: &ParticleSystemVulkanObjects,
|
||||
) -> Result<ParticleSystem> {
|
||||
let info = ParticleSystemInfo::load(game.build_data_path(&loader.settings.meta.particles))?;
|
||||
|
||||
if loader.settings.parameter.procedure_count > 1 {
|
||||
self.light =
|
||||
Self::create_lighting(game.upgrade().engine(), &loader, self.position, &info)?
|
||||
}
|
||||
|
||||
Self::create_particles(
|
||||
info,
|
||||
draw,
|
||||
particle_system_vulkan_objects,
|
||||
game.upgrade().engine(),
|
||||
ParticleInfo {
|
||||
radius: self.radius,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn direction(&self) -> Vector2<f32> {
|
||||
self.direction
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for AreaOfEffect {
|
||||
fn enable(&mut self, scene: &mut Scene) -> Result<()> {
|
||||
if let Some(light) = &self.light {
|
||||
scene.add_light(light.clone())?;
|
||||
}
|
||||
|
||||
if let Some(audio) = &mut self.audio {
|
||||
unsafe { audio.as_mut() }.play("idle_sound", true)?;
|
||||
}
|
||||
|
||||
self.start = scene.now();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&mut self, scene: &mut Scene) -> Result<()> {
|
||||
if let Some(light) = &self.light {
|
||||
scene.remove_light(light.clone())?;
|
||||
}
|
||||
|
||||
if let Some(audio) = &mut self.audio {
|
||||
unsafe { audio.as_mut() }.stop()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for AreaOfEffect {
|
||||
fn debug_name() -> &'static str {
|
||||
"AreaOfEffect"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AreaOfEffectCreateInfo {
|
||||
direction: Vector2<f32>,
|
||||
position: Vector2<f32>,
|
||||
arc: f32,
|
||||
radius: f32,
|
||||
height: f32,
|
||||
ability_info: AbilityLoader,
|
||||
base_damage: u32,
|
||||
owner: Entity,
|
||||
}
|
||||
|
||||
impl<'a> ComponentCreateInfo<'a> for AreaOfEffectCreateInfo {}
|
||||
|
||||
#[test]
|
||||
fn verify_procs() {
|
||||
// ------ check if the procs are splitted correctly ------
|
||||
|
||||
let delay_only =
|
||||
AreaOfEffect::calculate_proc_slots(1, Duration::from_secs(1), Duration::from_secs(0))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(delay_only, vec![Duration::from_secs(1)]);
|
||||
|
||||
let split_5_without_delay =
|
||||
AreaOfEffect::calculate_proc_slots(5, Duration::from_secs(0), Duration::from_secs(1))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
split_5_without_delay,
|
||||
vec![
|
||||
Duration::from_secs(0),
|
||||
Duration::from_secs_f32(0.25),
|
||||
Duration::from_secs_f32(0.5),
|
||||
Duration::from_secs_f32(0.75),
|
||||
Duration::from_secs(1),
|
||||
]
|
||||
);
|
||||
|
||||
let split_5_with_delay =
|
||||
AreaOfEffect::calculate_proc_slots(5, Duration::from_secs(2), Duration::from_secs(1))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
split_5_with_delay,
|
||||
vec![
|
||||
Duration::from_secs(2),
|
||||
Duration::from_millis(2250),
|
||||
Duration::from_millis(2500),
|
||||
Duration::from_millis(2750),
|
||||
Duration::from_secs(3),
|
||||
]
|
||||
);
|
||||
|
||||
// ------ check error detection ------
|
||||
|
||||
assert!(
|
||||
AreaOfEffect::calculate_proc_slots(2, Duration::from_secs(0), Duration::from_secs(0))
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
AreaOfEffect::calculate_proc_slots(0, Duration::from_secs(0), Duration::from_secs(0))
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
AreaOfEffect::calculate_proc_slots(1, Duration::from_secs(0), Duration::from_secs(1))
|
||||
.is_err()
|
||||
);
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
use std::{collections::HashSet, time::Duration};
|
||||
|
||||
use cgmath::{vec2, Deg, InnerSpace, Matrix2, Vector2};
|
||||
use engine::prelude::*;
|
||||
use entity_manager::*;
|
||||
use rpg_components::{
|
||||
components::{abilityloader::AbilityLoader, statistics::Statistics},
|
||||
damage_type::DamageType,
|
||||
};
|
||||
|
||||
use crate::*;
|
||||
|
||||
use self::game::content::abilities::{damage_and_experience, handle_npc_death};
|
||||
|
||||
pub struct AreaOfEffectArc {
|
||||
arc: Deg<f32>,
|
||||
radius: f32,
|
||||
|
||||
base_damage: u32,
|
||||
damage_type: DamageType,
|
||||
owner: Entity,
|
||||
|
||||
spawn_time: Duration,
|
||||
collider: HashSet<Entity>,
|
||||
}
|
||||
|
||||
impl AreaOfEffectArc {
|
||||
pub fn new(
|
||||
arc: impl Into<Deg<f32>>,
|
||||
radius: f32,
|
||||
base_damage: u32,
|
||||
damage_type: DamageType,
|
||||
owner: Entity,
|
||||
) -> Self {
|
||||
let arc = arc.into();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let arc_deg: Deg<f32> = arc;
|
||||
|
||||
if arc_deg.0 < 0.0 {
|
||||
panic!("arc should not be below 0°");
|
||||
}
|
||||
|
||||
if arc_deg.0 > 180.0 {
|
||||
panic!("arc should not be above 180°");
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
arc,
|
||||
radius,
|
||||
|
||||
base_damage,
|
||||
damage_type,
|
||||
owner,
|
||||
|
||||
spawn_time: Duration::default(),
|
||||
collider: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arc(&self) -> Deg<f32> {
|
||||
self.arc
|
||||
}
|
||||
|
||||
pub fn radius(&self) -> f32 {
|
||||
self.radius
|
||||
}
|
||||
|
||||
pub fn check_life_time(&self, now: Duration) -> bool {
|
||||
now <= self.spawn_time + Duration::from_secs(1)
|
||||
}
|
||||
|
||||
/// returns true if the collider was not present before
|
||||
pub fn check_collider(&mut self, collider: Entity) -> bool {
|
||||
self.collider.insert(collider)
|
||||
}
|
||||
|
||||
pub fn check_collision(
|
||||
&self,
|
||||
my_location: &Location,
|
||||
my_direction: Vector2<f32>,
|
||||
collider_hit_box: &HitBox,
|
||||
collider_location: &Location,
|
||||
) -> bool {
|
||||
let collider_pos = collider_location.position().xy();
|
||||
let my_pos = my_location.position().xy();
|
||||
|
||||
if !collider_hit_box.check_collision(collider_pos, self.radius, my_pos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.arc.0 < 175.0 {
|
||||
let left_border = Matrix2::from_angle(self.arc) * my_direction;
|
||||
let right_border = Matrix2::from_angle(-self.arc) * my_direction;
|
||||
|
||||
let left_normal = vec2(-left_border.y, left_border.x);
|
||||
let right_normal = vec2(right_border.y, -right_border.x);
|
||||
|
||||
let diff = my_pos - collider_pos;
|
||||
let norm_diff = -diff.normalize();
|
||||
|
||||
let left_dot = left_normal.dot(norm_diff);
|
||||
let right_dot = right_normal.dot(norm_diff);
|
||||
|
||||
if left_dot > 0.0 && right_dot > 0.0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let left_distance = (diff - (diff.dot(left_border)) * left_border).magnitude();
|
||||
let right_distance = (diff - (diff.dot(right_border)) * right_border).magnitude();
|
||||
|
||||
if (left_distance > collider_hit_box.radius() && left_dot > 0.0)
|
||||
|| (right_distance > collider_hit_box.radius() && right_dot > 0.0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn hit_event(
|
||||
&mut self,
|
||||
game_handle: &GameHandle,
|
||||
scene: &mut SceneContents<'_>,
|
||||
collider: Entity,
|
||||
) -> Result<()> {
|
||||
let s = unsafe { remove_life_time_mut(scene) };
|
||||
|
||||
let mut entity_multi_mut = scene.entities_multi_mut();
|
||||
|
||||
// collider
|
||||
let Ok(collider_object) = entity_multi_mut.get(collider) else {
|
||||
return Ok(());
|
||||
};
|
||||
let mut collider_multi_mut = collider_object.multi_mut();
|
||||
|
||||
// owner
|
||||
let Ok(owner_object) = entity_multi_mut.get(self.owner) else {
|
||||
return Ok(());
|
||||
};
|
||||
let mut owner_multi_mut = owner_object.multi_mut();
|
||||
|
||||
// factions
|
||||
let collider_faction = Faction::get(&mut collider_multi_mut);
|
||||
let owner_faction = Faction::get(&mut owner_multi_mut).expect("owner is not in a faction!");
|
||||
|
||||
if !owner_faction.check_collision(collider_faction) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
owner_multi_mut.clear_all_usages();
|
||||
}
|
||||
|
||||
let owner_stats = owner_multi_mut.get::<Statistics>()?;
|
||||
let damage = AbilityLoader::check_for_crit(self.base_damage, owner_stats);
|
||||
|
||||
damage_and_experience(
|
||||
damage,
|
||||
self.damage_type,
|
||||
&mut collider_multi_mut,
|
||||
owner_multi_mut,
|
||||
s,
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
collider_multi_mut.clear_all_usages();
|
||||
}
|
||||
|
||||
handle_npc_death(&game_handle.upgrade(), s, collider, collider_multi_mut)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for AreaOfEffectArc {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
|
||||
fn enable(&mut self, scene: &mut Scene) -> Result<()> {
|
||||
self.spawn_time = scene.now();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for AreaOfEffectArc {
|
||||
fn debug_name() -> &'static str {
|
||||
"AreaOfEffectArc"
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use cgmath::{vec2, vec3, Deg};
|
||||
// use engine::prelude::*;
|
||||
|
||||
// use crate::{AreaOfEffectArc, HitBox, Location};
|
||||
|
||||
// #[test]
|
||||
// fn verify_aoe_hitbox_collision() {
|
||||
// let mut left_location = Location::new();
|
||||
// left_location.set_position(vec3(35.93969, 14.690732, 0.0));
|
||||
|
||||
// let aoe_arc = AreaOfEffectArc::new(Deg(15.0), 0.8, |_, _, _| Ok(()));
|
||||
|
||||
// let left_direction = vec2(0.9995991, 0.02831539);
|
||||
|
||||
// let mut right_position = Location::new();
|
||||
// right_position.set_position(vec3(36.608078, 14.694146, 0.0));
|
||||
|
||||
// let hitbox = HitBox::new(0.25689012, 1.0);
|
||||
|
||||
// assert!(aoe_arc.check_collision(&left_location, left_direction, &hitbox, &right_position));
|
||||
// }
|
||||
// }
|
|
@ -1,116 +0,0 @@
|
|||
use cgmath::Vector3;
|
||||
use rpg_components::damage_type::DamageType;
|
||||
|
||||
use crate::*;
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
pub struct DamageEvent {
|
||||
pub damage: u32,
|
||||
pub damage_type: DamageType,
|
||||
pub position: Vector3<f32>,
|
||||
}
|
||||
|
||||
pub struct DamageNumber {
|
||||
grid: Arc<Grid>,
|
||||
start: Duration,
|
||||
}
|
||||
|
||||
impl DamageNumber {
|
||||
pub const LIFE_TIME: Duration = Duration::from_secs(2);
|
||||
pub const SPEED: f32 = 1.5;
|
||||
|
||||
pub fn new(game_handle: &GameHandle, damage: u32, damage_type: DamageType) -> Result<Self> {
|
||||
Ok(Self {
|
||||
grid: Self::create_ui(game_handle, damage, damage_type)?,
|
||||
start: Duration::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn create_ui(
|
||||
game_handle: &GameHandle,
|
||||
damage: u32,
|
||||
damage_type: DamageType,
|
||||
) -> Result<Arc<Grid>> {
|
||||
let game = game_handle.upgrade();
|
||||
let gui_handler = game.engine().gui_handler();
|
||||
|
||||
let grid = Grid::new(gui_handler.clone(), 1, 1, false)?;
|
||||
grid.disallow_position_scale()?;
|
||||
|
||||
let label = Label::builder()
|
||||
.set_text(damage.to_string())
|
||||
.set_text_color(damage_type)
|
||||
.build(gui_handler.clone())?;
|
||||
|
||||
grid.attach(label, 0, 0, 1, 1)?;
|
||||
grid.set_frame(0, 0, 120, 40, VerticalAlign::Top, HorizontalAlign::Left)?;
|
||||
|
||||
Ok(grid)
|
||||
}
|
||||
|
||||
pub fn update(&self, scene: &SceneContents<'_>, location: &mut Location) -> Result<bool> {
|
||||
let diff = scene.now() - self.start;
|
||||
|
||||
if diff > Self::LIFE_TIME {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let ratio = diff.as_secs_f32() / Self::LIFE_TIME.as_secs_f32();
|
||||
let mut position = location.position();
|
||||
position.z += Self::SPEED * ratio;
|
||||
|
||||
let (x, y) = scene.world_to_screen_space(position)?;
|
||||
let (width, height) = self.grid.extents();
|
||||
self.grid
|
||||
.set_position(x - (width as i32 / 2), y - (height as i32 / 2))?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl DamageNumber {
|
||||
pub fn setup_event(game_handle: &GameHandle, scene: &mut Scene) {
|
||||
scene.register_event::<DamageEvent>();
|
||||
|
||||
scene.add_event_reader({
|
||||
let game_handle = game_handle.clone();
|
||||
|
||||
move |scene, damage_event: &DamageEvent| {
|
||||
let mut entity = game_handle.upgrade().engine().assets().empty_entity();
|
||||
|
||||
let location = Location::new_and_setup(&mut entity)?;
|
||||
location.set_position(damage_event.position);
|
||||
|
||||
let damage_number =
|
||||
DamageNumber::new(&game_handle, damage_event.damage, damage_event.damage_type)?;
|
||||
entity.insert_component(damage_number);
|
||||
|
||||
scene.add_entity(entity)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for DamageNumber {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
|
||||
fn enable(&mut self, scene: &mut Scene) -> Result<()> {
|
||||
self.start = scene.now();
|
||||
self.grid.set_visibility(true)
|
||||
}
|
||||
|
||||
fn disable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
self.grid.set_visibility(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for DamageNumber {
|
||||
fn debug_name() -> &'static str {
|
||||
"DamageNumber"
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Faction {
|
||||
Player,
|
||||
Friendly,
|
||||
NPC,
|
||||
}
|
||||
|
||||
impl Faction {
|
||||
pub fn copy(source: &mut MultiMut<'_>, destination: &mut EntityObject) {
|
||||
if source.get::<FactionPlayer>().is_ok() {
|
||||
destination.insert_component(FactionPlayer);
|
||||
} else if source.get::<FactionNPC>().is_ok() {
|
||||
destination.insert_component(FactionNPC);
|
||||
} else {
|
||||
unreachable!("missing component!");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(obj: &mut MultiMut<'_>) -> Option<Self> {
|
||||
if obj.get::<FactionPlayer>().is_ok() {
|
||||
Some(Self::Player)
|
||||
} else if obj.get::<FactionFriendly>().is_ok() {
|
||||
Some(Self::Friendly)
|
||||
} else if obj.get::<FactionNPC>().is_ok() {
|
||||
Some(Self::NPC)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_collision(self, other: Option<Self>) -> bool {
|
||||
match other {
|
||||
Some(collider_faction) => {
|
||||
// same faction or friendly NPCs are ignored
|
||||
if self == collider_faction || collider_faction == Self::Friendly {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct FactionPlayer;
|
||||
|
||||
impl EntityComponent for FactionPlayer {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for FactionPlayer {
|
||||
fn debug_name() -> &'static str {
|
||||
"FactionPlayer"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct FactionFriendly;
|
||||
|
||||
impl EntityComponent for FactionFriendly {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for FactionFriendly {
|
||||
fn debug_name() -> &'static str {
|
||||
"FactionFriendly"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub struct FactionNPC;
|
||||
|
||||
impl EntityComponent for FactionNPC {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for FactionNPC {
|
||||
fn debug_name() -> &'static str {
|
||||
"FactionNPC"
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::Game;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Ghost {
|
||||
start: Duration,
|
||||
|
||||
duration: Duration,
|
||||
start_height: f32,
|
||||
end_height: f32,
|
||||
}
|
||||
|
||||
impl Ghost {
|
||||
pub fn new(
|
||||
game: &Game,
|
||||
location: &Location,
|
||||
radius: f32,
|
||||
height: f32,
|
||||
start: Duration,
|
||||
) -> Result<EntityObject> {
|
||||
let mut ghost = game
|
||||
.entity_manager()
|
||||
.load_entity(game.engine().assets(), "Ghost")?;
|
||||
|
||||
let ghost_location = Location::new_and_setup(&mut ghost)?;
|
||||
ghost_location.update(location.position(), location.direction());
|
||||
ghost_location.set_scale_uniform(radius * 2.0);
|
||||
|
||||
let start_height = ghost_location.position().z + height;
|
||||
let end_height = start_height + 2.0 * radius;
|
||||
|
||||
let me = Ghost {
|
||||
start,
|
||||
|
||||
duration: Duration::from_secs(2),
|
||||
start_height,
|
||||
end_height,
|
||||
};
|
||||
|
||||
ghost.insert_component(me);
|
||||
|
||||
Ok(ghost)
|
||||
}
|
||||
|
||||
pub fn update(&self, now: Duration, location: &mut Location) -> Result<bool> {
|
||||
if self.start + self.duration < now {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let diff = now - self.start;
|
||||
let ratio = diff.as_secs_f32() / self.duration.as_secs_f32();
|
||||
|
||||
let final_height = self.start_height + self.end_height * ratio;
|
||||
|
||||
let mut position = location.position();
|
||||
position.z = final_height;
|
||||
|
||||
location.set_position(position);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for Ghost {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for Ghost {
|
||||
fn debug_name() -> &'static str {
|
||||
"Ghost"
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use cgmath::vec3;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::Game;
|
||||
|
||||
use super::health_bar_base::HealthBarBase;
|
||||
|
||||
pub struct BossHealthBar {
|
||||
gui: Arc<GuiBuilder>,
|
||||
hp_bar: Arc<ProgressBar>,
|
||||
|
||||
base: HealthBarBase,
|
||||
}
|
||||
|
||||
impl BossHealthBar {
|
||||
pub fn new(game: &Game, boss_name: impl ToString) -> Result<Self> {
|
||||
let gui = GuiBuilder::new(
|
||||
game.engine().gui_handler(),
|
||||
&game.build_data_path("gui/xml/ingame/boss_healthbar.xml"),
|
||||
)?;
|
||||
|
||||
let progress_bar: Arc<ProgressBar> = gui.element("health_bar")?;
|
||||
progress_bar.set_progress(1.0)?;
|
||||
|
||||
let name: Arc<Label> = gui.element("boss name")?;
|
||||
name.set_text(boss_name)?;
|
||||
|
||||
Ok(Self {
|
||||
gui,
|
||||
hp_bar: progress_bar,
|
||||
|
||||
base: HealthBarBase::new(vec3(0.0, 0.0, 0.0)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, health: u32, hp_progress: f32, should_render: bool) -> Result<()> {
|
||||
if self.base.should_enable && !self.base.is_enabled {
|
||||
self.base.is_enabled = true;
|
||||
self.gui.enable()?;
|
||||
}
|
||||
|
||||
match (should_render, self.base.is_enabled) {
|
||||
(true, false) | (true, true) => self.gui.enable()?,
|
||||
(false, true) => self.gui.disable()?,
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
println!(
|
||||
"should render: {}, enabled: {}",
|
||||
should_render, self.base.is_enabled
|
||||
);
|
||||
|
||||
self.hp_bar.set_progress(hp_progress)?;
|
||||
self.hp_bar.set_text(health)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for BossHealthBar {
|
||||
fn enable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
self.base.should_enable = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
self.base.should_enable = false;
|
||||
self.base.is_enabled = false;
|
||||
|
||||
self.gui.disable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for BossHealthBar {
|
||||
fn debug_name() -> &'static str {
|
||||
"BossHealthBar"
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
use cgmath::Vector3;
|
||||
use engine::prelude::*;
|
||||
|
||||
pub struct HealthBarBase {
|
||||
pub center: (i32, i32),
|
||||
pub should_enable: bool,
|
||||
pub is_enabled: bool,
|
||||
|
||||
pub world_offset: Vector3<f32>,
|
||||
}
|
||||
|
||||
impl HealthBarBase {
|
||||
pub fn new(offset: Vector3<f32>) -> Self {
|
||||
Self {
|
||||
center: (0, 0),
|
||||
|
||||
should_enable: false,
|
||||
is_enabled: false,
|
||||
|
||||
world_offset: offset,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
mod boss_health_bar;
|
||||
mod health_bar_base;
|
||||
mod npc_health_bar;
|
||||
mod user_health_bar;
|
||||
|
||||
pub use boss_health_bar::BossHealthBar;
|
||||
pub use npc_health_bar::NPCHealthBar;
|
||||
pub use user_health_bar::UserHealthBar;
|
|
@ -1,98 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use cgmath::Vector3;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::{convert::TryFrom, sync::Arc};
|
||||
|
||||
use crate::Game;
|
||||
|
||||
use super::health_bar_base::HealthBarBase;
|
||||
|
||||
pub struct NPCHealthBar {
|
||||
gui: Arc<GuiBuilder>,
|
||||
main_grid: Arc<Grid>,
|
||||
hp_bar: Arc<ProgressBar>,
|
||||
|
||||
base: HealthBarBase,
|
||||
}
|
||||
|
||||
impl NPCHealthBar {
|
||||
pub fn new(game: &Game, elite: bool, world_offset: Vector3<f32>) -> Result<Self> {
|
||||
let gui = GuiBuilder::new(
|
||||
game.engine().gui_handler(),
|
||||
&game.build_data_path("gui/xml/ingame/npc_healthbar.xml"),
|
||||
)?;
|
||||
|
||||
let progress_bar: Arc<ProgressBar> = gui.element("health_bar")?;
|
||||
progress_bar.set_progress(1.0)?;
|
||||
|
||||
let health_bar_grid: Arc<Grid> = gui.element("health_bar_grid")?;
|
||||
|
||||
// this is super important to prevent weird behavior
|
||||
health_bar_grid.disallow_position_scale()?;
|
||||
|
||||
if elite {
|
||||
health_bar_grid
|
||||
.set_background((Color::try_from("#FFD700")?, DisplayableFillType::Expand))?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
gui,
|
||||
main_grid: health_bar_grid,
|
||||
hp_bar: progress_bar,
|
||||
|
||||
base: HealthBarBase::new(world_offset),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, (health, hp_progress): (u32, f32), (x, y): (i32, i32)) -> Result<()> {
|
||||
if self.base.should_enable && !self.base.is_enabled {
|
||||
self.base.is_enabled = true;
|
||||
self.gui.enable()?;
|
||||
}
|
||||
|
||||
self.hp_bar.set_progress(hp_progress)?;
|
||||
self.hp_bar.set_text(health)?;
|
||||
|
||||
if self.base.center != (x, y) {
|
||||
self.base.center = (x, y);
|
||||
|
||||
let (width, height) = self.main_grid.extents();
|
||||
self.main_grid
|
||||
.set_position(x - (width as i32 / 2), y - (height as i32 / 2))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn world_offset(&self) -> Vector3<f32> {
|
||||
self.base.world_offset
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for NPCHealthBar {
|
||||
fn enable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
self.base.should_enable = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
self.base.should_enable = false;
|
||||
self.base.is_enabled = false;
|
||||
|
||||
self.gui.disable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for NPCHealthBar {
|
||||
fn debug_name() -> &'static str {
|
||||
"NPCHealthBar"
|
||||
}
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use cgmath::Vector3;
|
||||
use engine::prelude::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::Game;
|
||||
|
||||
use super::health_bar_base::HealthBarBase;
|
||||
|
||||
pub struct UserHealthBar {
|
||||
gui: Arc<GuiBuilder>,
|
||||
|
||||
name_grid: Arc<Grid>,
|
||||
bar_grid: Arc<Grid>,
|
||||
|
||||
hp_bar: Arc<ProgressBar>,
|
||||
mana_bar: Arc<ProgressBar>,
|
||||
|
||||
base: HealthBarBase,
|
||||
}
|
||||
|
||||
impl UserHealthBar {
|
||||
pub fn new(game: &Game, user_name: &str, world_offset: Vector3<f32>) -> Result<Self> {
|
||||
let gui = GuiBuilder::new(
|
||||
game.engine().gui_handler(),
|
||||
&game.build_data_path("gui/xml/ingame/user_healthbar.xml"),
|
||||
)?;
|
||||
|
||||
let hp_bar: Arc<ProgressBar> = gui.element("health bar")?;
|
||||
hp_bar.set_progress(1.0)?;
|
||||
|
||||
let mana_bar: Arc<ProgressBar> = gui.element("mana bar")?;
|
||||
mana_bar.set_progress(1.0)?;
|
||||
|
||||
let name_grid: Arc<Grid> = gui.element("name grid")?;
|
||||
|
||||
// this is super important to prevent weird behavior
|
||||
name_grid.disallow_position_scale()?;
|
||||
|
||||
let bar_grid: Arc<Grid> = gui.element("bar grid")?;
|
||||
|
||||
// this is super important to prevent weird behavior
|
||||
bar_grid.disallow_position_scale()?;
|
||||
|
||||
let user_name_label: Arc<Label> = gui.element("user name")?;
|
||||
user_name_label.set_text(&user_name)?;
|
||||
|
||||
Ok(UserHealthBar {
|
||||
gui,
|
||||
|
||||
name_grid,
|
||||
bar_grid,
|
||||
|
||||
hp_bar,
|
||||
mana_bar,
|
||||
|
||||
base: HealthBarBase {
|
||||
center: (0, 0),
|
||||
|
||||
should_enable: false,
|
||||
is_enabled: false,
|
||||
|
||||
world_offset,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(
|
||||
&mut self,
|
||||
hp_progress: f32,
|
||||
mana_progress: f32,
|
||||
(x, y): (i32, i32),
|
||||
) -> Result<()> {
|
||||
if self.base.should_enable && !self.base.is_enabled {
|
||||
self.base.is_enabled = true;
|
||||
self.gui.enable()?;
|
||||
}
|
||||
|
||||
self.hp_bar.set_progress(hp_progress)?;
|
||||
self.mana_bar.set_progress(mana_progress)?;
|
||||
|
||||
if self.base.center != (x, y) {
|
||||
self.base.center = (x, y);
|
||||
|
||||
let (name_width, name_height) = self.name_grid.extents();
|
||||
let (bar_width, _) = self.bar_grid.extents();
|
||||
|
||||
self.name_grid
|
||||
.set_position(x - (name_width as i32 / 2), y - (name_height as i32 / 2))?;
|
||||
|
||||
self.bar_grid
|
||||
.set_position(x - (bar_width as i32 / 2), y + (name_height as i32 / 2))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn world_offset(&self) -> Vector3<f32> {
|
||||
self.base.world_offset
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for UserHealthBar {
|
||||
fn enable(&mut self, __scene: &mut Scene) -> Result<()> {
|
||||
self.base.should_enable = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
self.base.should_enable = false;
|
||||
self.base.is_enabled = false;
|
||||
|
||||
self.gui.disable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for UserHealthBar {
|
||||
fn debug_name() -> &'static str {
|
||||
"UserHealthBar"
|
||||
}
|
||||
}
|
|
@ -1,265 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
use anyhow::Result;
|
||||
use cgmath::Vector3;
|
||||
use engine::prelude::*;
|
||||
use entity_manager::*;
|
||||
use rpg_components::{
|
||||
components::{level::Level, npc_type::NPCType},
|
||||
items::Loot,
|
||||
};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct LootStash {
|
||||
pub loot: Vec<Option<Loot>>,
|
||||
|
||||
action_radius: f32,
|
||||
open_chest_ui: Option<Arc<GuiBuilder>>,
|
||||
is_enabled: bool,
|
||||
}
|
||||
|
||||
impl LootStash {
|
||||
pub const WORLD_OFFSET: f32 = 1.0;
|
||||
pub const MAX_STASH_SIZE: usize = 49;
|
||||
}
|
||||
|
||||
impl LootStash {
|
||||
pub fn handle_npc_death(
|
||||
game: &Game,
|
||||
npc_level: &Level,
|
||||
npc_location: &Location,
|
||||
npc_type: NPCType,
|
||||
) -> Result<Option<EntityObject>> {
|
||||
let loot_multiplier = match npc_type {
|
||||
NPCType::Boss => Some(game.item_settings.general.drop_chance_boss_multiplier),
|
||||
NPCType::Elite => Some(game.item_settings.general.drop_chance_elite_multiplier),
|
||||
NPCType::Normal => None,
|
||||
};
|
||||
|
||||
let mut loot_list = vec![None; Self::MAX_STASH_SIZE];
|
||||
|
||||
for loot_opt in loot_list.iter_mut() {
|
||||
match game
|
||||
.item_system()
|
||||
.get_random_loot(npc_level.level(), loot_multiplier)
|
||||
{
|
||||
Some(loot) => *loot_opt = Some(loot),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
if loot_list.iter().any(|loot_opt| loot_opt.is_some()) {
|
||||
return Ok(Some(LootStash::create(
|
||||
game,
|
||||
loot_list,
|
||||
npc_location.position(),
|
||||
)?));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn create(
|
||||
game: &Game,
|
||||
loot: Vec<Option<Loot>>,
|
||||
parent_position: Vector3<f32>,
|
||||
) -> Result<EntityObject> {
|
||||
let mut loot_stash = game
|
||||
.entity_manager()
|
||||
.load_entity(game.engine().assets(), "lootchest")?;
|
||||
|
||||
{
|
||||
let location = Location::new_and_setup(&mut loot_stash)?;
|
||||
location.set_position(parent_position);
|
||||
location
|
||||
.set_scale_uniform((loot.len() as f32 / (Self::MAX_STASH_SIZE * 2) as f32) + 0.5);
|
||||
|
||||
loot_stash.insert_component(Self {
|
||||
loot,
|
||||
action_radius: game.game_settings().loot_action_radius,
|
||||
open_chest_ui: None,
|
||||
is_enabled: false,
|
||||
});
|
||||
|
||||
loot_stash.insert_component(FactionFriendly);
|
||||
|
||||
if let Ok(hitbox) = loot_stash.get_component_mut::<HitBox>() {
|
||||
hitbox.set_event(move |scene, _me, collider| -> Result<CollisionEventType> {
|
||||
let collider_entity_object = scene.entity(collider)?;
|
||||
|
||||
if collider_entity_object
|
||||
.get_component::<FactionPlayer>()
|
||||
.is_ok()
|
||||
{
|
||||
return Ok(CollisionEventType::Block);
|
||||
}
|
||||
|
||||
Ok(CollisionEventType::Ignore)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Ok(loot_stash)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
Self::is_list_empty(&self.loot)
|
||||
}
|
||||
|
||||
pub fn remove_loot(&mut self, index: usize) -> Loot {
|
||||
self.loot[index].take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl LootStash {
|
||||
pub fn update(&self, (x, y): (i32, i32)) -> Result<()> {
|
||||
if let Some(ui) = &self.open_chest_ui {
|
||||
if self.is_enabled && !ui.visible() {
|
||||
ui.enable()?;
|
||||
}
|
||||
|
||||
let grid: Arc<Grid> = ui.element("main grid")?;
|
||||
|
||||
let (width, height) = grid.extents();
|
||||
grid.set_position(x - (width as i32 / 2), y - (height as i32 / 2))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enable_ui(&mut self, game: &Game) -> Result<()> {
|
||||
let open_chest_ui = {
|
||||
let ui = GuiBuilder::new(
|
||||
game.engine().gui_handler(),
|
||||
&game.build_data_path("gui/xml/ingame/open_loot.xml"),
|
||||
)?;
|
||||
|
||||
let icon: Arc<Icon> = ui.element("open chest icon")?;
|
||||
|
||||
match game
|
||||
.engine()
|
||||
.settings()
|
||||
.controller_icon(game.engine(), ControllerButton::DPadUp)?
|
||||
{
|
||||
Some(pic) => {
|
||||
icon.set_icon(&pic)?;
|
||||
}
|
||||
None => {
|
||||
icon.set_icon(game.engine().settings().dark_key().as_ref().unwrap())?;
|
||||
icon.set_text("E")?;
|
||||
}
|
||||
};
|
||||
|
||||
let grid: Arc<Grid> = ui.element("main grid")?;
|
||||
|
||||
// this is super important to prevent weird behavior
|
||||
grid.disallow_position_scale()?;
|
||||
|
||||
ui
|
||||
};
|
||||
|
||||
debug_assert!(self.open_chest_ui.is_none());
|
||||
self.open_chest_ui = Some(open_chest_ui);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disable_ui(&mut self) -> Result<()> {
|
||||
debug_assert!(self.open_chest_ui.is_some());
|
||||
|
||||
if self.is_enabled {
|
||||
self.open_chest_ui.as_ref().unwrap().disable()?;
|
||||
}
|
||||
|
||||
self.open_chest_ui = None;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_debug(
|
||||
game_handle: &GameHandle,
|
||||
loot: Vec<Option<Loot>>,
|
||||
parent_position: Vector3<f32>,
|
||||
) -> Result<EntityObject> {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
let mut loot_stash = game
|
||||
.entity_manager()
|
||||
.load_entity(game.engine().assets(), "lootchest")?;
|
||||
|
||||
{
|
||||
let location = Location::new_and_setup(&mut loot_stash)?;
|
||||
location.set_position(parent_position);
|
||||
location
|
||||
.set_scale_uniform((loot.len() as f32 / (Self::MAX_STASH_SIZE * 2) as f32) + 0.5);
|
||||
|
||||
let me = Self {
|
||||
loot,
|
||||
action_radius: game.game_settings().loot_action_radius,
|
||||
open_chest_ui: None,
|
||||
is_enabled: false,
|
||||
};
|
||||
|
||||
loot_stash.insert_component(me);
|
||||
loot_stash.insert_component(FactionFriendly);
|
||||
}
|
||||
|
||||
Ok(loot_stash)
|
||||
}
|
||||
}
|
||||
|
||||
impl LootStash {
|
||||
pub fn is_list_empty(loot: &[Option<Loot>]) -> bool {
|
||||
let mut is_empty = true;
|
||||
|
||||
for loot_opt in loot.iter() {
|
||||
if loot_opt.is_some() {
|
||||
is_empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
is_empty
|
||||
}
|
||||
|
||||
pub fn radius(&self) -> f32 {
|
||||
self.action_radius
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for LootStash {
|
||||
fn enable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
self.is_enabled = true;
|
||||
|
||||
if let Some(ui) = &self.open_chest_ui {
|
||||
debug_assert!(!ui.visible());
|
||||
|
||||
ui.enable()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
self.is_enabled = false;
|
||||
|
||||
if let Some(ui) = &self.open_chest_ui {
|
||||
debug_assert!(ui.visible());
|
||||
|
||||
ui.disable()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for LootStash {
|
||||
fn debug_name() -> &'static str {
|
||||
"LootStash"
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
mod ability_location_info;
|
||||
pub mod abilityloader;
|
||||
pub mod ai;
|
||||
mod aoe;
|
||||
pub mod aoe_arc;
|
||||
pub mod damage_number;
|
||||
mod entity_faction;
|
||||
mod ghost;
|
||||
pub mod health_bar;
|
||||
mod loot_stash;
|
||||
pub mod movement;
|
||||
mod npc_name;
|
||||
|
||||
pub use self::{ai::*, aoe_arc::AreaOfEffectArc};
|
||||
|
||||
pub use self::{
|
||||
ability_location_info::*, aoe::*, damage_number::*, entity_faction::*, ghost::*, health_bar::*,
|
||||
loot_stash::*, movement::Movement, npc_name::NPCName,
|
||||
};
|
||||
|
||||
pub const MOVEMENT_MAX_THRESHOLD: f32 = 0.8;
|
||||
pub const MOVEMENT_MIN_THRESHOLD: f32 = 0.4;
|
|
@ -1,133 +0,0 @@
|
|||
use cgmath;
|
||||
use cgmath::Vector2;
|
||||
use engine::prelude::*;
|
||||
use entity_manager::*;
|
||||
|
||||
use super::{MOVEMENT_MAX_THRESHOLD, MOVEMENT_MIN_THRESHOLD};
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct Movement {
|
||||
direction: Vector2<f32>,
|
||||
pub movement_speed: f32,
|
||||
|
||||
lock: bool,
|
||||
|
||||
pub(crate) base_movement_speed: f32,
|
||||
|
||||
pub current_distance: Option<f32>,
|
||||
|
||||
audio: Option<UnsafeComponentStore<Audio>>,
|
||||
}
|
||||
|
||||
impl Clone for Movement {
|
||||
fn clone(&self) -> Self {
|
||||
Movement {
|
||||
direction: self.direction,
|
||||
movement_speed: self.base_movement_speed,
|
||||
base_movement_speed: self.base_movement_speed,
|
||||
|
||||
lock: false,
|
||||
|
||||
current_distance: None,
|
||||
|
||||
audio: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Movement {
|
||||
pub(crate) fn new(speed: f32, audio: Option<&mut Audio>) -> Self {
|
||||
Self {
|
||||
direction: Vector2::zero(),
|
||||
movement_speed: speed,
|
||||
base_movement_speed: speed,
|
||||
|
||||
lock: false,
|
||||
|
||||
current_distance: None,
|
||||
|
||||
audio: audio.map(|audio| audio.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn post_setup(&mut self, audio: Option<&mut Audio>) {
|
||||
self.audio = audio.map(|audio| audio.into());
|
||||
}
|
||||
|
||||
pub fn set_direction(&mut self, direction: Vector2<f32>) {
|
||||
self.direction = self.check_direction(direction);
|
||||
}
|
||||
|
||||
pub fn direction(&self) -> Vector2<f32> {
|
||||
self.direction
|
||||
}
|
||||
|
||||
pub fn movement_speed(&self) -> f32 {
|
||||
self.movement_speed
|
||||
}
|
||||
|
||||
pub fn base_movement_speed(&self) -> f32 {
|
||||
self.base_movement_speed
|
||||
}
|
||||
|
||||
pub fn is_locked(&self) -> bool {
|
||||
self.lock
|
||||
}
|
||||
|
||||
fn check_direction(&mut self, mut direction: Vector2<f32>) -> Vector2<f32> {
|
||||
let length = direction.magnitude();
|
||||
|
||||
if length < MOVEMENT_MIN_THRESHOLD {
|
||||
if let Some(ptr) = &mut self.audio {
|
||||
let audio = unsafe { ptr.as_mut() };
|
||||
|
||||
audio.stop_looping().unwrap();
|
||||
}
|
||||
|
||||
direction = Vector2::zero();
|
||||
} else {
|
||||
if let Some(ptr) = &mut self.audio {
|
||||
let audio = unsafe { ptr.as_mut() };
|
||||
|
||||
audio.play(SOUND_MOVE_KEY, true).unwrap();
|
||||
}
|
||||
|
||||
if length > MOVEMENT_MAX_THRESHOLD {
|
||||
direction /= length;
|
||||
}
|
||||
}
|
||||
|
||||
direction
|
||||
}
|
||||
|
||||
pub fn has_direction(&self) -> bool {
|
||||
self.direction != Vector2::zero()
|
||||
}
|
||||
|
||||
pub fn process_move(&mut self, time_delta: Duration) -> Vector2<f32> {
|
||||
self.distance(time_delta) * self.direction
|
||||
}
|
||||
|
||||
pub fn distance(&mut self, time_delta: Duration) -> f32 {
|
||||
self.movement_speed * time_delta.as_secs_f32()
|
||||
}
|
||||
|
||||
pub fn setup_after_clone(&mut self, multi_mut: &mut MultiMut<'_>) {
|
||||
let audio = multi_mut.get::<Audio>();
|
||||
|
||||
self.audio = audio.map(UnsafeComponentStore::from).ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for Movement {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for Movement {
|
||||
fn debug_name() -> &'static str {
|
||||
"Movement"
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
use engine::prelude::*;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct NPCName {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl NPCName {
|
||||
pub fn new(name: impl ToString) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityComponent for NPCName {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for NPCName {
|
||||
fn debug_name() -> &'static str {
|
||||
"NPCName"
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use cgmath::{vec2, Deg, Rad, Vector2, Vector3};
|
||||
use entity_manager::*;
|
||||
|
||||
use crate::*;
|
||||
|
||||
pub struct LightningMarker;
|
||||
|
||||
impl EntityComponent for LightningMarker {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for LightningMarker {
|
||||
fn debug_name() -> &'static str {
|
||||
"LightningMarker"
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lightning {
|
||||
pub thickness: f32,
|
||||
pub lines: Vec<Vector2<f32>>,
|
||||
}
|
||||
|
||||
impl Lightning {
|
||||
const WIDTH: f32 = 0.25;
|
||||
const LENGTH: f32 = 1.0;
|
||||
const DIRECTION: Vector2<f32> = Vector2::new(1.0, 0.0);
|
||||
|
||||
pub fn new(thickness: f32) -> Self {
|
||||
Self {
|
||||
thickness,
|
||||
lines: Self::create_lines(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_vertices(&self) -> Vec<Vector2<f32>> {
|
||||
self.lines
|
||||
.iter()
|
||||
.skip(1)
|
||||
.enumerate()
|
||||
.map(|(index, p)| {
|
||||
let prev_p = self.lines[index];
|
||||
|
||||
let tangent = prev_p - p;
|
||||
let normal = vec2(-tangent.y, tangent.x).normalize();
|
||||
|
||||
let p1 = prev_p - normal * 0.5 * self.thickness;
|
||||
let p2 = p - normal * 0.5 * self.thickness;
|
||||
let p3 = p + normal * 0.5 * self.thickness;
|
||||
let p4 = prev_p + normal * 0.5 * self.thickness;
|
||||
|
||||
vec![p1, p2, p3, p3, p4, p1]
|
||||
})
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn create_entity(
|
||||
&self,
|
||||
game_handle: &GameHandle,
|
||||
scene: &Scene,
|
||||
position: Vector3<f32>,
|
||||
target: Vector3<f32>,
|
||||
) -> Result<EntityObject> {
|
||||
let game = game_handle.upgrade();
|
||||
let mut entity = game.engine().assets().empty_entity();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
entity.debug_name = Some("Lightning".to_string());
|
||||
}
|
||||
|
||||
let tangent = (target - position).truncate();
|
||||
let scale = tangent.magnitude();
|
||||
let angle = tangent.angle(Self::DIRECTION);
|
||||
let angle_offset: Rad<f32> = Deg(90.0).into();
|
||||
let height = position.z;
|
||||
|
||||
let mut mesh = AssetMesh::new(&scene.device(), scene.render_type())?;
|
||||
mesh.set_name("Lightning");
|
||||
|
||||
let data: Vec<PositionOnly> = self
|
||||
.create_vertices()
|
||||
.into_iter()
|
||||
.rev()
|
||||
.map(|p| PositionOnly::new(p.extend(0.0)))
|
||||
.collect();
|
||||
|
||||
let vertex_buffer = Buffer::builder()
|
||||
.set_memory_usage(MemoryUsage::CpuOnly)
|
||||
.set_usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
|
||||
.set_data(&data)
|
||||
.build(scene.device().clone())?;
|
||||
|
||||
mesh.add_primitive(
|
||||
vertex_buffer,
|
||||
None,
|
||||
None,
|
||||
PrimitiveMaterial {
|
||||
color: Color::from_str("#2660ff")?.into(),
|
||||
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
|
||||
entity.insert_component(Draw::new(vec![mesh]));
|
||||
|
||||
let audio = Audio::new(
|
||||
&game.engine().context(),
|
||||
[(
|
||||
SOUND_CREATE_KEY.to_string(),
|
||||
game_handle.build_data_path("sounds/shot2.ogg"),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect::<HashMap<_, _>>(),
|
||||
)?;
|
||||
|
||||
entity.insert_component(audio);
|
||||
|
||||
let location = Location::new_and_setup(&mut entity)?;
|
||||
location.set_position(position);
|
||||
location.set_scale_uniform(scale);
|
||||
location.set_rotation(angle - angle_offset);
|
||||
|
||||
entity.insert_component(LightningMarker);
|
||||
entity.insert_component(BoundingBox {
|
||||
min: [0.0, -0.25, 0.0],
|
||||
max: [1.0, 0.25, 1.0],
|
||||
});
|
||||
|
||||
println!(
|
||||
"Lightning created with components: {:#?}",
|
||||
entity.component_names()
|
||||
);
|
||||
|
||||
Ok(entity)
|
||||
}
|
||||
|
||||
fn create_lines() -> Vec<Vector2<f32>> {
|
||||
let mut lines = Vec::new();
|
||||
|
||||
for _ in 0..50 {
|
||||
lines.push(vec2(Random::range_f32(0.0, Self::LENGTH), 0.0));
|
||||
}
|
||||
|
||||
lines.sort_by(|lhs, rhs| lhs.x.partial_cmp(&rhs.x).unwrap());
|
||||
|
||||
[vec2(0.0, 0.0)]
|
||||
.into_iter()
|
||||
.chain(
|
||||
lines
|
||||
.into_iter()
|
||||
.map(|p| vec2(p.x, Random::range_f32(-Self::WIDTH, Self::WIDTH))),
|
||||
)
|
||||
.chain([vec2(1.0, 0.0)].into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
pub mod abilities;
|
||||
pub mod components;
|
||||
pub mod objects;
|
||||
|
||||
pub mod ability_type;
|
||||
pub mod lightning;
|
||||
pub mod prelude;
|
|
@ -1,498 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use assetpath::AssetPath;
|
||||
use cgmath::{vec2, Vector2};
|
||||
use engine::prelude::*;
|
||||
use entity_manager::*;
|
||||
use rpg_components::{
|
||||
ability_type::AbilityType,
|
||||
components::{
|
||||
ability_slots::AbilitySlots,
|
||||
attributes::Attributes,
|
||||
character_status::CharacterStatus,
|
||||
crafting_materials::CraftingMaterials,
|
||||
inventory::Inventory,
|
||||
item_slots::ItemSlotContainer,
|
||||
level::{Level, LevelUpEvent},
|
||||
statistics::Statistics,
|
||||
},
|
||||
config::{
|
||||
attributes::AttributeSettings,
|
||||
experience::ExperienceSettings,
|
||||
save_game::{save_game_dir, SaveGame},
|
||||
},
|
||||
damage_type::DamageType,
|
||||
items::{ItemSystem, Rarities},
|
||||
};
|
||||
|
||||
use super::super::prelude::*;
|
||||
|
||||
use crate::GameHandle;
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum HeroCreateType {
|
||||
SaveGame(SaveGame),
|
||||
New(String),
|
||||
}
|
||||
|
||||
impl From<SaveGame> for HeroCreateType {
|
||||
fn from(save_game: SaveGame) -> Self {
|
||||
Self::SaveGame(save_game)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for HeroCreateType {
|
||||
fn from(name: String) -> Self {
|
||||
Self::New(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for HeroCreateType {
|
||||
fn from(name: &str) -> Self {
|
||||
Self::New(name.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for HeroCreateType {
|
||||
fn from(name: &String) -> Self {
|
||||
Self::New(name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MainUser;
|
||||
|
||||
impl EntityComponent for MainUser {
|
||||
fn name(&self) -> &str {
|
||||
Self::debug_name()
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentDebug for MainUser {
|
||||
fn debug_name() -> &'static str {
|
||||
"MainUser"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Hero {
|
||||
name: String,
|
||||
entity: Entity,
|
||||
}
|
||||
|
||||
impl Hero {
|
||||
pub fn reuse(
|
||||
&self,
|
||||
game_handle: &GameHandle,
|
||||
hero_create_type: impl Into<HeroCreateType>,
|
||||
scene: &mut Scene,
|
||||
) -> Result<()> {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
let hero_create_type = hero_create_type.into();
|
||||
|
||||
let mut entity_object = scene.entity_mut(self.entity)?;
|
||||
Location::setup(entity_object.multi_mut())?;
|
||||
|
||||
let exp_settings = &game.experience_settings;
|
||||
let attribute_settings = &game.attribute_settings;
|
||||
let item_settings = &game.item_settings;
|
||||
let item_system = &game.item_system();
|
||||
|
||||
let level = Self::level(&hero_create_type, exp_settings);
|
||||
let mut attributes = Self::attributes(&hero_create_type, attribute_settings);
|
||||
let items = Self::items(&hero_create_type, game_handle.clone(), &mut entity_object)?;
|
||||
let inventory = Self::inventory(&hero_create_type, item_system)?;
|
||||
let abilities = Self::abilities(&hero_create_type, game_handle.clone())?;
|
||||
let crafting_materials: CraftingMaterials = Self::crafting_materials(&hero_create_type);
|
||||
let movement = Self::movement(&mut entity_object);
|
||||
|
||||
let mut stats = Statistics::default();
|
||||
stats.update(&mut attributes, attribute_settings, (&items, item_settings));
|
||||
|
||||
let current_status = CharacterStatus::new_full(&stats);
|
||||
|
||||
*entity_object.get_component_mut()? = level;
|
||||
*entity_object.get_component_mut()? = attributes;
|
||||
*entity_object.get_component_mut()? = items;
|
||||
*entity_object.get_component_mut()? = inventory;
|
||||
*entity_object.get_component_mut()? = abilities;
|
||||
*entity_object.get_component_mut()? = crafting_materials;
|
||||
*entity_object.get_component_mut()? = movement;
|
||||
*entity_object.get_component_mut()? = stats;
|
||||
*entity_object.get_component_mut()? = current_status;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
game_handle: &GameHandle,
|
||||
hero_create_type: impl Into<HeroCreateType>,
|
||||
) -> Result<(EntityObject, Hero)> {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
let mut entity_object = game
|
||||
.entity_manager()
|
||||
.load_entity(game.engine().assets(), "character")?;
|
||||
|
||||
let entity = entity_object.as_entity();
|
||||
|
||||
let hero_create_type = hero_create_type.into();
|
||||
|
||||
let exp_settings = &game.experience_settings;
|
||||
let attribute_settings = &game.attribute_settings;
|
||||
let item_settings = &game.item_settings;
|
||||
let item_system = &game.item_system();
|
||||
|
||||
{
|
||||
Location::new_and_setup(&mut entity_object)?;
|
||||
|
||||
let level = Self::level(&hero_create_type, exp_settings);
|
||||
let mut attributes = Self::attributes(&hero_create_type, attribute_settings);
|
||||
let items = Self::items(&hero_create_type, game_handle.clone(), &mut entity_object)?;
|
||||
let inventory = Self::inventory(&hero_create_type, item_system)?;
|
||||
let abilities = Self::abilities(&hero_create_type, game_handle.clone())?;
|
||||
let crafting_materials = Self::crafting_materials(&hero_create_type);
|
||||
let movement = Self::movement(&mut entity_object);
|
||||
|
||||
let mut stats = Statistics::default();
|
||||
stats.update(&mut attributes, attribute_settings, (&items, item_settings));
|
||||
|
||||
let current_status = CharacterStatus::new_full(&stats);
|
||||
|
||||
entity_object.insert_component(level);
|
||||
entity_object.insert_component(attributes);
|
||||
entity_object.insert_component(items);
|
||||
entity_object.insert_component(inventory);
|
||||
entity_object.insert_component(abilities);
|
||||
entity_object.insert_component(crafting_materials);
|
||||
entity_object.insert_component(movement);
|
||||
entity_object.insert_component(stats);
|
||||
entity_object.insert_component(current_status);
|
||||
|
||||
entity_object.insert_component(FactionPlayer);
|
||||
entity_object.insert_component(MainUser);
|
||||
}
|
||||
|
||||
let name = match hero_create_type {
|
||||
HeroCreateType::SaveGame(save_game) => save_game.general.name,
|
||||
HeroCreateType::New(name) => name,
|
||||
};
|
||||
|
||||
Ok((entity_object, Hero { name, entity }))
|
||||
}
|
||||
|
||||
pub fn set_level_up_effect(game: &GameHandle, entity: Entity) -> Result<()> {
|
||||
let strong_game = game.upgrade();
|
||||
let engine = strong_game.engine();
|
||||
|
||||
let sound = engine.context().sound().load_sound(
|
||||
game.build_data_path("sounds/blessing2.ogg"),
|
||||
"sfx",
|
||||
SoundInterpretation::Spatial,
|
||||
)?;
|
||||
let particle_system_info =
|
||||
ParticleSystemInfo::load(game.build_data_path("particles/level_up.particle"))?;
|
||||
|
||||
let game_handle = game.clone();
|
||||
|
||||
engine.on_scene_mut(move |scene| {
|
||||
scene.add_event_reader(move |scene, _level_up: &LevelUpEvent| {
|
||||
let position = scene
|
||||
.entity(entity)?
|
||||
.get_component::<Location>()?
|
||||
.position();
|
||||
|
||||
let game = game_handle.upgrade();
|
||||
let mut particle_entity = game.engine().assets().empty_entity();
|
||||
|
||||
let mut draw = Draw::new(Vec::new());
|
||||
let mut audio = Audio::new(&game.engine().context(), None)?;
|
||||
|
||||
audio.add_sound(SOUND_CREATE_KEY, sound.clone());
|
||||
|
||||
let particle_system = ParticleSystem::new_with_vk_objects(
|
||||
particle_system_info.clone(),
|
||||
game_handle.upgrade().engine(),
|
||||
scene.particle_system_vulkan_objects(),
|
||||
&mut draw,
|
||||
)?;
|
||||
|
||||
particle_entity.insert_component(audio);
|
||||
particle_entity.insert_component(draw);
|
||||
particle_entity.insert_component(particle_system);
|
||||
particle_entity.insert_component(BoundingBox {
|
||||
min: [-1.0, -1.0, 0.0],
|
||||
max: [1.0, 1.0, 2.0],
|
||||
});
|
||||
|
||||
Location::new_and_setup(&mut particle_entity)?.set_position(position);
|
||||
|
||||
scene.add_entity(particle_entity)?;
|
||||
|
||||
// ingame
|
||||
// .level_up_ui
|
||||
// .show(ingame.game().engine().time(), level)?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn move_in_direction(&self, movement: &mut Movement, left_stick: Vector2<f32>) {
|
||||
movement.set_direction(left_stick);
|
||||
}
|
||||
|
||||
pub fn execute_ability(
|
||||
hero: Entity,
|
||||
game_handle: &GameHandle,
|
||||
time: Duration,
|
||||
index: usize,
|
||||
multi_mut: &mut MultiMut<'_>,
|
||||
entities: &mut Entities<'_>,
|
||||
events: &mut ContentEvents<'_>,
|
||||
) -> Result<()> {
|
||||
let animation = multi_mut.get::<Animation>()?;
|
||||
|
||||
let draw = multi_mut.get::<Draw>()?;
|
||||
|
||||
let animation_info = multi_mut.get::<AnimationInfo>()?;
|
||||
|
||||
let abilities = multi_mut.get::<AbilitySlots>()?;
|
||||
let character_status = multi_mut.get::<CharacterStatus>()?;
|
||||
let location = multi_mut.get::<Location>()?;
|
||||
|
||||
let direction = location.direction();
|
||||
|
||||
if let Some(ability_book) = abilities.book_mut(index) {
|
||||
if !animation_info.is_locked()
|
||||
&& ability_book.validate_use(time, character_status, location)?
|
||||
{
|
||||
unsafe {
|
||||
multi_mut.clear_all_usages();
|
||||
}
|
||||
|
||||
{
|
||||
// TODO: further separation of animation types (bows, ...)
|
||||
let animation_type =
|
||||
match ability_book.ability().data().settings.parameter.damage_type {
|
||||
DamageType::Physical => AnimationType::Attack,
|
||||
_ => AnimationType::Cast,
|
||||
};
|
||||
|
||||
animation_info.set_animation(
|
||||
animation,
|
||||
draw,
|
||||
Some(animation_type),
|
||||
time,
|
||||
true,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
|
||||
match ability_book
|
||||
.ability()
|
||||
.data()
|
||||
.settings
|
||||
.parameter
|
||||
.ability_type
|
||||
{
|
||||
AbilityType::Projectile => Projectile::execute(
|
||||
ability_book.ability().data(),
|
||||
hero,
|
||||
multi_mut,
|
||||
direction,
|
||||
&ability_book,
|
||||
game_handle,
|
||||
entities,
|
||||
events,
|
||||
)?,
|
||||
AbilityType::SelfCast => SelfCast::execute(
|
||||
ability_book.ability().data(),
|
||||
hero,
|
||||
multi_mut,
|
||||
direction,
|
||||
&ability_book,
|
||||
game_handle,
|
||||
entities,
|
||||
events,
|
||||
)?,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn change_name(&mut self, name: &str) {
|
||||
self.name = name.to_string();
|
||||
}
|
||||
|
||||
pub fn save(&self, scene: &Scene) -> Result<()> {
|
||||
let mut save_game = SaveGame::default();
|
||||
|
||||
save_game.file_name =
|
||||
AssetPath::from((save_game_dir("gavania"), format!("{}.savegame", self.name)));
|
||||
|
||||
let entity = scene.entity(self.entity)?;
|
||||
|
||||
let level = entity.get_component::<Level>()?;
|
||||
let attributes = entity.get_component::<Attributes>()?;
|
||||
let items = entity.get_component::<ItemSlotContainer>()?;
|
||||
let inventory = entity.get_component::<Inventory>()?;
|
||||
let abilities = entity.get_component::<AbilitySlots>()?;
|
||||
let crafting_materials = entity.get_component::<CraftingMaterials>()?;
|
||||
|
||||
save_game.general.name = self.name.clone();
|
||||
save_game.general.level = level.level();
|
||||
save_game.general.exp = level.current_experience;
|
||||
save_game.general.strength = attributes.base_strength().raw();
|
||||
save_game.general.agility = attributes.base_agility().raw();
|
||||
save_game.general.intelligence = attributes.base_intelligence().raw();
|
||||
|
||||
items.store(&mut save_game);
|
||||
inventory.store(&mut save_game);
|
||||
abilities.store(&mut save_game);
|
||||
crafting_materials.store(&mut save_game);
|
||||
|
||||
save_game.store()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hero {
|
||||
fn level(hero_create_type: &HeroCreateType, exp_settings: &ExperienceSettings) -> Level {
|
||||
match hero_create_type {
|
||||
HeroCreateType::SaveGame(save_game) => {
|
||||
Level::load(save_game.general.level, save_game.general.exp, exp_settings)
|
||||
}
|
||||
HeroCreateType::New(_) => Level::new(exp_settings),
|
||||
}
|
||||
}
|
||||
|
||||
fn attributes(
|
||||
hero_create_type: &HeroCreateType,
|
||||
attribute_settings: &AttributeSettings,
|
||||
) -> Attributes {
|
||||
match hero_create_type {
|
||||
HeroCreateType::SaveGame(save_game) => Attributes::load(
|
||||
save_game.general.strength,
|
||||
save_game.general.agility,
|
||||
save_game.general.intelligence,
|
||||
),
|
||||
HeroCreateType::New(_) => Attributes::new(&attribute_settings.starting_attributes),
|
||||
}
|
||||
}
|
||||
|
||||
fn items(
|
||||
hero_create_type: &HeroCreateType,
|
||||
game_handle: GameHandle,
|
||||
entity_object: &mut EntityObject,
|
||||
) -> Result<ItemSlotContainer> {
|
||||
let mut multi_mut = entity_object.multi_mut();
|
||||
|
||||
let draw = multi_mut.get::<Draw>().unwrap();
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
let mut items = match hero_create_type {
|
||||
HeroCreateType::SaveGame(save_game) => {
|
||||
ItemSlotContainer::load(save_game, &game.item_system())?
|
||||
}
|
||||
HeroCreateType::New(_) => ItemSlotContainer::new(),
|
||||
};
|
||||
|
||||
items.set_item_change_callback(draw, multi_mut.get::<Location>()?)?;
|
||||
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
fn inventory(
|
||||
hero_create_type: &HeroCreateType,
|
||||
item_system: &Arc<ItemSystem>,
|
||||
) -> Result<Inventory> {
|
||||
match hero_create_type {
|
||||
HeroCreateType::SaveGame(save_game) => Inventory::load(save_game, item_system),
|
||||
HeroCreateType::New(_) => Ok(Inventory::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn abilities(
|
||||
hero_create_type: &HeroCreateType,
|
||||
game_handle: GameHandle,
|
||||
) -> Result<AbilitySlots> {
|
||||
let game = game_handle.upgrade();
|
||||
|
||||
match hero_create_type {
|
||||
HeroCreateType::SaveGame(save_game) => {
|
||||
AbilitySlots::load(&game.item_system(), save_game)
|
||||
}
|
||||
HeroCreateType::New(_) => {
|
||||
let mut abilities = AbilitySlots::empty();
|
||||
|
||||
let book = game_handle.upgrade().item_system().ability_book(
|
||||
"Basic Attack",
|
||||
Rarities::Common,
|
||||
Vec::new(),
|
||||
1,
|
||||
);
|
||||
|
||||
abilities.insert_book(book, 0);
|
||||
|
||||
Ok(abilities)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn crafting_materials(hero_create_type: &HeroCreateType) -> CraftingMaterials {
|
||||
match hero_create_type {
|
||||
HeroCreateType::SaveGame(save_game) => CraftingMaterials::load(save_game),
|
||||
HeroCreateType::New(_) => CraftingMaterials::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn movement(entity_object: &mut EntityObject) -> Movement {
|
||||
entity_object
|
||||
.get_component_mut::<HitBox>()
|
||||
.unwrap()
|
||||
.set_event(move |scene, me, collider| {
|
||||
let mut entities_multi_mut = scene.entities_multi_mut();
|
||||
|
||||
if let Ok(my_object) = entities_multi_mut.get(me) {
|
||||
if let Ok(collider_object) = entities_multi_mut.get(collider) {
|
||||
if collider_object.get_component::<LootStash>().is_ok() {
|
||||
my_object
|
||||
.get_component_mut::<Movement>()?
|
||||
.set_direction(vec2(0.0, 0.0));
|
||||
}
|
||||
|
||||
if collider_object.get_component::<FactionFriendly>().is_ok()
|
||||
|| collider_object.get_component::<FactionPlayer>().is_ok()
|
||||
{
|
||||
return Ok(CollisionEventType::Ignore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CollisionEventType::Block)
|
||||
});
|
||||
|
||||
let movement = {
|
||||
let audio = entity_object.get_component_mut::<Audio>().ok();
|
||||
|
||||
Movement::new(1.0, audio)
|
||||
};
|
||||
|
||||
movement
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
pub mod hero;
|
||||
pub mod npc;
|
|
@ -1,408 +0,0 @@
|
|||
use std::{
|
||||
collections::{hash_map::Iter, HashMap},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::game::content::prelude::*;
|
||||
use crate::game::game::GameHandle;
|
||||
|
||||
use anyhow::Result;
|
||||
use assetpath::AssetPath;
|
||||
use cgmath::{vec3, Deg, InnerSpace, Vector3, Zero};
|
||||
use engine::prelude::*;
|
||||
use entity_manager::*;
|
||||
use rpg_components::{
|
||||
components::{
|
||||
attributes::Attributes,
|
||||
character_status::CharacterStatus,
|
||||
level::Level,
|
||||
npc_type::{NPCBoss, NPCElite, NPCNormal, NPCType},
|
||||
statistics::Statistics,
|
||||
},
|
||||
items::Rarities,
|
||||
};
|
||||
|
||||
const NPC_BASE_STATS: u32 = 2;
|
||||
|
||||
create_settings_section!(
|
||||
NPCMetaInfo,
|
||||
"Meta",
|
||||
{
|
||||
entity_file: String,
|
||||
name: String,
|
||||
movement_speed: f32,
|
||||
aggro_radius: f32,
|
||||
deaggro_radius: f32,
|
||||
scale: f32,
|
||||
|
||||
mob_type: NPCType,
|
||||
|
||||
strength_per_level: f32,
|
||||
intelligence_per_level: f32,
|
||||
agility_per_level: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AbilitySlot0,
|
||||
"AbilitySlot_0",
|
||||
{
|
||||
used: bool,
|
||||
name: String,
|
||||
rarity: Rarities,
|
||||
level: f32,
|
||||
[addons: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AbilitySlot1,
|
||||
"AbilitySlot_1",
|
||||
{
|
||||
used: bool,
|
||||
name: String,
|
||||
rarity: Rarities,
|
||||
level: f32,
|
||||
[addons: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AbilitySlot2,
|
||||
"AbilitySlot_2",
|
||||
{
|
||||
used: bool,
|
||||
name: String,
|
||||
rarity: Rarities,
|
||||
level: f32,
|
||||
[addons: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AbilitySlot3,
|
||||
"AbilitySlot_3",
|
||||
{
|
||||
used: bool,
|
||||
name: String,
|
||||
rarity: Rarities,
|
||||
level: f32,
|
||||
[addons: String],
|
||||
}
|
||||
);
|
||||
|
||||
pub trait NPCAbilitySlot {
|
||||
fn used(&mut self) -> &mut bool;
|
||||
fn name(&mut self) -> &mut String;
|
||||
fn rarity(&mut self) -> &mut Rarities;
|
||||
fn level(&mut self) -> &mut f32;
|
||||
fn addons(&mut self) -> &mut [String];
|
||||
}
|
||||
|
||||
impl NPCAbilitySlot for AbilitySlot0 {
|
||||
fn used(&mut self) -> &mut bool {
|
||||
&mut self.used
|
||||
}
|
||||
|
||||
fn name(&mut self) -> &mut String {
|
||||
&mut self.name
|
||||
}
|
||||
|
||||
fn rarity(&mut self) -> &mut Rarities {
|
||||
&mut self.rarity
|
||||
}
|
||||
|
||||
fn level(&mut self) -> &mut f32 {
|
||||
&mut self.level
|
||||
}
|
||||
|
||||
fn addons(&mut self) -> &mut [String] {
|
||||
&mut self.addons
|
||||
}
|
||||
}
|
||||
|
||||
impl NPCAbilitySlot for AbilitySlot1 {
|
||||
fn used(&mut self) -> &mut bool {
|
||||
&mut self.used
|
||||
}
|
||||
|
||||
fn name(&mut self) -> &mut String {
|
||||
&mut self.name
|
||||
}
|
||||
|
||||
fn rarity(&mut self) -> &mut Rarities {
|
||||
&mut self.rarity
|
||||
}
|
||||
|
||||
fn level(&mut self) -> &mut f32 {
|
||||
&mut self.level
|
||||
}
|
||||
|
||||
fn addons(&mut self) -> &mut [String] {
|
||||
&mut self.addons
|
||||
}
|
||||
}
|
||||
|
||||
impl NPCAbilitySlot for AbilitySlot2 {
|
||||
fn used(&mut self) -> &mut bool {
|
||||
&mut self.used
|
||||
}
|
||||
|
||||
fn name(&mut self) -> &mut String {
|
||||
&mut self.name
|
||||
}
|
||||
|
||||
fn rarity(&mut self) -> &mut Rarities {
|
||||
&mut self.rarity
|
||||
}
|
||||
|
||||
fn level(&mut self) -> &mut f32 {
|
||||
&mut self.level
|
||||
}
|
||||
|
||||
fn addons(&mut self) -> &mut [String] {
|
||||
&mut self.addons
|
||||
}
|
||||
}
|
||||
|
||||
impl NPCAbilitySlot for AbilitySlot3 {
|
||||
fn used(&mut self) -> &mut bool {
|
||||
&mut self.used
|
||||
}
|
||||
|
||||
fn name(&mut self) -> &mut String {
|
||||
&mut self.name
|
||||
}
|
||||
|
||||
fn rarity(&mut self) -> &mut Rarities {
|
||||
&mut self.rarity
|
||||
}
|
||||
|
||||
fn level(&mut self) -> &mut f32 {
|
||||
&mut self.level
|
||||
}
|
||||
|
||||
fn addons(&mut self) -> &mut [String] {
|
||||
&mut self.addons
|
||||
}
|
||||
}
|
||||
|
||||
create_settings_container!(
|
||||
NPCSettings,
|
||||
{
|
||||
meta: NPCMetaInfo,
|
||||
|
||||
ability_0: AbilitySlot0,
|
||||
ability_1: AbilitySlot1,
|
||||
ability_2: AbilitySlot2,
|
||||
ability_3: AbilitySlot3,
|
||||
}
|
||||
);
|
||||
|
||||
pub struct NPCFactory {
|
||||
elite_npcs: HashMap<String, NPCSettings>,
|
||||
normal_npcs: HashMap<String, NPCSettings>,
|
||||
boss_npcs: HashMap<String, NPCSettings>,
|
||||
}
|
||||
|
||||
impl NPCFactory {
|
||||
pub fn new(npc_directory: AssetPath) -> Result<Self> {
|
||||
let files = search_dir_recursively(&npc_directory.full_path(), ".npc")?;
|
||||
|
||||
let mut elite_npcs = HashMap::new();
|
||||
let mut normal_npcs = HashMap::new();
|
||||
let mut boss_npcs = HashMap::new();
|
||||
|
||||
for npc_file in files {
|
||||
let npc_name = Path::new(&npc_file.full_path())
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let npc_setting = NPCSettings::load(npc_file)?;
|
||||
|
||||
match npc_setting.meta.mob_type {
|
||||
NPCType::Normal => normal_npcs.insert(npc_name, npc_setting),
|
||||
NPCType::Elite => elite_npcs.insert(npc_name, npc_setting),
|
||||
NPCType::Boss => boss_npcs.insert(npc_name, npc_setting),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
elite_npcs,
|
||||
normal_npcs,
|
||||
boss_npcs,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npc(&self, name: &String, npc_type: impl Into<NPCType>) -> Option<&NPCSettings> {
|
||||
match npc_type.into() {
|
||||
NPCType::Normal => self.normal_npcs.get(name),
|
||||
NPCType::Elite => self.elite_npcs.get(name),
|
||||
NPCType::Boss => self.boss_npcs.get(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn npcs(&self, npc_type: impl Into<NPCType>) -> Iter<'_, String, NPCSettings> {
|
||||
match npc_type.into() {
|
||||
NPCType::Normal => self.normal_npcs.iter(),
|
||||
NPCType::Elite => self.elite_npcs.iter(),
|
||||
NPCType::Boss => self.boss_npcs.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_setting(&self, npc: &String) -> Result<&NPCSettings> {
|
||||
self.normal_npcs
|
||||
.get(npc)
|
||||
.or(self.elite_npcs.get(npc))
|
||||
.or(self.boss_npcs.get(npc))
|
||||
.ok_or(anyhow::Error::msg(format!("NPC file not found {}", npc)))
|
||||
}
|
||||
|
||||
pub fn create_npc(
|
||||
&self,
|
||||
entity_manager: &mut EntityManager,
|
||||
game_handle: &GameHandle,
|
||||
npc: &String,
|
||||
level: u32,
|
||||
position: Vector3<f32>,
|
||||
) -> Result<EntityObject> {
|
||||
let game = game_handle.upgrade();
|
||||
let npc_settings = self.find_setting(npc)?;
|
||||
|
||||
let mut entity_object =
|
||||
entity_manager.load_entity(game.engine().assets(), &npc_settings.meta.entity_file)?;
|
||||
|
||||
// ========== create components ==========
|
||||
|
||||
// location
|
||||
let location = Location::new_and_setup(&mut entity_object)?;
|
||||
location.update_raw(position, Deg(Random::range_f32(0.0, 360.0)));
|
||||
|
||||
// scale
|
||||
let scale = npc_settings.meta.scale;
|
||||
location.set_scale(vec3(scale, scale, scale));
|
||||
|
||||
let hitbox = entity_object.get_component_mut::<HitBox>()?;
|
||||
hitbox.set_radius(hitbox.radius() * scale);
|
||||
hitbox.set_height(hitbox.height() * scale);
|
||||
|
||||
// movement
|
||||
let movement = Movement::new(
|
||||
npc_settings.meta.movement_speed,
|
||||
entity_object.get_component_mut::<Audio>().ok(),
|
||||
);
|
||||
|
||||
// attributes
|
||||
let strength = (level as f32 * npc_settings.meta.strength_per_level) as u32;
|
||||
let intelligence = (level as f32 * npc_settings.meta.intelligence_per_level) as u32;
|
||||
let agility = (level as f32 * npc_settings.meta.agility_per_level) as u32;
|
||||
|
||||
let mut attributes = Attributes::load(
|
||||
NPC_BASE_STATS + strength,
|
||||
NPC_BASE_STATS + agility,
|
||||
NPC_BASE_STATS + intelligence,
|
||||
);
|
||||
|
||||
// statistics
|
||||
let mut stats = Statistics::default();
|
||||
stats.update(&mut attributes, &game.attribute_settings, None);
|
||||
|
||||
// character status
|
||||
let character_status = CharacterStatus::new_full(&stats);
|
||||
|
||||
// name
|
||||
let npc_name = NPCName::new(npc);
|
||||
|
||||
// entity faction
|
||||
entity_object.insert_component(FactionNPC);
|
||||
|
||||
// npc type
|
||||
match npc_settings.meta.mob_type {
|
||||
NPCType::Normal => {
|
||||
entity_object.insert_component(NPCNormal);
|
||||
}
|
||||
NPCType::Elite => {
|
||||
entity_object.insert_component(NPCElite);
|
||||
}
|
||||
NPCType::Boss => {
|
||||
entity_object.insert_component(NPCBoss);
|
||||
}
|
||||
}
|
||||
|
||||
// health bar
|
||||
match npc_settings.meta.mob_type {
|
||||
NPCType::Boss => {
|
||||
let name = npc_name.name.clone();
|
||||
entity_object.insert_component(BossHealthBar::new(&game, name)?);
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// level
|
||||
let level = Level::load(level, 0, &game.experience_settings);
|
||||
|
||||
// ai
|
||||
|
||||
let ai = SimpleAI::new(6.0, 9.0, entity_object.as_entity());
|
||||
|
||||
// abilities
|
||||
// TODO
|
||||
// let abilities =
|
||||
// AbilitySlots::load_for_npc(level.level(), npc_settings, game_handle.clone())?;
|
||||
|
||||
// ========== insert components ==========
|
||||
entity_object.insert_component(movement);
|
||||
entity_object.insert_component(attributes);
|
||||
entity_object.insert_component(stats);
|
||||
entity_object.insert_component(character_status);
|
||||
entity_object.insert_component(level);
|
||||
// entity_object.insert_component(abilities);
|
||||
entity_object.insert_component(npc_name);
|
||||
|
||||
entity_object.insert_component(ai);
|
||||
|
||||
// set hit box event
|
||||
entity_object
|
||||
.get_component_mut::<HitBox>()
|
||||
.unwrap()
|
||||
.set_event(move |scene, me, collider| {
|
||||
let mut entity_multi_mut = scene.entities_multi_mut();
|
||||
|
||||
if let Ok(my_object) = entity_multi_mut.get(me) {
|
||||
if let Ok(collider_object) = entity_multi_mut.get(collider) {
|
||||
if collider_object.get_component::<FactionNPC>().is_ok() {
|
||||
// if we hit an allied NPC, move both in parallel
|
||||
let my_movement = my_object.get_component_mut::<Movement>().unwrap();
|
||||
let other_movement =
|
||||
collider_object.get_component_mut::<Movement>().unwrap();
|
||||
|
||||
if my_movement.direction().is_zero()
|
||||
|| other_movement.direction().is_zero()
|
||||
{
|
||||
return Ok(CollisionEventType::Block);
|
||||
}
|
||||
|
||||
let common_dir =
|
||||
(my_movement.direction() + other_movement.direction()).normalize();
|
||||
|
||||
my_movement.set_direction(common_dir);
|
||||
other_movement.set_direction(common_dir);
|
||||
|
||||
return Ok(CollisionEventType::Ignore);
|
||||
} else if collider_object.get_component::<FactionFriendly>().is_ok() {
|
||||
// friendly NPCs are ignored
|
||||
return Ok(CollisionEventType::Ignore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CollisionEventType::Block)
|
||||
});
|
||||
|
||||
Ok(entity_object)
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
pub use super::abilities::prelude::*;
|
||||
pub use super::ability_type::AbilityType;
|
||||
pub use super::components::*;
|
||||
pub use super::lightning::{Lightning, LightningMarker};
|
||||
pub use super::objects::hero::{Hero, MainUser};
|
||||
pub use super::objects::npc::{NPCAbilitySlot, NPCFactory, NPCSettings};
|
|
@ -1,610 +0,0 @@
|
|||
// engine
|
||||
use engine::prelude::*;
|
||||
|
||||
use assetpath::AssetPath;
|
||||
|
||||
// use lua_wrapper::LuaFunction;
|
||||
|
||||
use anyhow::Result;
|
||||
use entity_manager::*;
|
||||
use lua_wrapper::LuaFunction;
|
||||
use rpg_components::config::abilities::AbilitySettings;
|
||||
use rpg_components::config::attributes::AttributeSettings;
|
||||
use rpg_components::config::experience::ExperienceSettings;
|
||||
use rpg_components::config::items::ItemSettings;
|
||||
use rpg_components::items::ItemSystem;
|
||||
|
||||
// std
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Mutex, MutexGuard, RwLock};
|
||||
use std::{fs::create_dir_all, path::Path, sync::Arc};
|
||||
|
||||
// game
|
||||
use crate::game::configloader::*;
|
||||
use crate::loader::settings::*;
|
||||
|
||||
use cgmath::Vector3;
|
||||
use promise::Promise;
|
||||
|
||||
use super::handle::{StrongHandle, WeakHandle};
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MapInformation {
|
||||
// root directories for maps
|
||||
pub map_directories: Vec<AssetPath>,
|
||||
|
||||
// possible start map, where players get spawned on connection
|
||||
pub start_map: Option<String>,
|
||||
|
||||
// maps that are going to be ignored
|
||||
pub black_list: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct Game {
|
||||
engine: Arc<Engine>,
|
||||
|
||||
game_settings: GameSection,
|
||||
_lua_scripts: LuaScripts,
|
||||
|
||||
data_directory: String,
|
||||
|
||||
pub attribute_settings: AttributeSettings,
|
||||
pub item_settings: ItemSettings,
|
||||
pub experience_settings: ExperienceSettings,
|
||||
pub mob_settings: MobSettings,
|
||||
pub ability_settings: AbilitySettings,
|
||||
|
||||
pub maps: RwLock<HashMap<String, AssetPath>>,
|
||||
pub black_listed_maps: RwLock<HashMap<String, AssetPath>>,
|
||||
pub tiles: RwLock<HashMap<String, AssetPath>>,
|
||||
|
||||
settings_file_location: AssetPath,
|
||||
|
||||
item_system: Promise<Arc<ItemSystem>>,
|
||||
|
||||
entity_manager: Mutex<EntityManager>,
|
||||
|
||||
slide_images: Mutex<HashMap<String, Arc<Image>>>,
|
||||
|
||||
pub change_map_context: LuaFunction,
|
||||
|
||||
map_info: MapInformation,
|
||||
}
|
||||
|
||||
pub type GameHandle = WeakHandle<Game>;
|
||||
pub type StrongGame = StrongHandle<Game>;
|
||||
|
||||
impl GameHandle {
|
||||
pub fn gui_builder(&self, path: &str) -> Result<Arc<GuiBuilder>> {
|
||||
let strong = self.upgrade();
|
||||
|
||||
GuiBuilder::new(strong.engine.gui_handler(), &strong.build_data_path(path))
|
||||
}
|
||||
|
||||
pub fn gui_snippet(&self, path: &str) -> Result<Arc<GuiSnippet>> {
|
||||
let strong = self.upgrade();
|
||||
|
||||
GuiSnippet::new(strong.engine.gui_handler(), &strong.build_data_path(path))
|
||||
}
|
||||
|
||||
pub fn controller_icon(&self, button: ControllerButton) -> Result<Arc<Image>> {
|
||||
let game = self.upgrade();
|
||||
let engine = game.engine();
|
||||
|
||||
engine
|
||||
.settings()
|
||||
.controller_button_for(engine, button, ControllerType::XBox)
|
||||
}
|
||||
|
||||
pub fn build_data_path(&self, path: &str) -> AssetPath {
|
||||
self.upgrade().build_data_path(path)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Settings {
|
||||
pub core_settings: CoreSettings,
|
||||
pub user_settings: UserSettings,
|
||||
pub data_path: String,
|
||||
pub user_settings_file: AssetPath,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn load_settings<'a>(data_path: impl Into<Option<&'a str>>) -> Result<Settings> {
|
||||
let data_path = data_path.into().unwrap_or("data");
|
||||
|
||||
println!("Data directory: {}", data_path);
|
||||
|
||||
let mut core_settings = CoreSettings::load((data_path, "settings.conf"))?;
|
||||
let mut user_settings = UserSettings::load((data_path, "user_settings.conf"))?;
|
||||
|
||||
let gavania_save_dir = settings_file_dir();
|
||||
Self::verify_dir_existence(&gavania_save_dir)?;
|
||||
|
||||
let user_settings_file = AssetPath::from((gavania_save_dir, "user_settings.conf"));
|
||||
|
||||
if user_settings_file.exists() {
|
||||
user_settings.load_with_default(user_settings_file.clone())?;
|
||||
} else {
|
||||
user_settings.file_name = user_settings_file.clone();
|
||||
}
|
||||
|
||||
user_settings.store()?;
|
||||
|
||||
Self::apply_data_dir(&mut core_settings, data_path);
|
||||
|
||||
Ok(Settings {
|
||||
core_settings,
|
||||
user_settings,
|
||||
data_path: data_path.to_string(),
|
||||
user_settings_file,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new(settings: Settings) -> Result<StrongGame> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
Self::set_radeon_ray_tracing();
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
Self::check_wayland_for_sdl();
|
||||
}
|
||||
|
||||
let game_settings = settings.core_settings.game.clone();
|
||||
let lua_scripts = settings.core_settings.lua_scripts.clone();
|
||||
let map_info = settings.core_settings.map_info();
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut engine_create_info =
|
||||
into_engine_info(settings.core_settings, settings.user_settings);
|
||||
|
||||
{
|
||||
engine_create_info.app_info.application_name = "Gavania".to_string();
|
||||
engine_create_info.app_info.application_version = 1;
|
||||
}
|
||||
|
||||
let attribute_settings =
|
||||
AttributeSettings::load((settings.data_path.as_str(), "configs/stats.conf"))?;
|
||||
let mut item_settings =
|
||||
ItemSettings::load((settings.data_path.as_str(), "configs/items.conf"))?;
|
||||
let experience_settings =
|
||||
ExperienceSettings::load((settings.data_path.as_str(), "configs/experience.conf"))?;
|
||||
let mob_settings = MobSettings::load((settings.data_path.as_str(), "configs/mobs.conf"))?;
|
||||
let mut ability_settings =
|
||||
AbilitySettings::load((settings.data_path.as_str(), "configs/abilities.conf"))?;
|
||||
|
||||
Self::apply_data_dir_to_configs(
|
||||
&mut item_settings,
|
||||
&mut ability_settings,
|
||||
&settings.data_path,
|
||||
);
|
||||
|
||||
// ----------------- maps ---------------------
|
||||
let mut map_map = HashMap::new();
|
||||
let mut black_listed_maps = HashMap::new();
|
||||
|
||||
for map_directory in map_info.map_directories.iter() {
|
||||
let map_vector = search_dir_recursively(&map_directory.full_path(), "")?;
|
||||
|
||||
'outer: for file in &map_vector {
|
||||
let s = file.full_path();
|
||||
let path = Path::new(s.as_str());
|
||||
|
||||
if let Some(name) = path.file_stem() {
|
||||
if let Some(name_str) = name.to_str() {
|
||||
let string = name_str.to_string();
|
||||
|
||||
for blacked in map_info.black_list.iter() {
|
||||
if *blacked == string {
|
||||
assert!(
|
||||
black_listed_maps
|
||||
.insert(string.clone(), file.clone())
|
||||
.is_none(),
|
||||
"blacklisted map already present: {}",
|
||||
string
|
||||
);
|
||||
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
assert!(
|
||||
map_map.insert(string.clone(), file.clone()).is_none(),
|
||||
"map already present: {}",
|
||||
string
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let game = {
|
||||
engine_create_info.gui_info.resource_directory = settings.data_path.as_str().into();
|
||||
engine_create_info.resource_base_path = settings.data_path.clone();
|
||||
|
||||
let engine = Engine::new(engine_create_info.clone())?;
|
||||
|
||||
// ----------------- tiles --------------------
|
||||
let tile_vector = search_dir_recursively(
|
||||
engine.settings().tiles_directory().full_path().as_str(),
|
||||
".png",
|
||||
)?;
|
||||
let mut tile_map = HashMap::new();
|
||||
|
||||
for file in &tile_vector {
|
||||
let s = file.full_path();
|
||||
let path = Path::new(s.as_str());
|
||||
|
||||
if let Some(name) = path.file_stem() {
|
||||
if let Some(name_str) = name.to_str() {
|
||||
let string = name_str.to_string();
|
||||
|
||||
tile_map.insert(string.clone(), file.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let item_system = Promise::new({
|
||||
let engine = engine.clone();
|
||||
let item_settings = item_settings.clone();
|
||||
let ability_settings = ability_settings.clone();
|
||||
let attribute_settings = attribute_settings.clone();
|
||||
let ability_directory = game_settings.ability_directory.clone();
|
||||
let data_path = settings.data_path.clone();
|
||||
|
||||
move || {
|
||||
Ok(Arc::new(ItemSystem::new(
|
||||
&engine,
|
||||
&item_settings,
|
||||
&ability_settings,
|
||||
&attribute_settings,
|
||||
&ability_directory,
|
||||
&data_path,
|
||||
)?))
|
||||
}
|
||||
});
|
||||
|
||||
StrongHandle::new(Game {
|
||||
maps: RwLock::new(map_map),
|
||||
black_listed_maps: RwLock::new(black_listed_maps),
|
||||
tiles: RwLock::new(tile_map),
|
||||
|
||||
change_map_context: LuaFunction::new(&lua_scripts.change_map)?,
|
||||
game_settings,
|
||||
_lua_scripts: lua_scripts,
|
||||
|
||||
data_directory: settings.data_path.to_string(),
|
||||
|
||||
attribute_settings,
|
||||
item_settings,
|
||||
experience_settings,
|
||||
mob_settings,
|
||||
ability_settings,
|
||||
|
||||
item_system,
|
||||
|
||||
entity_manager: Mutex::new(EntityManager::new(
|
||||
engine.settings().entity_directory().clone(),
|
||||
)?),
|
||||
|
||||
settings_file_location: settings.user_settings_file,
|
||||
|
||||
slide_images: Mutex::new(HashMap::new()),
|
||||
|
||||
map_info,
|
||||
|
||||
engine,
|
||||
})
|
||||
};
|
||||
|
||||
Ok(game)
|
||||
}
|
||||
|
||||
fn verify_dir_existence(dir: &str) -> Result<()> {
|
||||
let path = Path::new(&dir);
|
||||
|
||||
if !path.exists() {
|
||||
create_dir_all(path)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&self) -> Result<()> {
|
||||
self.engine.run()
|
||||
}
|
||||
|
||||
pub fn set_game_state<E>(&self, game_object: E) -> Result<()>
|
||||
where
|
||||
E: EngineObject + Send + Sync,
|
||||
{
|
||||
self.engine.set_game_object(Some(game_object))
|
||||
}
|
||||
|
||||
pub fn quit(&self) -> Result<()> {
|
||||
self.store_settings()?;
|
||||
|
||||
self.engine.quit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn set_radeon_ray_tracing() {
|
||||
std::env::set_var("RADV_PERFTEST", "rt");
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn check_wayland_for_sdl() {
|
||||
if let Ok(session_type) = std::env::var("XDG_SESSION_TYPE") {
|
||||
if session_type == "wayland" {
|
||||
std::env::set_var("SDL_VIDEODRIVER", "wayland");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_directories(&self) -> &[AssetPath] {
|
||||
&self.map_info.map_directories
|
||||
}
|
||||
|
||||
pub fn home_map(&self) -> Option<&String> {
|
||||
self.map_info.start_map.as_ref()
|
||||
}
|
||||
|
||||
pub fn black_listed_maps(&self) -> &[String] {
|
||||
&self.map_info.black_list
|
||||
}
|
||||
|
||||
pub fn engine(&self) -> &Arc<Engine> {
|
||||
&self.engine
|
||||
}
|
||||
|
||||
pub fn game_settings(&self) -> &GameSection {
|
||||
&self.game_settings
|
||||
}
|
||||
|
||||
pub fn item_system(&self) -> Arc<ItemSystem> {
|
||||
self.item_system.get()
|
||||
}
|
||||
|
||||
pub fn data_directory(&self) -> &str {
|
||||
&self.data_directory
|
||||
}
|
||||
|
||||
pub fn build_data_path(&self, path: &str) -> AssetPath {
|
||||
AssetPath::from((self.data_directory.as_str(), path))
|
||||
}
|
||||
|
||||
pub fn entity_manager(&self) -> MutexGuard<'_, EntityManager> {
|
||||
self.entity_manager.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn slide_images(&self) -> MutexGuard<'_, HashMap<String, Arc<Image>>> {
|
||||
self.slide_images.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn gui_builder(&self, path: &str) -> Result<Arc<GuiBuilder>> {
|
||||
GuiBuilder::new(self.engine.gui_handler(), &self.build_data_path(path))
|
||||
}
|
||||
|
||||
pub fn gui_snippet(&self, path: &str) -> Result<Arc<GuiSnippet>> {
|
||||
GuiSnippet::new(self.engine.gui_handler(), &self.build_data_path(path))
|
||||
}
|
||||
|
||||
pub fn store_settings(&self) -> Result<()> {
|
||||
let mut user_settings = UserSettings::load(self.settings_file_location.clone())?;
|
||||
|
||||
// first update settings
|
||||
if self.engine.window_config().is_fullscreen() {
|
||||
user_settings.window.fullscreen = true;
|
||||
} else {
|
||||
user_settings.window.fullscreen = false;
|
||||
let render_core = self.engine.context().render_core();
|
||||
|
||||
user_settings.window.width = render_core.width();
|
||||
user_settings.window.height = render_core.height();
|
||||
}
|
||||
|
||||
{
|
||||
let sound = self.engine.sound();
|
||||
|
||||
user_settings.audio.master = sound.volume("master")?;
|
||||
user_settings.audio.gui = sound.volume("gui")?;
|
||||
user_settings.audio.sfx = sound.volume("sfx")?;
|
||||
user_settings.audio.music = sound.volume("music")?;
|
||||
}
|
||||
|
||||
// check if renderer type actually changed
|
||||
// if so, clear all loaded entities
|
||||
let renderer_type = self.engine.settings().graphics_info()?.render_type;
|
||||
|
||||
if renderer_type != user_settings.graphics.renderer_type {
|
||||
user_settings.graphics.renderer_type = renderer_type;
|
||||
|
||||
self.engine.assets().clear()?;
|
||||
}
|
||||
|
||||
user_settings.store()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_frustum_check(scene: &mut Scene) {
|
||||
let entity_name = "Lightning".to_string();
|
||||
|
||||
scene.set_frustum_check(move |entity, _view_proj, _view_frustum, frustum_planes| {
|
||||
// filter everything that isnt Draw
|
||||
if !entity.contains_component::<Draw>() {
|
||||
if entity.debug_name.as_ref() == Some(&entity_name) {
|
||||
println!("{entity_name}-Entity does not contain Draw");
|
||||
}
|
||||
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// filter everything that isnt Bounding Box
|
||||
let bb = match entity.get_component::<BoundingBox>() {
|
||||
Ok(bounding_box) => bounding_box,
|
||||
Err(_) => {
|
||||
if entity.debug_name.as_ref() == Some(&entity_name) {
|
||||
println!("{entity_name}-Entity does not contain BoundingBox");
|
||||
}
|
||||
|
||||
return Ok(false);
|
||||
}
|
||||
};
|
||||
|
||||
let transformation_matrix = entity
|
||||
.get_component::<Location>()
|
||||
.ok()
|
||||
.map(|location| location.transformation_matrix());
|
||||
|
||||
let planes = frustum_planes.as_array();
|
||||
let corners: Vec<Vector3<f32>> = bb
|
||||
.corners()
|
||||
.iter()
|
||||
.map(|corner| match transformation_matrix {
|
||||
Some(m) => (m * corner.extend(1.0)).xyz(),
|
||||
None => *corner,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// if there is a plane where all corners are outside, the box is not visible
|
||||
for plane in planes {
|
||||
let mut outside = true;
|
||||
|
||||
for corner in corners.iter() {
|
||||
if !plane.is_above(*corner) {
|
||||
outside = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if outside {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
if entity.debug_name.as_ref() == Some(&entity_name) {
|
||||
println!("{entity_name}-Entity is inside");
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
});
|
||||
}
|
||||
|
||||
fn apply_data_dir(settings: &mut CoreSettings, data_dir: &str) {
|
||||
// game
|
||||
settings.game.sounds_directory.set_prefix(data_dir);
|
||||
settings.game.music_directory.set_prefix(data_dir);
|
||||
settings.game.ability_icon_directory.set_prefix(data_dir);
|
||||
settings.game.ability_directory.set_prefix(data_dir);
|
||||
settings.game.particle_directory.set_prefix(data_dir);
|
||||
settings.game.slides_directory.set_prefix(data_dir);
|
||||
settings.game.npc_directory.set_prefix(data_dir);
|
||||
|
||||
// map info
|
||||
settings.map_infos.directory.set_prefix(data_dir);
|
||||
|
||||
// scripts
|
||||
settings.lua_scripts.change_map.set_prefix(data_dir);
|
||||
}
|
||||
|
||||
fn apply_data_dir_to_configs(
|
||||
item_settings: &mut ItemSettings,
|
||||
ability_settings: &mut AbilitySettings,
|
||||
data_dir: &str,
|
||||
) {
|
||||
item_settings.icon_paths.amulet.set_prefix(data_dir);
|
||||
item_settings.icon_paths.background.set_prefix(data_dir);
|
||||
item_settings.icon_paths.boots.set_prefix(data_dir);
|
||||
item_settings.icon_paths.chest.set_prefix(data_dir);
|
||||
item_settings.icon_paths.helmet.set_prefix(data_dir);
|
||||
item_settings.icon_paths.jewel.set_prefix(data_dir);
|
||||
item_settings.icon_paths.ring.set_prefix(data_dir);
|
||||
item_settings.icon_paths.belt.set_prefix(data_dir);
|
||||
item_settings.icon_paths.gloves.set_prefix(data_dir);
|
||||
item_settings.icon_paths.main_hand.set_prefix(data_dir);
|
||||
item_settings.icon_paths.off_hand.set_prefix(data_dir);
|
||||
|
||||
item_settings.jewel_paths.first.set_prefix(data_dir);
|
||||
item_settings.jewel_paths.second.set_prefix(data_dir);
|
||||
item_settings.jewel_paths.third.set_prefix(data_dir);
|
||||
item_settings.jewel_paths.fourth.set_prefix(data_dir);
|
||||
|
||||
ability_settings.icons.book.set_prefix(data_dir);
|
||||
ability_settings.icons.damage.set_prefix(data_dir);
|
||||
ability_settings.icons.projectile_speed.set_prefix(data_dir);
|
||||
ability_settings.icons.bounce.set_prefix(data_dir);
|
||||
ability_settings.icons.explosion.set_prefix(data_dir);
|
||||
ability_settings.icons.size.set_prefix(data_dir);
|
||||
ability_settings
|
||||
.icons
|
||||
.additional_projectiles
|
||||
.set_prefix(data_dir);
|
||||
ability_settings.icons.cool_down.set_prefix(data_dir);
|
||||
ability_settings.icons.distance.set_prefix(data_dir);
|
||||
ability_settings.icons.addon_background.set_prefix(data_dir);
|
||||
}
|
||||
|
||||
fn gather_maps(
|
||||
map_directories: &[AssetPath],
|
||||
home_map: Option<&str>,
|
||||
ignore_maps: &[String],
|
||||
) -> Result<Vec<String>> {
|
||||
let mut maps = Vec::new();
|
||||
|
||||
for map_directory in map_directories.iter() {
|
||||
for map in search_dir_recursively(&map_directory.full_path(), "")? {
|
||||
let map_name = Path::new(&map.full_path())
|
||||
.file_name()
|
||||
.expect("map is not a file name")
|
||||
.to_str()
|
||||
.expect("failed converting OsStr to str")
|
||||
.to_string();
|
||||
|
||||
if let Some(home) = home_map {
|
||||
if map_name == home {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ignore_maps.contains(&map_name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
maps.push(map_name);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(maps)
|
||||
}
|
||||
|
||||
pub fn next_map(&self, current_map: &str) -> Result<String> {
|
||||
let map_directories = self.map_directories();
|
||||
let home_map = self.home_map().map(|s| s.as_str());
|
||||
let ignore_maps: Vec<String> = self
|
||||
.black_listed_maps
|
||||
.read()
|
||||
.unwrap()
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let maps = Self::gather_maps(map_directories, home_map, &ignore_maps)?;
|
||||
let map_count = maps.len();
|
||||
|
||||
let next_map = self.change_map_context.execute((
|
||||
current_map,
|
||||
home_map.unwrap_or(""),
|
||||
maps,
|
||||
map_count,
|
||||
))?;
|
||||
|
||||
Ok(next_map)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Game {
|
||||
fn drop(&mut self) {}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
use std::{
|
||||
ops::Deref,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
pub struct StrongHandle<T> {
|
||||
strong: Arc<T>,
|
||||
}
|
||||
|
||||
impl<T> StrongHandle<T> {
|
||||
pub fn new(t: T) -> Self {
|
||||
Self {
|
||||
strong: Arc::new(t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for StrongHandle<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.strong
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WeakHandle<T> {
|
||||
weak: Weak<T>,
|
||||
}
|
||||
|
||||
impl<T> WeakHandle<T> {
|
||||
pub fn on<F, R>(&self, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&T) -> Result<R>,
|
||||
{
|
||||
let strong: Arc<T> = self
|
||||
.weak
|
||||
.upgrade()
|
||||
.ok_or(anyhow!("failed to get strong handle"))?;
|
||||
|
||||
f(&strong)
|
||||
}
|
||||
|
||||
pub fn upgrade(&self) -> StrongHandle<T> {
|
||||
StrongHandle {
|
||||
strong: self.weak.upgrade().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_upgrade(&self) -> Result<StrongHandle<T>> {
|
||||
Ok(StrongHandle {
|
||||
strong: self
|
||||
.weak
|
||||
.upgrade()
|
||||
.ok_or(anyhow!("failed to upgrade WeakHandle"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for WeakHandle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
weak: self.weak.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<&StrongHandle<T>> for WeakHandle<T> {
|
||||
fn from(handle: &StrongHandle<T>) -> Self {
|
||||
Self {
|
||||
weak: Arc::downgrade(&handle.strong),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#[macro_use]
|
||||
pub mod configloader;
|
||||
pub mod content;
|
||||
pub mod game;
|
||||
pub mod handle;
|
|
@ -1,14 +0,0 @@
|
|||
//! This is the documentation for `gavania-core`
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(unused_must_use)]
|
||||
|
||||
pub mod game;
|
||||
mod loader;
|
||||
|
||||
pub use anyhow::{Error, Result};
|
||||
|
||||
pub use engine::prelude::*;
|
||||
pub use game::content::prelude::*;
|
||||
pub use game::game::{Game, GameHandle, StrongGame};
|
||||
pub use game::handle::*;
|
||||
pub use loader::settings::*;
|
|
@ -1 +0,0 @@
|
|||
pub mod settings;
|
|
@ -1,325 +0,0 @@
|
|||
#![allow(unused)]
|
||||
|
||||
use assetpath::AssetPath;
|
||||
use engine::prelude::*;
|
||||
use std::{collections::HashMap, env::var, marker::PhantomData};
|
||||
|
||||
use crate::game::game::MapInformation;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn settings_file_dir() -> String {
|
||||
let b = var("LOCALAPPDATA").expect("couldn't get local appdata variable");
|
||||
|
||||
format!("{}\\gavania\\", b)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn settings_file_dir() -> String {
|
||||
let b = var("HOME").expect("couldn't get HOME variable");
|
||||
|
||||
format!("{}/.local/share/gavania/", b)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn settings_file_dir() -> String {
|
||||
compile_error!("settings directory not set!");
|
||||
}
|
||||
|
||||
create_settings_section!(
|
||||
LuaScripts,
|
||||
"Scripts",
|
||||
{
|
||||
change_map: AssetPath,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
VulkanSection,
|
||||
"Vulkan",
|
||||
{
|
||||
debugging: bool,
|
||||
steam_layer: bool,
|
||||
verbose: bool,
|
||||
renderdoc: bool,
|
||||
util: bool,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
WindowSection,
|
||||
"Window",
|
||||
{
|
||||
window_title: String,
|
||||
width: u32,
|
||||
height: u32,
|
||||
fullscreen: bool,
|
||||
preferred_display: String,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
InputSection,
|
||||
"Input",
|
||||
{
|
||||
keyboard_enabled: bool,
|
||||
mouse_enabled: bool,
|
||||
controller_enabled: bool,
|
||||
axis_enable_deadzone: f32,
|
||||
axis_disable_deadzone: f32,
|
||||
trigger_enable_deadzone: f32,
|
||||
trigger_disable_deadzone: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
GuiSection,
|
||||
"Gui",
|
||||
{
|
||||
font: AssetPath,
|
||||
default_button: AssetPath,
|
||||
default_selected_button: AssetPath,
|
||||
default_click_sound: AssetPath,
|
||||
default_hover_sound: AssetPath,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
GraphicsSection,
|
||||
"Graphics",
|
||||
{
|
||||
renderer_type: SceneType,
|
||||
render_scale: f32,
|
||||
sample_count: u32,
|
||||
vsync: bool,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
RaytracingSection,
|
||||
"Raytracing",
|
||||
{
|
||||
tris_per_as: u32,
|
||||
max_samplers: u32,
|
||||
acceleration_count: u32,
|
||||
stack_size: u32,
|
||||
max_lights: u32,
|
||||
recursion_depth: u32,
|
||||
use_default_pipeline: bool,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
RasterizerSection,
|
||||
"Rasterizing",
|
||||
{
|
||||
shadow_image_size: u32,
|
||||
enable_lighting: bool,
|
||||
use_deferred: bool,
|
||||
use_shadow_maps: bool,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
EngineSection,
|
||||
"Engine",
|
||||
{
|
||||
tile_directory: AssetPath,
|
||||
entity_directory: AssetPath,
|
||||
gltf_directory: AssetPath,
|
||||
xbox_controller_directory: AssetPath,
|
||||
steam_controller_directory: AssetPath,
|
||||
ps4_controller_directory: AssetPath,
|
||||
light_key: AssetPath,
|
||||
dark_key: AssetPath,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
AudioSection,
|
||||
"Audio",
|
||||
{
|
||||
master: f32,
|
||||
music: f32,
|
||||
gui: f32,
|
||||
sfx: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
TweakSection,
|
||||
"Tweaks",
|
||||
{
|
||||
enable_gamemode: bool,
|
||||
enable_backtrace: bool,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
GameSection,
|
||||
"Game",
|
||||
{
|
||||
sounds_directory: AssetPath,
|
||||
music_directory: AssetPath,
|
||||
|
||||
ability_icon_directory: AssetPath,
|
||||
ability_directory: AssetPath,
|
||||
slides_directory: AssetPath,
|
||||
particle_directory: AssetPath,
|
||||
npc_directory: AssetPath,
|
||||
|
||||
camera_distance: f32,
|
||||
camera_angle: f32,
|
||||
loot_action_radius: f32,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_section!(
|
||||
MapInfos,
|
||||
"Map Infos",
|
||||
{
|
||||
directory: AssetPath,
|
||||
start: String,
|
||||
[black_list: String],
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_container!(
|
||||
CoreSettings,
|
||||
{
|
||||
vulkan: VulkanSection,
|
||||
gui: GuiSection,
|
||||
engine: EngineSection,
|
||||
game: GameSection,
|
||||
map_infos: MapInfos,
|
||||
lua_scripts: LuaScripts,
|
||||
}
|
||||
);
|
||||
|
||||
create_settings_container!(
|
||||
UserSettings,
|
||||
{
|
||||
window: WindowSection,
|
||||
input: InputSection,
|
||||
graphics: GraphicsSection,
|
||||
ray_tracing: RaytracingSection,
|
||||
rasterizer: RasterizerSection,
|
||||
audio: AudioSection,
|
||||
tweaks: TweakSection,
|
||||
}
|
||||
);
|
||||
|
||||
impl CoreSettings {
|
||||
pub fn map_info(&self) -> MapInformation {
|
||||
MapInformation {
|
||||
map_directories: vec![self.map_infos.directory.clone()],
|
||||
start_map: if self.map_infos.start.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.map_infos.start.clone())
|
||||
},
|
||||
black_list: self.map_infos.black_list.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_engine_info<'a>(
|
||||
core_settings: CoreSettings,
|
||||
user_settings: UserSettings,
|
||||
) -> EngineCreateInfo<'a> {
|
||||
EngineCreateInfo {
|
||||
app_info: ApplicationInfo::default(),
|
||||
|
||||
window_info: WindowCreateInfo {
|
||||
title: user_settings.window.window_title,
|
||||
width: user_settings.window.width,
|
||||
height: user_settings.window.height,
|
||||
fullscreen: user_settings.window.fullscreen,
|
||||
requested_display: if user_settings.window.preferred_display.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(user_settings.window.preferred_display)
|
||||
},
|
||||
},
|
||||
|
||||
os_specific_config: OsSpecificConfig {
|
||||
enable_game_mode: user_settings.tweaks.enable_gamemode,
|
||||
},
|
||||
|
||||
vulkan_debug_info: VulkanDebugInfo {
|
||||
debugging: core_settings.vulkan.debugging,
|
||||
steam_layer: core_settings.vulkan.steam_layer,
|
||||
verbose: core_settings.vulkan.verbose,
|
||||
renderdoc: core_settings.vulkan.renderdoc,
|
||||
},
|
||||
volume_info: {
|
||||
let mut audio = HashMap::new();
|
||||
|
||||
audio.insert("master".to_string(), user_settings.audio.master);
|
||||
audio.insert("music".to_string(), user_settings.audio.music);
|
||||
audio.insert("gui".to_string(), user_settings.audio.gui);
|
||||
audio.insert("sfx".to_string(), user_settings.audio.sfx);
|
||||
|
||||
audio
|
||||
},
|
||||
|
||||
gui_info: GuiHandlerCreateInfo {
|
||||
menu_button: core_settings.gui.default_button,
|
||||
menu_button_selected: core_settings.gui.default_selected_button,
|
||||
font: Font::Path(core_settings.gui.font),
|
||||
click_sound: core_settings.gui.default_click_sound,
|
||||
hover_sound: core_settings.gui.default_hover_sound,
|
||||
resource_directory: AssetPath::default(),
|
||||
},
|
||||
|
||||
enable_backtrace: user_settings.tweaks.enable_backtrace,
|
||||
enable_mouse: user_settings.input.mouse_enabled,
|
||||
enable_keyboard: user_settings.input.keyboard_enabled,
|
||||
enable_controller: user_settings.input.controller_enabled,
|
||||
|
||||
resource_base_path: String::new(),
|
||||
|
||||
controller_deadzones: ControllerDeadzones {
|
||||
axis_enable_deadzone: user_settings.input.axis_enable_deadzone,
|
||||
axis_disable_deadzone: user_settings.input.axis_disable_deadzone,
|
||||
trigger_enable_deadzone: user_settings.input.trigger_enable_deadzone,
|
||||
trigger_disable_deadzone: user_settings.input.trigger_disable_deadzone,
|
||||
},
|
||||
|
||||
controller_directories: ControllerPictureDirectories {
|
||||
xbox_path: core_settings.engine.xbox_controller_directory,
|
||||
steam_path: core_settings.engine.steam_controller_directory,
|
||||
ps4_path: core_settings.engine.ps4_controller_directory,
|
||||
},
|
||||
asset_directories: AssetDirectories {
|
||||
entity_file_directory: core_settings.engine.entity_directory,
|
||||
gltf_file_directory: core_settings.engine.gltf_directory,
|
||||
tile_file_directory: core_settings.engine.tile_directory,
|
||||
},
|
||||
|
||||
graphics_info: GraphicsInfo {
|
||||
sample_count: VkSampleCountFlags::from(user_settings.graphics.sample_count),
|
||||
render_type: user_settings.graphics.renderer_type,
|
||||
render_scale: user_settings.graphics.render_scale,
|
||||
vsync: user_settings.graphics.vsync,
|
||||
},
|
||||
raytracing_info: RaytracingInfo {
|
||||
triangles_per_as: user_settings.ray_tracing.tris_per_as,
|
||||
accelerate_buffer_size: user_settings.ray_tracing.acceleration_count,
|
||||
stack_size: user_settings.ray_tracing.stack_size,
|
||||
max_lights: user_settings.ray_tracing.max_lights,
|
||||
recursion_depth: user_settings.ray_tracing.recursion_depth,
|
||||
use_default_pipeline: user_settings.ray_tracing.use_default_pipeline,
|
||||
max_samplers: user_settings.ray_tracing.max_samplers,
|
||||
},
|
||||
rasterizer_info: RasterizerInfo {
|
||||
shadow_image_size: user_settings.rasterizer.shadow_image_size,
|
||||
enable_lighting: user_settings.rasterizer.enable_lighting,
|
||||
use_deferred: user_settings.rasterizer.use_deferred,
|
||||
use_shadow_maps: user_settings.rasterizer.use_shadow_maps,
|
||||
},
|
||||
key_backgrounds: ButtonBackgrounds {
|
||||
light: core_settings.engine.light_key,
|
||||
dark: core_settings.engine.dark_key,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
fn dynamic_library(library_name: &str) {
|
||||
println!("cargo:rustc-link-lib=dylib={}", library_name);
|
||||
}
|
||||
|
||||
fn static_library(library_name: &str) {
|
||||
println!("cargo:rustc-link-lib=static={}", library_name);
|
||||
}
|
||||
|
||||
fn mac_framework_path(path: &str) {
|
||||
println!("cargo:rustc-link-search=framework={}", path);
|
||||
}
|
||||
|
||||
fn native_path(path: &str) {
|
||||
println!("cargo:rustc-link-search=native={}", path);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn set_compiler_flags() {
|
||||
native_path("Gavania/lib/MacOS");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn set_compiler_flags() {
|
||||
native_path("lib/windows");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn set_compiler_flags() {
|
||||
native_path("lib/linux");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
set_compiler_flags();
|
||||
}
|
|
@ -10,3 +10,4 @@ assetpath = { workspace = true }
|
|||
destructure_traitobject = { workspace = true }
|
||||
|
||||
engine = { path = "../engine" }
|
||||
ecs = { path = "../ecs" }
|
||||
|
|
210
map/src/map.rs
210
map/src/map.rs
|
@ -2,7 +2,6 @@
|
|||
use anyhow::Result;
|
||||
use assetpath::AssetPath;
|
||||
|
||||
use engine::prelude::PrimitiveMaterial;
|
||||
use engine::prelude::*;
|
||||
|
||||
use super::{
|
||||
|
@ -12,26 +11,20 @@ use super::{
|
|||
};
|
||||
|
||||
use super::async_db::AsyncDBAccess;
|
||||
|
||||
use super::map_db::{DBEntityInfo, MapDataBase};
|
||||
|
||||
// std
|
||||
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use std::{collections::HashMap, intrinsics::transmute};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops::IndexMut;
|
||||
use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
// cgmath
|
||||
|
||||
use cgmath::Rad;
|
||||
|
||||
use cgmath::{vec2, InnerSpace, Vector2, Vector3, Zero};
|
||||
use cgmath::{InnerSpace, Vector2, Vector3, Zero, vec2};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MapObjectType {
|
||||
|
@ -58,16 +51,11 @@ impl Map {
|
|||
// describes the length of a chunk (e.g.: 10 in x and in y direction = 100 tiles per chunk)
|
||||
const CHUNK_DIMENSION: u32 = 10;
|
||||
|
||||
pub fn downcast(upcasted: &Box<dyn engine::prelude::Map>) -> &Box<Self> {
|
||||
unsafe { transmute(destructure_traitobject::data(upcasted)) }
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
name: &str,
|
||||
width: u32,
|
||||
height: u32,
|
||||
engine: &Engine,
|
||||
scene: &mut Scene,
|
||||
world: &mut World,
|
||||
maps: &mut HashMap<String, AssetPath>,
|
||||
tiles: &HashMap<String, AssetPath>,
|
||||
entity_manager: &mut impl AssetLoader,
|
||||
|
@ -76,8 +64,8 @@ impl Map {
|
|||
let map_path = build_path(&("maps/".to_string() + name));
|
||||
|
||||
let sql_connection = MapDataBase::new(map_path.full_path(), name, width, height)?;
|
||||
let map = Self::load_data_from_db(engine, scene, sql_connection, tiles, entity_manager)?;
|
||||
scene.set_map(Some(Box::new(map)))?;
|
||||
let map = Self::load_data_from_db(world, sql_connection, tiles, entity_manager)?;
|
||||
world.resources.insert(map);
|
||||
|
||||
maps.insert(name.to_string(), map_path);
|
||||
|
||||
|
@ -85,15 +73,14 @@ impl Map {
|
|||
}
|
||||
|
||||
pub fn load_raw_scene(
|
||||
engine: &Engine,
|
||||
scene: &mut Scene,
|
||||
world: &mut World,
|
||||
path: AssetPath,
|
||||
tiles: &HashMap<String, AssetPath>,
|
||||
entity_manager: &mut impl AssetLoader,
|
||||
) -> Result<()> {
|
||||
let sql_connection = MapDataBase::load(&path.full_path())?;
|
||||
let map = Self::load_data_from_db(engine, scene, sql_connection, tiles, entity_manager)?;
|
||||
scene.set_map(Some(Box::new(map)))?;
|
||||
let map = Self::load_data_from_db(world, sql_connection, tiles, entity_manager)?;
|
||||
world.resources.insert(map);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -110,13 +97,7 @@ impl Map {
|
|||
self.async_db.dispatch()
|
||||
}
|
||||
|
||||
pub fn add_height(
|
||||
&self,
|
||||
x: u32,
|
||||
y: u32,
|
||||
height: f32,
|
||||
scene: &mut impl SceneEntities,
|
||||
) -> Result<()> {
|
||||
pub fn add_height(&self, x: u32, y: u32, height: f32, world: &mut World) -> Result<()> {
|
||||
let mut data = self.data.write().unwrap();
|
||||
|
||||
data.add_height(x, y, height, &self.async_db)?;
|
||||
|
@ -135,7 +116,7 @@ impl Map {
|
|||
Self::calculate_chunk_and_primitive_indices(dx, dy);
|
||||
|
||||
let chunk_entity = data.chunk_handles[&chunk];
|
||||
let entity = scene.entity_mut(chunk_entity)?;
|
||||
let entity = world.entity_mut(chunk_entity)?;
|
||||
|
||||
let draw = entity.get_component_mut::<Draw>()?;
|
||||
let mesh = &mut draw[0];
|
||||
|
@ -195,8 +176,8 @@ impl Map {
|
|||
self.updated.store(true, SeqCst);
|
||||
}
|
||||
|
||||
pub fn check_height_at(&self, x: u32, y: u32, scene: &mut Scene) -> Result<()> {
|
||||
self.data.write().unwrap().check_height_at(x, y, scene)
|
||||
pub fn check_height_at(&self, x: u32, y: u32, world: &mut World) -> Result<()> {
|
||||
self.data.write().unwrap().check_height_at(x, y, world)
|
||||
}
|
||||
|
||||
pub fn npc_spawn_info(&self, position: Vector2<f32>) -> Result<MapNPCSpawnInfo> {
|
||||
|
@ -251,13 +232,13 @@ impl Map {
|
|||
|
||||
pub fn change_npc_spawn(
|
||||
&self,
|
||||
scene_contents: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
position: Vector2<f32>,
|
||||
npc_spawn_parameter: NPCSpawnParameter,
|
||||
) -> Result<()> {
|
||||
self.data.write().unwrap().change_npc_spawn(
|
||||
&self.async_db,
|
||||
scene_contents,
|
||||
world,
|
||||
position,
|
||||
npc_spawn_parameter,
|
||||
)?;
|
||||
|
@ -279,7 +260,7 @@ impl Map {
|
|||
x: u32,
|
||||
y: u32,
|
||||
texture: &Arc<Image>,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
) -> Result<()> {
|
||||
let mut data = self.data.write().unwrap();
|
||||
|
||||
|
@ -289,7 +270,7 @@ impl Map {
|
|||
let (chunk, primitive_index) = Self::calculate_chunk_and_primitive_indices(x, y);
|
||||
|
||||
let chunk_entity = data.chunk_handles[&chunk];
|
||||
let entity = scene.entity_mut(chunk_entity)?;
|
||||
let entity = world.entity_mut(chunk_entity)?;
|
||||
|
||||
let draw = entity.get_component_mut::<Draw>()?;
|
||||
let mesh = &mut draw[0];
|
||||
|
@ -318,7 +299,7 @@ impl Map {
|
|||
|
||||
pub fn set_entity(
|
||||
&self,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
entity_opt: Option<(String, EntityObject)>,
|
||||
position: Vector2<f32>,
|
||||
rotation: impl Into<Rad<f32>>,
|
||||
|
@ -326,18 +307,18 @@ impl Map {
|
|||
self.data
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_entity(scene, entity_opt, position, rotation, &self.async_db)
|
||||
.set_entity(world, entity_opt, position, rotation, &self.async_db)
|
||||
}
|
||||
|
||||
pub fn set_spawn_location(
|
||||
&self,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
entity_opt: Option<(String, EntityObject)>,
|
||||
position: Vector2<f32>,
|
||||
rotation: impl Into<Rad<f32>>,
|
||||
) -> Result<()> {
|
||||
self.data.write().unwrap().set_spawn_location(
|
||||
scene,
|
||||
world,
|
||||
entity_opt,
|
||||
position,
|
||||
rotation,
|
||||
|
@ -345,19 +326,19 @@ impl Map {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn disable_spawns(&self, scene: &mut impl SceneEntities) -> Result<()> {
|
||||
self.data.write().unwrap().disable_spawns(scene)
|
||||
pub fn disable_spawns(&self, world: &mut World) -> Result<()> {
|
||||
self.data.write().unwrap().disable_spawns(world)
|
||||
}
|
||||
|
||||
pub fn set_leave_location(
|
||||
&self,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
entity_opt: Option<(String, EntityObject)>,
|
||||
position: Vector2<f32>,
|
||||
rotation: impl Into<Rad<f32>>,
|
||||
) -> Result<()> {
|
||||
self.data.write().unwrap().set_leave_location(
|
||||
scene,
|
||||
world,
|
||||
entity_opt,
|
||||
position,
|
||||
rotation,
|
||||
|
@ -365,19 +346,19 @@ impl Map {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn toggle_npc_spawns(&self, engine: &Engine, scene: &mut impl SceneEntities) -> Result<()> {
|
||||
self.data.write().unwrap().toggle_npc_spawns(engine, scene)
|
||||
pub fn toggle_npc_spawns(&self, world: &mut World) -> Result<()> {
|
||||
self.data.write().unwrap().toggle_npc_spawns(world)
|
||||
}
|
||||
|
||||
pub fn set_npc_spawn_marker(
|
||||
&self,
|
||||
flag_name: &str,
|
||||
area_name: &str,
|
||||
engine: &Engine,
|
||||
world: &mut World,
|
||||
entity_manager: &mut impl AssetLoader,
|
||||
) -> Result<()> {
|
||||
self.data.write().unwrap().npc_spawn_marker = Some(SpawnMarkerEntities::new(
|
||||
engine,
|
||||
world,
|
||||
entity_manager,
|
||||
flag_name,
|
||||
area_name,
|
||||
|
@ -388,56 +369,38 @@ impl Map {
|
|||
|
||||
pub fn set_npc_spawn(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
scene: &mut impl SceneEntities,
|
||||
|
||||
world: &mut World,
|
||||
position: Vector2<f32>,
|
||||
radius: f32,
|
||||
min: u32,
|
||||
max: u32,
|
||||
) -> Result<()> {
|
||||
self.data.write().unwrap().set_npc_spawn(
|
||||
engine,
|
||||
scene,
|
||||
position,
|
||||
&self.async_db,
|
||||
radius,
|
||||
min,
|
||||
max,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn unset_npc_spawn(
|
||||
&self,
|
||||
scene: &mut impl SceneEntities,
|
||||
position: Vector2<f32>,
|
||||
) -> Result<()> {
|
||||
self.data
|
||||
.write()
|
||||
.unwrap()
|
||||
.unset_npc_spawn(scene, position, &self.async_db)
|
||||
.set_npc_spawn(world, position, &self.async_db, radius, min, max)
|
||||
}
|
||||
|
||||
pub fn set_boss_spawn(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
scene: &mut impl SceneEntities,
|
||||
position: Vector2<f32>,
|
||||
) -> Result<()> {
|
||||
pub fn unset_npc_spawn(&self, world: &mut World, position: Vector2<f32>) -> Result<()> {
|
||||
self.data
|
||||
.write()
|
||||
.unwrap()
|
||||
.set_boss_spawn(engine, scene, position, &self.async_db)
|
||||
.unset_npc_spawn(world, position, &self.async_db)
|
||||
}
|
||||
|
||||
pub fn unset_boss_spawn(
|
||||
&self,
|
||||
scene: &mut impl SceneEntities,
|
||||
position: Vector2<f32>,
|
||||
) -> Result<()> {
|
||||
pub fn set_boss_spawn(&self, world: &mut World, position: Vector2<f32>) -> Result<()> {
|
||||
self.data
|
||||
.write()
|
||||
.unwrap()
|
||||
.unset_boss_spawn(scene, position, &self.async_db)
|
||||
.set_boss_spawn(world, position, &self.async_db)
|
||||
}
|
||||
|
||||
pub fn unset_boss_spawn(&self, world: &mut World, position: Vector2<f32>) -> Result<()> {
|
||||
self.data
|
||||
.write()
|
||||
.unwrap()
|
||||
.unset_boss_spawn(world, position, &self.async_db)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -489,8 +452,7 @@ impl Map {
|
|||
}
|
||||
|
||||
fn create_tiles(
|
||||
engine: &Engine,
|
||||
scene: &mut Scene,
|
||||
world: &mut World,
|
||||
textures: &HashMap<u32, OrderedElement<Arc<Image>>>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
@ -498,6 +460,9 @@ impl Map {
|
|||
) -> Result<HashMap<(u32, u32), Entity>> {
|
||||
let tile_handles = Map::create_tile_handles(width, height, &surface);
|
||||
|
||||
let context = world.resources.get_unchecked::<Context>();
|
||||
let scene = world.resources.get_unchecked::<Scene>();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
Self::verify_tile_handles(width, height, &tile_handles);
|
||||
|
||||
|
@ -517,7 +482,7 @@ impl Map {
|
|||
let mut tile_entity = {
|
||||
let (tiles, bounding_box) = &tile_handles[&(chunk_x, chunk_y)];
|
||||
|
||||
let mut asset_mesh = AssetMesh::new(&scene.device(), scene.render_type())?;
|
||||
let mut asset_mesh = AssetMesh::new(context.device(), scene.render_type())?;
|
||||
|
||||
for tile_x in 0..Self::CHUNK_DIMENSION {
|
||||
for tile_y in 0..Self::CHUNK_DIMENSION {
|
||||
|
@ -533,7 +498,7 @@ impl Map {
|
|||
)
|
||||
.set_memory_usage(MemoryUsage::CpuOnly)
|
||||
.set_data(&buffer_data)
|
||||
.build(engine.device().clone())?;
|
||||
.build(context.device().clone())?;
|
||||
|
||||
asset_mesh.add_primitive(
|
||||
buffer,
|
||||
|
@ -545,7 +510,7 @@ impl Map {
|
|||
}
|
||||
}
|
||||
|
||||
let mut entity_object = engine.assets().empty_entity();
|
||||
let mut entity_object = AssetHandler::create(world).empty_entity();
|
||||
|
||||
entity_object.insert_component(Draw::new(vec![asset_mesh]));
|
||||
entity_object.insert_component(bounding_box.clone());
|
||||
|
@ -558,7 +523,7 @@ impl Map {
|
|||
tile_entity.debug_name = Some(format!("Chunk ({}, {})", chunk_x, chunk_y));
|
||||
}
|
||||
|
||||
let entity = scene.add_entity(tile_entity)?;
|
||||
let entity = world.add_entity(tile_entity)?;
|
||||
|
||||
tiles.insert((chunk_x, chunk_y), entity);
|
||||
}
|
||||
|
@ -662,8 +627,7 @@ impl Map {
|
|||
// DB load and create map handle
|
||||
impl Map {
|
||||
fn load_data_from_db(
|
||||
engine: &Engine,
|
||||
scene: &mut Scene,
|
||||
world: &mut World,
|
||||
data_base: MapDataBase,
|
||||
tiles: &HashMap<String, AssetPath>,
|
||||
entity_manager: &mut impl AssetLoader,
|
||||
|
@ -677,6 +641,8 @@ impl Map {
|
|||
let mob_spawn_locations = data_base.read_mob_spawn_locations()?;
|
||||
let boss_spawn_locations = data_base.read_boss_spawn_locations()?;
|
||||
|
||||
let context = world.resources.get_unchecked::<Context>();
|
||||
|
||||
// check if the amount of tiles and points is correct
|
||||
let tile_count = data_base.width() * data_base.height();
|
||||
let point_count = (data_base.width() + 1) * (data_base.height() + 1);
|
||||
|
@ -713,7 +679,7 @@ impl Map {
|
|||
return Err(anyhow::Error::msg(format!(
|
||||
"Map: Could not find index ({}) in height map",
|
||||
index
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -733,13 +699,13 @@ impl Map {
|
|||
let image = match tiles.get(&texture.name) {
|
||||
Some(path) => Image::from_file(path.clone())?
|
||||
.format(VK_FORMAT_R8G8B8A8_UNORM)
|
||||
.attach_sampler(Sampler::nearest_sampler().build(engine.device())?)
|
||||
.build(engine.device(), engine.queue())?,
|
||||
.attach_sampler(Sampler::nearest_sampler().build(context.device())?)
|
||||
.build(context.device(), context.queue())?,
|
||||
None => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"Map: Image ({}) could not be found",
|
||||
texture.name
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -769,7 +735,7 @@ impl Map {
|
|||
return Err(anyhow::Error::msg(format!(
|
||||
"Map: Index ({}) of tiles map could not be found",
|
||||
i
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -779,7 +745,7 @@ impl Map {
|
|||
return Err(anyhow::Error::msg(format!(
|
||||
"Map: Texture with id ({}) of textures_info could not be found",
|
||||
texture_id
|
||||
)))
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -791,34 +757,39 @@ impl Map {
|
|||
map_tiles,
|
||||
)?;
|
||||
|
||||
let asset_manager = engine.assets();
|
||||
let mut asset_manager = AssetHandler::create(unsafe { remove_life_time_mut(world) });
|
||||
|
||||
let entity_positions =
|
||||
Self::create_entity_alike(asset_manager, entity_manager, entity_pos, &surface, scene)?;
|
||||
let entity_positions = Self::create_entity_alike(
|
||||
&mut asset_manager,
|
||||
entity_manager,
|
||||
entity_pos,
|
||||
&surface,
|
||||
world,
|
||||
)?;
|
||||
|
||||
let spawn_positions = Self::create_entity_alike(
|
||||
asset_manager,
|
||||
&mut asset_manager,
|
||||
entity_manager,
|
||||
spawn_locations,
|
||||
&surface,
|
||||
scene,
|
||||
world,
|
||||
)?;
|
||||
|
||||
let mut spawns = HashMap::new();
|
||||
|
||||
for (coordinate, entity) in spawn_positions.into_iter() {
|
||||
let e = scene.entity(entity)?;
|
||||
let e = world.entity(entity)?;
|
||||
let position = e.get_component::<Location>()?.position();
|
||||
|
||||
spawns.insert(coordinate, (entity, position));
|
||||
}
|
||||
|
||||
let leave_positions = Self::create_entity_alike(
|
||||
asset_manager,
|
||||
&mut asset_manager,
|
||||
entity_manager,
|
||||
leave_locations,
|
||||
&surface,
|
||||
scene,
|
||||
world,
|
||||
)?;
|
||||
|
||||
let npc_spawn_positions = mob_spawn_locations
|
||||
|
@ -882,8 +853,7 @@ impl Map {
|
|||
|
||||
data: RwLock::new(MapData {
|
||||
chunk_handles: Self::create_tiles(
|
||||
engine,
|
||||
scene,
|
||||
world,
|
||||
&textures_info,
|
||||
data_base.width(),
|
||||
data_base.height(),
|
||||
|
@ -925,11 +895,11 @@ impl Map {
|
|||
|
||||
#[inline]
|
||||
fn create_entity_alike(
|
||||
asset_manager: AssetHandler<'_>,
|
||||
asset_manager: &mut AssetHandler<'_>,
|
||||
entity_manager: &mut impl AssetLoader,
|
||||
source: Vec<DBEntityInfo>,
|
||||
surface: &Surface,
|
||||
scene: &mut Scene,
|
||||
world: &mut World,
|
||||
) -> Result<HashMap<Coordinate, Entity>> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
|
@ -949,7 +919,7 @@ impl Map {
|
|||
|
||||
Location::new_and_setup(&mut entity_object)?.update_raw(position, info.rotation);
|
||||
|
||||
let entity = scene.add_entity(entity_object)?;
|
||||
let entity = world.add_entity(entity_object)?;
|
||||
|
||||
map.insert(
|
||||
Coordinate {
|
||||
|
@ -962,14 +932,12 @@ impl Map {
|
|||
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl engine::prelude::Map for Map {
|
||||
fn name(&self) -> &str {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn check_walkability(
|
||||
pub fn check_walkability(
|
||||
&self,
|
||||
position: Vector2<f32>,
|
||||
direction: Vector2<f32>,
|
||||
|
@ -1016,45 +984,45 @@ impl engine::prelude::Map for Map {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn get_height(&self, x: f32, y: f32) -> Result<f32> {
|
||||
pub fn get_height(&self, x: f32, y: f32) -> Result<f32> {
|
||||
Ok(self.data.read().unwrap().get_height(x, y))
|
||||
}
|
||||
|
||||
fn spawn_positions(&self) -> Result<Vec<Vector3<f32>>> {
|
||||
pub fn spawn_positions(&self) -> Result<Vec<Vector3<f32>>> {
|
||||
Ok(self.data.read().unwrap().spawn_locations())
|
||||
}
|
||||
|
||||
fn leave_markers(&self) -> Result<Vec<Entity>> {
|
||||
pub fn leave_markers(&self) -> Result<Vec<Entity>> {
|
||||
Ok(self.data.read().unwrap().leave_markers())
|
||||
}
|
||||
|
||||
fn disable(&self, scene: &mut Scene) -> Result<()> {
|
||||
pub fn disable(&self, world: &mut World) -> Result<()> {
|
||||
let data = self.data.read().unwrap();
|
||||
|
||||
// clear tiles
|
||||
for chunk in data.chunk_handles.values() {
|
||||
scene.remove_entity(*chunk)?;
|
||||
world.remove_entity(*chunk)?;
|
||||
}
|
||||
|
||||
// clear entities
|
||||
for entity in data.entities.values() {
|
||||
scene.remove_entity(*entity)?;
|
||||
world.remove_entity(*entity)?;
|
||||
}
|
||||
|
||||
// clear spawns
|
||||
for (spawn, _) in data.spawn_locations.values() {
|
||||
scene.remove_entity(*spawn)?;
|
||||
world.remove_entity(*spawn)?;
|
||||
}
|
||||
|
||||
// clear exits
|
||||
for leave in data.leave_locations.values() {
|
||||
scene.remove_entity(*leave)?;
|
||||
world.remove_entity(*leave)?;
|
||||
}
|
||||
|
||||
// clear npc spawns
|
||||
for (_, marker) in data.npc_spawn_areas.iter() {
|
||||
if let Some(marker) = marker {
|
||||
marker.remove(scene)?;
|
||||
marker.remove(world)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use engine::prelude::*;
|
|||
use super::{async_db::AsyncDBAccess, map::MapObjectType, map_db::EntityDBType, surface::Surface};
|
||||
|
||||
use anyhow::Result;
|
||||
use cgmath::{vec3, Rad};
|
||||
use cgmath::{Rad, vec3};
|
||||
use cgmath::{Vector2, Vector3};
|
||||
|
||||
// std
|
||||
|
@ -53,14 +53,14 @@ pub struct SpawnMarkerEntities {
|
|||
|
||||
impl SpawnMarkerEntities {
|
||||
pub fn new(
|
||||
engine: &Engine,
|
||||
world: &mut World,
|
||||
entity_manager: &mut impl AssetLoader,
|
||||
flag_name: &str,
|
||||
area_name: &str,
|
||||
) -> Result<Self> {
|
||||
let assets = engine.assets();
|
||||
let mut assets = AssetHandler::create(world);
|
||||
|
||||
let mut flag = entity_manager.load_entity(assets, flag_name)?;
|
||||
let mut flag = entity_manager.load_entity(&mut assets, flag_name)?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -69,7 +69,7 @@ impl SpawnMarkerEntities {
|
|||
|
||||
Location::new_and_setup(&mut flag)?;
|
||||
|
||||
let mut area = entity_manager.load_entity(assets, area_name)?;
|
||||
let mut area = entity_manager.load_entity(&mut assets, area_name)?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -81,8 +81,8 @@ impl SpawnMarkerEntities {
|
|||
Ok(Self { flag, area })
|
||||
}
|
||||
|
||||
fn clone_entity(entity: &EntityObject, engine: &Engine) -> Result<EntityObject> {
|
||||
let mut flag = entity.clone_without_components(engine);
|
||||
fn clone_entity(entity: &EntityObject, world: &mut World) -> Result<EntityObject> {
|
||||
let mut flag = world.clone_without_components(entity);
|
||||
flag.clone_component_from::<Draw>(entity)?;
|
||||
flag.clone_component_from::<Location>(entity)?;
|
||||
flag.clone_component_from::<BoundingBox>(entity)?;
|
||||
|
@ -92,9 +92,9 @@ impl SpawnMarkerEntities {
|
|||
Ok(flag)
|
||||
}
|
||||
|
||||
fn clone(&self, engine: &Engine) -> Result<Self> {
|
||||
let flag = Self::clone_entity(&self.flag, engine)?;
|
||||
let area = Self::clone_entity(&self.area, engine)?;
|
||||
fn clone(&self, world: &mut World) -> Result<Self> {
|
||||
let flag = Self::clone_entity(&self.flag, world)?;
|
||||
let area = Self::clone_entity(&self.area, world)?;
|
||||
|
||||
Ok(Self { flag, area })
|
||||
}
|
||||
|
@ -118,9 +118,9 @@ impl SpawnMarkerEntities {
|
|||
.set_scale(vec3(radius, radius, 1.0));
|
||||
}
|
||||
|
||||
pub fn add(self, scene: &mut impl SceneEntities) -> Result<SpawnMarker> {
|
||||
let flag = scene.add_entity(self.flag)?;
|
||||
let area = scene.add_entity(self.area)?;
|
||||
pub fn add(self, world: &mut World) -> Result<SpawnMarker> {
|
||||
let flag = world.add_entity(self.flag)?;
|
||||
let area = world.add_entity(self.area)?;
|
||||
|
||||
Ok(SpawnMarker { flag, area })
|
||||
}
|
||||
|
@ -132,8 +132,8 @@ pub struct SpawnMarker {
|
|||
}
|
||||
|
||||
impl SpawnMarker {
|
||||
fn set_radius(&self, scene_contents: &mut impl SceneEntities, radius: f32) -> Result<()> {
|
||||
let flag_object = scene_contents.entity_mut(self.area)?;
|
||||
fn set_radius(&self, world: &mut World, radius: f32) -> Result<()> {
|
||||
let flag_object = world.entity_mut(self.area)?;
|
||||
|
||||
flag_object
|
||||
.get_component_mut::<Location>()?
|
||||
|
@ -193,9 +193,9 @@ impl AlterEntities for HashMap<Coordinate, (Entity, Vector3<f32>)> {
|
|||
}
|
||||
|
||||
impl SpawnMarker {
|
||||
pub fn remove(&self, scene: &mut impl SceneEntities) -> Result<()> {
|
||||
scene.remove_entity(self.flag)?;
|
||||
scene.remove_entity(self.area)?;
|
||||
pub fn remove(&self, world: &mut World) -> Result<()> {
|
||||
world.remove_entity(self.flag)?;
|
||||
world.remove_entity(self.area)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -222,10 +222,10 @@ pub struct MapBossSpawnInfo {
|
|||
impl MapBossSpawnInfo {
|
||||
fn setup_flag(
|
||||
marker: &SpawnMarkerEntities,
|
||||
engine: &Engine,
|
||||
world: &mut World,
|
||||
position: Vector3<f32>,
|
||||
) -> Result<EntityObject> {
|
||||
let mut flag = SpawnMarkerEntities::clone_entity(&marker.flag, engine)?;
|
||||
let mut flag = SpawnMarkerEntities::clone_entity(&marker.flag, world)?;
|
||||
|
||||
let location = flag.get_component_mut::<Location>()?;
|
||||
let scale = location.scale();
|
||||
|
@ -472,9 +472,9 @@ impl MapData {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_height_at(&mut self, x: u32, y: u32, scene: &mut Scene) -> Result<()> {
|
||||
pub fn check_height_at(&mut self, x: u32, y: u32, world: &mut World) -> Result<()> {
|
||||
self.execute_on_entity(x, y, |entity| {
|
||||
let entity_object = scene.entity_mut(entity)?;
|
||||
let entity_object = world.entity_mut(entity)?;
|
||||
|
||||
let location = entity_object.get_component_mut::<Location>()?;
|
||||
|
||||
|
@ -493,7 +493,7 @@ impl MapData {
|
|||
|
||||
pub fn set_spawn_location(
|
||||
&mut self,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
entity_opt: Option<(String, EntityObject)>,
|
||||
position: cgmath::Vector2<f32>,
|
||||
rotation: impl Into<Rad<f32>>,
|
||||
|
@ -508,7 +508,7 @@ impl MapData {
|
|||
(self.width, self.height),
|
||||
&self.surface,
|
||||
EntityDBType::spawn_location(),
|
||||
scene,
|
||||
world,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -539,7 +539,7 @@ impl MapData {
|
|||
pub fn change_npc_spawn(
|
||||
&mut self,
|
||||
async_db: &AsyncDBAccess,
|
||||
scene_contents: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
position: Vector2<f32>,
|
||||
npc_spawn_parameter: NPCSpawnParameter,
|
||||
) -> Result<()> {
|
||||
|
@ -580,10 +580,7 @@ impl MapData {
|
|||
NPCSpawnParameter::Radius(radius) => {
|
||||
spawn_info.radius = radius;
|
||||
|
||||
marker
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_radius(scene_contents, radius)?;
|
||||
marker.as_ref().unwrap().set_radius(world, radius)?;
|
||||
|
||||
async_db.add(move |sql| sql.update_npc_spawn_radius(&coordinate, radius))?;
|
||||
}
|
||||
|
@ -593,12 +590,12 @@ impl MapData {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn disable_spawns(&mut self, scene: &mut impl SceneEntities) -> Result<()> {
|
||||
pub fn disable_spawns(&mut self, world: &mut World) -> Result<()> {
|
||||
if self.show_spawn_locations {
|
||||
self.show_spawn_locations = false;
|
||||
|
||||
for (spawn_entity, _) in self.spawn_locations.values() {
|
||||
scene.remove_entity(*spawn_entity)?;
|
||||
world.remove_entity(*spawn_entity)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -621,7 +618,7 @@ impl MapData {
|
|||
|
||||
pub fn set_leave_location(
|
||||
&mut self,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
entity_opt: Option<(String, EntityObject)>,
|
||||
position: cgmath::Vector2<f32>,
|
||||
rotation: impl Into<Rad<f32>>,
|
||||
|
@ -636,15 +633,11 @@ impl MapData {
|
|||
(self.width, self.height),
|
||||
&self.surface,
|
||||
EntityDBType::leave_location(),
|
||||
scene,
|
||||
world,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn toggle_npc_spawns(
|
||||
&mut self,
|
||||
engine: &Engine,
|
||||
scene: &mut impl SceneEntities,
|
||||
) -> Result<()> {
|
||||
pub fn toggle_npc_spawns(&mut self, world: &mut World) -> Result<()> {
|
||||
self.show_npc_spawns = !self.show_npc_spawns;
|
||||
|
||||
for (spawn_info, marker) in self.npc_spawn_areas.iter_mut() {
|
||||
|
@ -660,13 +653,13 @@ impl MapData {
|
|||
Some(marker) => Some(marker),
|
||||
None => match &self.npc_spawn_marker {
|
||||
Some(global_marker) => {
|
||||
let mut new_marker = global_marker.clone(engine)?;
|
||||
let mut new_marker = global_marker.clone(world)?;
|
||||
|
||||
// set position
|
||||
new_marker.set_position(spawn_info.position.extend(height));
|
||||
new_marker.set_radius(spawn_info.radius);
|
||||
|
||||
*marker = Some(new_marker.add(scene)?);
|
||||
*marker = Some(new_marker.add(world)?);
|
||||
|
||||
marker.as_mut()
|
||||
}
|
||||
|
@ -680,7 +673,7 @@ impl MapData {
|
|||
if self.show_npc_spawns {
|
||||
// marker.add(scene)?;
|
||||
} else {
|
||||
marker.remove(scene)?;
|
||||
marker.remove(world)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -700,11 +693,11 @@ impl MapData {
|
|||
Some(global_marker) => {
|
||||
let flag = MapBossSpawnInfo::setup_flag(
|
||||
global_marker,
|
||||
engine,
|
||||
world,
|
||||
spawn_info.position.extend(height),
|
||||
)?;
|
||||
|
||||
*flag_marker = Some(scene.add_entity(flag)?);
|
||||
*flag_marker = Some(world.add_entity(flag)?);
|
||||
|
||||
*flag_marker
|
||||
}
|
||||
|
@ -718,7 +711,7 @@ impl MapData {
|
|||
if self.show_npc_spawns {
|
||||
// marker.add(scene)?;
|
||||
} else {
|
||||
scene.remove_entity(marker)?;
|
||||
world.remove_entity(marker)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -728,8 +721,7 @@ impl MapData {
|
|||
|
||||
pub fn set_npc_spawn(
|
||||
&mut self,
|
||||
engine: &Engine,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
position: cgmath::Vector2<f32>,
|
||||
async_db: &AsyncDBAccess,
|
||||
radius: f32,
|
||||
|
@ -749,13 +741,13 @@ impl MapData {
|
|||
|
||||
let marker = match self.npc_spawn_marker.as_ref() {
|
||||
Some(managed_entity) => {
|
||||
let mut marker = managed_entity.clone(engine)?;
|
||||
let mut marker = managed_entity.clone(world)?;
|
||||
|
||||
// set position
|
||||
marker.set_position(position.extend(self.get_height(position.x, position.y)));
|
||||
marker.set_radius(radius);
|
||||
|
||||
Some(marker.add(scene)?)
|
||||
Some(marker.add(world)?)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
@ -781,7 +773,7 @@ impl MapData {
|
|||
|
||||
pub fn unset_npc_spawn(
|
||||
&mut self,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
position: cgmath::Vector2<f32>,
|
||||
async_db: &AsyncDBAccess,
|
||||
) -> Result<()> {
|
||||
|
@ -797,7 +789,7 @@ impl MapData {
|
|||
async_db.add(move |sql| sql.remove_npc_spawn((coordinate.x, coordinate.y)))?;
|
||||
|
||||
if let Some(marker) = marker {
|
||||
marker.remove(scene)?;
|
||||
marker.remove(world)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -806,8 +798,7 @@ impl MapData {
|
|||
|
||||
pub fn set_boss_spawn(
|
||||
&mut self,
|
||||
engine: &Engine,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
position: cgmath::Vector2<f32>,
|
||||
async_db: &AsyncDBAccess,
|
||||
) -> Result<()> {
|
||||
|
@ -826,11 +817,11 @@ impl MapData {
|
|||
Some(managed_entity) => {
|
||||
let flag = MapBossSpawnInfo::setup_flag(
|
||||
managed_entity,
|
||||
engine,
|
||||
world,
|
||||
position.extend(self.get_height(position.x, position.y)),
|
||||
)?;
|
||||
|
||||
Some(scene.add_entity(flag)?)
|
||||
Some(world.add_entity(flag)?)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
@ -851,7 +842,7 @@ impl MapData {
|
|||
|
||||
pub fn unset_boss_spawn(
|
||||
&mut self,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
position: cgmath::Vector2<f32>,
|
||||
async_db: &AsyncDBAccess,
|
||||
) -> Result<()> {
|
||||
|
@ -867,7 +858,7 @@ impl MapData {
|
|||
async_db.add(move |sql| sql.remove_boss_spawn((coordinate.x, coordinate.y)))?;
|
||||
|
||||
if let Some(marker) = marker {
|
||||
scene.remove_entity(marker)?;
|
||||
world.remove_entity(marker)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -883,7 +874,7 @@ impl MapData {
|
|||
(width, height): (u32, u32),
|
||||
surface: &Surface,
|
||||
table_name: EntityDBType,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
) -> Result<()> {
|
||||
let coordinate = Coordinate {
|
||||
x: position.x as u32,
|
||||
|
@ -927,7 +918,7 @@ impl MapData {
|
|||
.unwrap()
|
||||
.update_raw(position_3d, rad_rotation);
|
||||
|
||||
let handle = scene.add_entity(entity)?;
|
||||
let handle = world.add_entity(entity)?;
|
||||
|
||||
entities.insert(coordinate, handle, position_3d);
|
||||
}
|
||||
|
@ -938,7 +929,7 @@ impl MapData {
|
|||
sql.remove_entity(table_name, (coordinate.x, coordinate.y))
|
||||
})?;
|
||||
|
||||
scene.remove_entity(entity)?;
|
||||
world.remove_entity(entity)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -950,7 +941,7 @@ impl MapData {
|
|||
|
||||
pub fn set_entity(
|
||||
&mut self,
|
||||
scene: &mut impl SceneEntities,
|
||||
world: &mut World,
|
||||
entity_opt: Option<(String, EntityObject)>,
|
||||
position: cgmath::Vector2<f32>,
|
||||
rotation: impl Into<Rad<f32>>,
|
||||
|
@ -965,7 +956,7 @@ impl MapData {
|
|||
(self.width, self.height),
|
||||
&self.surface,
|
||||
EntityDBType::entity(),
|
||||
scene,
|
||||
world,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1122,7 +1113,7 @@ impl MapData {
|
|||
return Err(anyhow::Error::msg(format!(
|
||||
"Index ({}) in textures_info (MapData) not found",
|
||||
remove_index
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1132,7 +1123,7 @@ impl MapData {
|
|||
return Err(anyhow::Error::msg(format!(
|
||||
"Could not remove key ({}) from textures_index",
|
||||
info.name
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ impl CharacterStatus {
|
|||
}
|
||||
|
||||
impl EntityComponent for CharacterStatus {
|
||||
fn enable(&mut self, _scene: &mut Scene) -> Result<()> {
|
||||
fn enable(&mut self, _world: &mut World) -> Result<()> {
|
||||
self.last_tick = None;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
crafting_materials::CraftingMaterials, inventory::Inventory, item_slots::ItemSlotContainer,
|
||||
level::Level, statistics::Statistics,
|
||||
},
|
||||
items::{ability_book::Ability, ItemAffix, ItemSystem, Rarities},
|
||||
items::{ItemAffix, ItemSystem, Rarities, ability_book::Ability},
|
||||
};
|
||||
|
||||
use std::env::var;
|
||||
|
@ -361,21 +361,19 @@ create_settings_container!(
|
|||
impl SaveGame {
|
||||
pub fn to_entity_object<A: Ability + 'static>(
|
||||
self,
|
||||
engine: &Engine,
|
||||
world: &mut World,
|
||||
) -> Result<(Entity, String)> {
|
||||
let scene = engine.scene_mut();
|
||||
|
||||
let experience_settings = scene.resources.get::<ExperienceSettings>();
|
||||
let attribute_settings = scene.resources.get::<AttributeSettings>();
|
||||
let item_settings = scene.resources.get::<ItemSettings>();
|
||||
let item_system = scene.resources.get::<ItemSystem<A>>();
|
||||
|
||||
let mut entity_object = engine.assets().empty_entity();
|
||||
let mut entity_object = AssetHandler::create(world).empty_entity();
|
||||
|
||||
entity_object.insert_component(Draw::new(Vec::new()));
|
||||
entity_object.insert_component(Audio::new(engine.context(), None)?);
|
||||
entity_object.insert_component(Audio::new(world.resources.get_mut::<Context>(), None)?);
|
||||
Location::new_and_setup(&mut entity_object)?;
|
||||
|
||||
let experience_settings = world.resources.get::<ExperienceSettings>();
|
||||
let attribute_settings = world.resources.get::<AttributeSettings>();
|
||||
let item_settings = world.resources.get::<ItemSettings>();
|
||||
let item_system = world.resources.get::<ItemSystem<A>>();
|
||||
|
||||
let level = Level::load(self.general.level, self.general.exp, experience_settings);
|
||||
let mut attributes = Attributes::load(
|
||||
self.general.strength,
|
||||
|
@ -399,6 +397,6 @@ impl SaveGame {
|
|||
entity_object.insert_component(statistics);
|
||||
entity_object.insert_component(current_status);
|
||||
|
||||
Ok((scene.add_entity(entity_object)?, self.general.name))
|
||||
Ok((world.add_entity(entity_object)?, self.general.name))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ use crate::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
ability_addon::{AbilityAddon, AbilityAddonCollection, AbilityAddonTypes},
|
||||
ItemSystem, Rarities, Tooltip,
|
||||
ability_addon::{AbilityAddon, AbilityAddonCollection, AbilityAddonTypes},
|
||||
};
|
||||
|
||||
pub trait Ability: Send + Sync + Clone + Default {
|
||||
fn create(engine: &Engine, asset_path: impl Into<AssetPath>) -> Result<Self>;
|
||||
fn create(context: &Context, asset_path: impl Into<AssetPath>) -> Result<Self>;
|
||||
|
||||
fn name(&self) -> &str;
|
||||
fn icon_path(&self) -> &AssetPath;
|
||||
|
|
|
@ -5,7 +5,7 @@ use engine::prelude::*;
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use image::{imageops::FilterType, DynamicImage, ImageBuffer, Pixel, Rgba, RgbaImage};
|
||||
use image::{DynamicImage, ImageBuffer, Pixel, Rgba, RgbaImage, imageops::FilterType};
|
||||
|
||||
use crate::components::attributes::{Attribute, Attributes};
|
||||
use crate::components::inventory::Storable;
|
||||
|
@ -53,7 +53,7 @@ pub struct ItemSystem<A: Ability> {
|
|||
|
||||
impl<A: Ability> ItemSystem<A> {
|
||||
pub fn new(
|
||||
engine: &Arc<Engine>,
|
||||
context: &Context,
|
||||
item_settings: &ItemSettings,
|
||||
ability_settings: &AbilitySettings,
|
||||
attribute_settings: &AttributeSettings,
|
||||
|
@ -64,7 +64,7 @@ impl<A: Ability> ItemSystem<A> {
|
|||
|
||||
let abilities = search_dir_recursively(&ability_directory.full_path(), ".abil")?
|
||||
.into_iter()
|
||||
.map(|path| A::create(engine, path))
|
||||
.map(|path| A::create(context, path))
|
||||
.collect::<Result<Vec<A>>>()?;
|
||||
|
||||
let (
|
||||
|
@ -149,8 +149,8 @@ impl<A: Ability> ItemSystem<A> {
|
|||
item_icon_combinations.insert(
|
||||
(*rarity, *slot),
|
||||
Self::create_icon(
|
||||
engine.device(),
|
||||
engine.queue(),
|
||||
context.device(),
|
||||
context.queue(),
|
||||
width,
|
||||
height,
|
||||
final_image.into_raw(),
|
||||
|
@ -177,8 +177,8 @@ impl<A: Ability> ItemSystem<A> {
|
|||
ability_icon_combinations.insert(
|
||||
(*rarity, name.clone()),
|
||||
Self::create_icon(
|
||||
engine.device(),
|
||||
engine.queue(),
|
||||
context.device(),
|
||||
context.queue(),
|
||||
width,
|
||||
height,
|
||||
final_image.into_raw(),
|
||||
|
@ -215,8 +215,8 @@ impl<A: Ability> ItemSystem<A> {
|
|||
addon_icon_combinations.insert(
|
||||
(*rarity, *addon_type),
|
||||
Self::create_icon(
|
||||
engine.device(),
|
||||
engine.queue(),
|
||||
context.device(),
|
||||
context.queue(),
|
||||
width,
|
||||
height,
|
||||
final_image.into_raw(),
|
||||
|
@ -247,8 +247,8 @@ impl<A: Ability> ItemSystem<A> {
|
|||
jewel_icon_combinations.insert(
|
||||
(*rarity, *level + 1, *attribute),
|
||||
Self::create_icon(
|
||||
engine.device(),
|
||||
engine.queue(),
|
||||
context.device(),
|
||||
context.queue(),
|
||||
width,
|
||||
height,
|
||||
final_image.into_raw(),
|
||||
|
|
Loading…
Reference in a new issue