269 lines
7.1 KiB
Rust
269 lines
7.1 KiB
Rust
use anyhow::Result;
|
|
use assetpath::AssetPath;
|
|
use engine::prelude::*;
|
|
|
|
use cgmath::Vector3;
|
|
use std::path::Path;
|
|
|
|
use crate::{
|
|
ability_type::AbilityType,
|
|
damage_type::DamageType,
|
|
items::{ability_addon::AbilityAddonCollection, ability_book::Ability},
|
|
};
|
|
|
|
use super::statistics::Statistics;
|
|
|
|
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"
|
|
}
|
|
}
|