engine/gavania-core/src/game/content/components/abilityloader.rs

266 lines
7.1 KiB
Rust
Raw Normal View History

2024-08-23 11:22:09 +00:00
use anyhow::Result;
use assetpath::AssetPath;
use engine::prelude::*;
use crate::Game;
use super::super::prelude::*;
use cgmath::Vector3;
use std::{path::Path, sync::Arc};
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) -> Result<Arc<dyn Ability>> {
match self.settings.parameter.ability_type {
AbilityType::Projectile => Ok(Arc::new(Projectile::new(self)?)),
AbilityType::SelfCast => Ok(Arc::new(SelfCast::new(self)?)),
}
}
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,
game: &Game,
position: Vector3<f32>,
offset: Vector3<f32>,
particle_system_vulkan_objects: &ParticleSystemVulkanObjects,
) -> Result<Option<EntityObject>> {
Ok(if self.settings.parameter.on_hit_particles.is_empty() {
None
} else {
let file_name = game.build_data_path(&self.settings.parameter.on_hit_particles);
let info = ParticleSystemInfo::load(file_name)?;
Some(Self::create_on_hit_particles(
info,
&self.settings.parameter.on_collision_sound,
game,
position,
offset,
particle_system_vulkan_objects,
)?)
})
}
pub fn create_on_hit_particles(
info: ParticleSystemInfo,
on_collision_sound: &str,
game: &Game,
position: Vector3<f32>,
offset: Vector3<f32>,
particle_system_vulkan_objects: &ParticleSystemVulkanObjects,
) -> Result<EntityObject> {
let mut particle_entity = game.engine().assets().empty_entity();
{
let mut draw = Draw::new(Vec::new());
let particles = ParticleSystem::new_with_vk_objects(
info,
&game.engine(),
particle_system_vulkan_objects,
&mut draw,
)?;
if !on_collision_sound.is_empty() {
let mut audio = Audio::new(game.engine().context(), None)?;
audio.set_sound(game.build_data_path(on_collision_sound), SOUND_CREATE_KEY)?;
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"
}
}