From 6648926f08fc26705c6d42fc6b6160c1bb807de7 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Fri, 28 Feb 2025 08:39:15 +0100 Subject: [PATCH] Fix rpg components & move core --- Cargo.toml | 6 +- gavania-core/.vscode/settings.json | 7 - gavania-core/Cargo.toml | 29 - gavania-core/src/game/configloader/mobs.rs | 38 -- gavania-core/src/game/configloader/mod.rs | 2 - .../src/game/content/abilities/mod.rs | 139 ---- .../content/abilities/on_hit_particles.rs | 67 -- .../game/content/abilities/particle_spawn.rs | 58 -- .../src/game/content/abilities/prelude.rs | 4 - .../src/game/content/abilities/projectile.rs | 371 ----------- .../src/game/content/abilities/selfcast.rs | 64 -- .../src/game/content/abilities/slide.rs | 182 ------ gavania-core/src/game/content/ability_type.rs | 38 -- .../components/ability_location_info.rs | 98 --- .../game/content/components/abilityloader.rs | 261 -------- .../src/game/content/components/ai/mod.rs | 3 - .../src/game/content/components/ai/simple.rs | 244 ------- .../src/game/content/components/aoe.rs | 384 ----------- .../src/game/content/components/aoe_arc.rs | 224 ------- .../game/content/components/damage_number.rs | 116 ---- .../game/content/components/entity_faction.rs | 91 --- .../src/game/content/components/ghost.rs | 78 --- .../components/health_bar/boss_health_bar.rs | 89 --- .../components/health_bar/health_bar_base.rs | 23 - .../game/content/components/health_bar/mod.rs | 8 - .../components/health_bar/npc_health_bar.rs | 98 --- .../components/health_bar/user_health_bar.rs | 129 ---- .../src/game/content/components/loot_stash.rs | 265 -------- .../src/game/content/components/mod.rs | 22 - .../src/game/content/components/movement.rs | 133 ---- .../src/game/content/components/npc_name.rs | 26 - gavania-core/src/game/content/lightning.rs | 162 ----- gavania-core/src/game/content/mod.rs | 7 - gavania-core/src/game/content/objects/hero.rs | 498 -------------- gavania-core/src/game/content/objects/mod.rs | 2 - gavania-core/src/game/content/objects/npc.rs | 408 ------------ gavania-core/src/game/content/prelude.rs | 6 - gavania-core/src/game/game.rs | 610 ------------------ gavania-core/src/game/handle.rs | 76 --- gavania-core/src/game/mod.rs | 5 - gavania-core/src/lib.rs | 14 - gavania-core/src/loader/mod.rs | 1 - gavania-core/src/loader/settings.rs | 325 ---------- gavania-core/src/osbuild.rs | 36 -- .../src/components/character_status.rs | 2 +- rpg_components/src/config/save_game.rs | 22 +- rpg_components/src/items/ability_book.rs | 4 +- rpg_components/src/items/item_system.rs | 22 +- 48 files changed, 28 insertions(+), 5469 deletions(-) delete mode 100644 gavania-core/.vscode/settings.json delete mode 100644 gavania-core/Cargo.toml delete mode 100644 gavania-core/src/game/configloader/mobs.rs delete mode 100644 gavania-core/src/game/configloader/mod.rs delete mode 100644 gavania-core/src/game/content/abilities/mod.rs delete mode 100644 gavania-core/src/game/content/abilities/on_hit_particles.rs delete mode 100644 gavania-core/src/game/content/abilities/particle_spawn.rs delete mode 100644 gavania-core/src/game/content/abilities/prelude.rs delete mode 100644 gavania-core/src/game/content/abilities/projectile.rs delete mode 100644 gavania-core/src/game/content/abilities/selfcast.rs delete mode 100644 gavania-core/src/game/content/abilities/slide.rs delete mode 100644 gavania-core/src/game/content/ability_type.rs delete mode 100644 gavania-core/src/game/content/components/ability_location_info.rs delete mode 100644 gavania-core/src/game/content/components/abilityloader.rs delete mode 100644 gavania-core/src/game/content/components/ai/mod.rs delete mode 100644 gavania-core/src/game/content/components/ai/simple.rs delete mode 100644 gavania-core/src/game/content/components/aoe.rs delete mode 100644 gavania-core/src/game/content/components/aoe_arc.rs delete mode 100644 gavania-core/src/game/content/components/damage_number.rs delete mode 100644 gavania-core/src/game/content/components/entity_faction.rs delete mode 100644 gavania-core/src/game/content/components/ghost.rs delete mode 100644 gavania-core/src/game/content/components/health_bar/boss_health_bar.rs delete mode 100644 gavania-core/src/game/content/components/health_bar/health_bar_base.rs delete mode 100644 gavania-core/src/game/content/components/health_bar/mod.rs delete mode 100644 gavania-core/src/game/content/components/health_bar/npc_health_bar.rs delete mode 100644 gavania-core/src/game/content/components/health_bar/user_health_bar.rs delete mode 100644 gavania-core/src/game/content/components/loot_stash.rs delete mode 100644 gavania-core/src/game/content/components/mod.rs delete mode 100644 gavania-core/src/game/content/components/movement.rs delete mode 100644 gavania-core/src/game/content/components/npc_name.rs delete mode 100644 gavania-core/src/game/content/lightning.rs delete mode 100644 gavania-core/src/game/content/mod.rs delete mode 100644 gavania-core/src/game/content/objects/hero.rs delete mode 100644 gavania-core/src/game/content/objects/mod.rs delete mode 100644 gavania-core/src/game/content/objects/npc.rs delete mode 100644 gavania-core/src/game/content/prelude.rs delete mode 100644 gavania-core/src/game/game.rs delete mode 100644 gavania-core/src/game/handle.rs delete mode 100644 gavania-core/src/game/mod.rs delete mode 100644 gavania-core/src/lib.rs delete mode 100644 gavania-core/src/loader/mod.rs delete mode 100644 gavania-core/src/loader/settings.rs delete mode 100644 gavania-core/src/osbuild.rs diff --git a/Cargo.toml b/Cargo.toml index 502d710..6ca7b98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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.3.1" } diff --git a/gavania-core/.vscode/settings.json b/gavania-core/.vscode/settings.json deleted file mode 100644 index 124e279..0000000 --- a/gavania-core/.vscode/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "workbench.colorCustomizations": { - "activityBar.background": "#4F1E1C", - "titleBar.activeBackground": "#6F2A27", - "titleBar.activeForeground": "#FEFCFC" - } -} \ No newline at end of file diff --git a/gavania-core/Cargo.toml b/gavania-core/Cargo.toml deleted file mode 100644 index bbd35df..0000000 --- a/gavania-core/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "gavania-core" -version = "0.1.0" -authors = ["hodasemi "] -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 = [] diff --git a/gavania-core/src/game/configloader/mobs.rs b/gavania-core/src/game/configloader/mobs.rs deleted file mode 100644 index 3eb4bfb..0000000 --- a/gavania-core/src/game/configloader/mobs.rs +++ /dev/null @@ -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, - } -); diff --git a/gavania-core/src/game/configloader/mod.rs b/gavania-core/src/game/configloader/mod.rs deleted file mode 100644 index b206b33..0000000 --- a/gavania-core/src/game/configloader/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod mobs; -pub use mobs::*; diff --git a/gavania-core/src/game/content/abilities/mod.rs b/gavania-core/src/game/content/abilities/mod.rs deleted file mode 100644 index 95603c2..0000000 --- a/gavania-core/src/game/content/abilities/mod.rs +++ /dev/null @@ -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::() { - if collider_status.is_dead() { - let collider_level = collider_components.get::()?; - let collider_location = collider_components.get::()?; - - if collider_components.get::().is_ok() { - let npc_movement = collider_components.get::()?; - 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::().is_ok() { - let npc_hitbox = collider_components.get::()?; - - 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::().is_ok() { - NPCType::Normal - } else if collider_components.get::().is_ok() { - NPCType::Elite - } else if collider_components.get::().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::() { - if let Ok(collider_stats) = collider_multi_mut.get::() { - if let Ok(collider_level) = collider_multi_mut.get::() { - let resistance = collider_stats.calculate_resistance(damage_type); - - if resistance < base_damage { - let damage = base_damage - resistance; - let owner_status = owner_components.get::()?; - - collider_current_status.apply_damage(damage as f32); - scene.write_event(DamageEvent { - damage, - damage_type, - position: collider_multi_mut.get::()?.position(), - }); - - if collider_current_status.is_dead() { - if owner_components.get::().is_ok() { - if !owner_status.is_dead() { - let level = owner_components.get::()?; - - if level.add_experience( - collider_level.level(), - if collider_multi_mut.get::().is_ok() { - NPCType::Normal - } else if collider_multi_mut.get::().is_ok() { - NPCType::Elite - } else if collider_multi_mut.get::().is_ok() { - NPCType::Boss - } else { - unreachable!("missing component!"); - }, - )? { - scene.write_event(LevelUpEvent); - } - } - } - } - } - } - } - } - - Ok(()) -} diff --git a/gavania-core/src/game/content/abilities/on_hit_particles.rs b/gavania-core/src/game/content/abilities/on_hit_particles.rs deleted file mode 100644 index bea46c9..0000000 --- a/gavania-core/src/game/content/abilities/on_hit_particles.rs +++ /dev/null @@ -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, - pub offset: Vector3, - - pub arc_info: Option, -} - -impl OnHitParticles { - pub fn setup(game_handle: &GameHandle, scene: &mut Scene) { - scene.register_event::(); - - 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(()) - } -} diff --git a/gavania-core/src/game/content/abilities/particle_spawn.rs b/gavania-core/src/game/content/abilities/particle_spawn.rs deleted file mode 100644 index 5fea258..0000000 --- a/gavania-core/src/game/content/abilities/particle_spawn.rs +++ /dev/null @@ -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::(); - - 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::()?; - - match particle_spawn.executor { - ParticleSpawnExecutor::AoE => { - let aoe = multi_mut.get::()?; - - 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) - } - }); - } -} diff --git a/gavania-core/src/game/content/abilities/prelude.rs b/gavania-core/src/game/content/abilities/prelude.rs deleted file mode 100644 index f79ed74..0000000 --- a/gavania-core/src/game/content/abilities/prelude.rs +++ /dev/null @@ -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; diff --git a/gavania-core/src/game/content/abilities/projectile.rs b/gavania-core/src/game/content/abilities/projectile.rs deleted file mode 100644 index 4d72c52..0000000 --- a/gavania-core/src/game/content/abilities/projectile.rs +++ /dev/null @@ -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; 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; 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 { - // 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::()?; - - // 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::().unwrap().position(); - let movement = projectile_components.get::().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::()?; - - 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::()?; - - 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::().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 { - 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, - 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] = 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::()?; - let owner_location = owner_components.get::()?; - - 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::()?; - - // 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::