498 lines
16 KiB
Rust
498 lines
16 KiB
Rust
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
|
|
}
|
|
}
|