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 for HeroCreateType { fn from(save_game: SaveGame) -> Self { Self::SaveGame(save_game) } } impl From 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, 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, ) -> 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::()? .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) { 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::()?; let draw = multi_mut.get::()?; let animation_info = multi_mut.get::()?; let abilities = multi_mut.get::()?; let character_status = multi_mut.get::()?; let location = multi_mut.get::()?; 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::()?; let attributes = entity.get_component::()?; let items = entity.get_component::()?; let inventory = entity.get_component::()?; let abilities = entity.get_component::()?; let crafting_materials = entity.get_component::()?; 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 { let mut multi_mut = entity_object.multi_mut(); let draw = multi_mut.get::().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::()?)?; Ok(items) } fn inventory( hero_create_type: &HeroCreateType, item_system: &Arc, ) -> Result { 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 { 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::() .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::().is_ok() { my_object .get_component_mut::()? .set_direction(vec2(0.0, 0.0)); } if collider_object.get_component::().is_ok() || collider_object.get_component::().is_ok() { return Ok(CollisionEventType::Ignore); } } } Ok(CollisionEventType::Block) }); let movement = { let audio = entity_object.get_component_mut::