diff --git a/src/asset/proto.rs b/src/asset/proto.rs index 0ed3346..6caaa17 100644 --- a/src/asset/proto.rs +++ b/src/asset/proto.rs @@ -11,7 +11,7 @@ pub use db::ProtoDb; use super::*; use crate::asset::EntityKind; -use crate::asset::frame::FrameId; +use crate::asset::frame::{FrameId, Idx}; use crate::asset::message::MessageId; use crate::game::script::ScriptPid; use crate::graphics::geometry::hex::TileGrid; @@ -189,8 +189,8 @@ pub struct Armor { pub damage_resistance: EnumMap, pub damage_threshold: EnumMap, pub perk: Option, - pub male_fid: FrameId, - pub female_fid: FrameId, + pub male_fidx: Idx, + pub female_fidx: Idx, } impl Armor { @@ -255,7 +255,7 @@ pub struct RangeInclusive { #[derive(Debug)] pub struct Weapon { pub attack_kinds: EnumMap, - pub animation_code: WeaponKind, + pub kind: WeaponKind, // item_w_damage_min_max pub damage: RangeInclusive, pub damage_kind: DamageKind, diff --git a/src/asset/proto/db.rs b/src/asset/proto/db.rs index 18c166a..72782fb 100644 --- a/src/asset/proto/db.rs +++ b/src/asset/proto/db.rs @@ -220,25 +220,23 @@ impl ProtoDb { fn read_armor(rd: &mut impl Read) -> io::Result { let armor_class = rd.read_i32::()?; let mut damage_resistance = EnumMap::new(); - for d in 0..7 { - let dmg = DamageKind::from_usize(d).unwrap(); + for &dmg in DamageKind::basic() { damage_resistance[dmg] = rd.read_i32::()?; } let mut damage_threshold = EnumMap::new(); - for d in 0..7 { - let dmg = DamageKind::from_usize(d).unwrap(); + for &dmg in DamageKind::basic() { damage_threshold[dmg] = rd.read_i32::()?; } let perk = read_opt_enum(rd, "invalid armor perk")?; - let male_fid = FrameId::read(rd)?; - let female_fid = FrameId::read(rd)?; + let male_fidx = FrameId::read(rd)?.idx(); + let female_fidx = FrameId::read(rd)?.idx(); Ok(Armor { armor_class, damage_resistance, damage_threshold, perk, - male_fid, - female_fid, + male_fidx, + female_fidx, }) } @@ -353,7 +351,7 @@ impl ProtoDb { Ok(Weapon { attack_kinds, - animation_code, + kind: animation_code, damage, damage_kind, max_ranges, diff --git a/src/game/inventory.rs b/src/game/inventory.rs index 5798439..2278d4f 100644 --- a/src/game/inventory.rs +++ b/src/game/inventory.rs @@ -6,7 +6,7 @@ use crate::asset::*; use crate::asset::frame::FrameId; use crate::asset::message::{Messages, MessageId}; use crate::fs::FileSystem; -use crate::game::object::{self, EquipmentSlot, Object, InventoryItem}; +use crate::game::object::{self, EquipmentSlot, Hand, Object, InventoryItem}; use crate::game::rpg::Rpg; use crate::game::ui::action_menu::{self, Action}; use crate::game::ui::inventory_list::{self, InventoryList, Scroll, MouseMode}; @@ -85,8 +85,8 @@ impl Inventory { } fn show(&mut self, rpg: &Rpg, ui: &mut Ui) { - let obj = self.world.borrow().dude_obj().unwrap(); - let internal = Internal::new(self.msgs.take().unwrap(), self.world.clone(), obj, ui); + let owner = self.world.borrow().dude_obj().unwrap(); + let internal = Internal::new(self.msgs.take().unwrap(), self.world.clone(), owner, ui); internal.sync_mouse_mode_to_ui(ui); internal.sync_from_obj(rpg, ui); assert!(self.internal.replace(internal).is_none()); @@ -108,7 +108,7 @@ enum Slot { struct Internal { msgs: Messages, world: WorldRef, - obj: object::Handle, + owner: object::Handle, win: ui::Handle, mouse_mode: MouseMode, list: ui::Handle, @@ -129,7 +129,7 @@ struct Internal { } impl Internal { - fn new(msgs: Messages, world: WorldRef, obj: object::Handle, ui: &mut Ui) -> Self { + fn new(msgs: Messages, world: WorldRef, owner: object::Handle, ui: &mut Ui) -> Self { let win = ui.new_window(Rect::with_size(80, 0, 499, 377), Some(Sprite::new(FrameId::INVENTORY_WINDOW))); ui.set_modal_window(Some(win)); @@ -220,7 +220,7 @@ impl Internal { Self { msgs, world, - obj, + owner, win, mouse_mode: MouseMode::Drag, list, @@ -255,8 +255,8 @@ impl Internal { right_hand.clear(); let world = self.world.borrow(); - let obj = world.objects().get(self.obj); - for item in &obj.inventory.items { + let owner = world.objects().get(self.owner); + for item in &owner.inventory.items { let item_obj = &world.objects().get(item.object); let inv_list_item = Self::make_list_item(item, item_obj); match () { @@ -343,10 +343,10 @@ impl Internal { // display_stats fn update_stats(&self, rpg: &Rpg, ui: &Ui) { let world = self.world.borrow(); - let name = world.object_name(self.obj).unwrap(); - let obj = &world.objects().get(self.obj); + let name = world.object_name(self.owner).unwrap(); + let owner = &world.objects().get(self.owner); - let stat = |stat| rpg.stat(stat, obj, world.objects()); + let stat = |stat| rpg.stat(stat, owner, world.objects()); let msg = |id| &self.msgs.get(id).unwrap().text; let mut cols = [BString::new(), BString::new(), BString::new(), BString::new()]; @@ -395,8 +395,8 @@ impl Internal { misc.push_str(name); misc.push_str("\n---------------------\n\n\n\n\n\n\n\n"); - for &slot in &[EquipmentSlot::LeftHand, EquipmentSlot::RightHand] { - let item = obj.equipment(slot, world.objects()); + for &slot in &[EquipmentSlot::Hand(Hand::Left), EquipmentSlot::Hand(Hand::Right)] { + let item = owner.equipment(slot, world.objects()); misc.push_str("---------------------\n"); if let Some(item) = item { let item = &world.objects().get(item); @@ -479,9 +479,9 @@ impl Internal { } let mut total_weight = BString::new(); - if obj.kind() == EntityKind::Critter { + if owner.kind() == EntityKind::Critter { let cw = stat(Stat::CarryWeight); - let w = obj.inventory.weight(world.objects()); + let w = owner.inventory.weight(world.objects()); // Total Wt: 100/200 total_weight.push_str(msg(MSG_TOTAL_WEIGHT)); total_weight.push(b' '); @@ -489,7 +489,7 @@ impl Internal { total_weight.push(b'/'); total_weight.push_str(cw.to_bstring()); } - let overloaded = obj.is_overloaded(rpg, world.objects()); + let overloaded = owner.is_overloaded(rpg, world.objects()); { let mut w = ui.widget_mut::(self.total_weight); let w = w.text_mut().unwrap(); @@ -562,8 +562,8 @@ impl Internal { Some(match () { _ if widget == self.list => Slot::Inventory, _ if widget == self.wearing => Slot::Equipment(EquipmentSlot::Armor), - _ if widget == self.left_hand => Slot::Equipment(EquipmentSlot::LeftHand), - _ if widget == self.right_hand => Slot::Equipment(EquipmentSlot::RightHand), + _ if widget == self.left_hand => Slot::Equipment(EquipmentSlot::Hand(Hand::Left)), + _ if widget == self.right_hand => Slot::Equipment(EquipmentSlot::Hand(Hand::Right)), _ => return None, }) } @@ -589,7 +589,7 @@ impl Internal { let (bump, existing) = match target_slot { Slot::Inventory => (Some(obj), None), Slot::Equipment(eq_slot) => { - let v = world.objects().get(self.obj) + let v = world.objects().get(self.owner) .equipment(eq_slot, world.objects()); (v, v) } @@ -609,8 +609,8 @@ impl Internal { match target_slot { EquipmentSlot::Armor => obj.flags.insert(Flag::Worn), - EquipmentSlot::LeftHand => obj.flags.insert(Flag::LeftHand), - EquipmentSlot::RightHand => obj.flags.insert(Flag::RightHand), + EquipmentSlot::Hand(Hand::Left) => obj.flags.insert(Flag::LeftHand), + EquipmentSlot::Hand(Hand::Right) => obj.flags.insert(Flag::RightHand), } } Slot::Inventory => {} @@ -621,7 +621,7 @@ impl Internal { // Bump item: remove from slots and move to inventory top. if let Some(bump) = bump { - let mut owner = world.objects().get_mut(self.obj); + let mut owner = world.objects().get_mut(self.owner); world.objects().get_mut(bump) .flags.remove(Flag::Worn | Flag::LeftHand | Flag::RightHand); let i = owner.inventory.items.iter() @@ -633,7 +633,7 @@ impl Internal { } { - let owner = &mut world.objects().get_mut(self.obj); + let owner = &mut world.objects().get_mut(self.owner); if src_slot == Slot::Equipment(EquipmentSlot::Armor) { let old_armor = world.objects().get(obj); rpg.apply_armor_change(owner, None, Some(old_armor), world.objects()); @@ -645,6 +645,13 @@ impl Internal { } self.sync_from_obj(rpg, ui); + self.sync_owner_fid(rpg) + } + + fn sync_owner_fid(&self, rpg: &Rpg) { + let world = self.world.borrow(); + let mut owner = world.objects().get_mut(self.owner); + owner.fid = owner.equipped_fid(world.objects(), rpg); } } diff --git a/src/game/object.rs b/src/game/object.rs index 1677366..969a20c 100644 --- a/src/game/object.rs +++ b/src/game/object.rs @@ -9,7 +9,7 @@ use std::mem; use std::rc::Rc; use crate::asset::*; -use crate::asset::frame::{FrameId, FrameDb}; +use crate::asset::frame::*; use crate::asset::proto::*; use crate::asset::script::ProgramId; use crate::game::rpg::Rpg; @@ -81,8 +81,13 @@ pub struct InventoryItem { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum EquipmentSlot { Armor, - LeftHand, - RightHand, + Hand(Hand), +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Hand { + Left, + Right, } #[derive(Clone, Copy, Debug, Default)] @@ -439,8 +444,8 @@ impl Object { pub fn equipment(&self, slot: EquipmentSlot, objects: &Objects) -> Option { let flag = match slot { EquipmentSlot::Armor => Flag::Worn, - EquipmentSlot::LeftHand => Flag::LeftHand, - EquipmentSlot::RightHand => Flag::RightHand, + EquipmentSlot::Hand(Hand::Left) => Flag::LeftHand, + EquipmentSlot::Hand(Hand::Right) => Flag::RightHand, }; self.find_inventory_item(objects, |o| o.flags.contains(flag)) } @@ -573,6 +578,38 @@ impl Object { } } + // adjust_fid + pub fn equipped_fid(&self, objects: &Objects, rpg: &Rpg) -> FrameId { + if self.proto().unwrap().kind() != ExactEntityKind::Critter { + return self.fid; + } + let dude = self.sub.as_critter().unwrap().try_dude(); + + let idx = self.equipment(EquipmentSlot::Armor, objects) + .map(|armor| { + let armor = objects.get(armor); + let armor = armor.proto().unwrap(); + let armor = armor.sub.as_armor().unwrap(); + if rpg.stat(Stat::Gender, self, objects) == 1 { + armor.female_fidx + } else { + armor.male_fidx + } + }) + .or(dude.map(|d| d.naked_fidx)) + .unwrap_or(self.fid.idx()); + + let active_hand = dude.map(|d| d.active_hand).unwrap_or(Hand::Left); + let weapon = self.equipment(EquipmentSlot::Hand(active_hand), objects) + .and_then(|item| { + objects.get(item).proto().unwrap().sub + .as_weapon().map(|w| w.kind) + }) + .unwrap_or(WeaponKind::Unarmed); + + FrameId::new_critter(Some(self.direction), CritterAnim::Stand, weapon, idx).unwrap() + } + fn find_inventory_item(&self, objects: &Objects, f: impl Fn(&Object) -> bool) -> Option { self.inventory.items.iter() .map(|i| i.object) @@ -1465,6 +1502,8 @@ pub enum SubObject { #[derive(Debug)] pub struct Dude { + pub naked_fidx: Idx, + pub active_hand: Hand, } #[derive(Debug)] @@ -1502,11 +1541,11 @@ impl Critter { } pub fn try_dude(&self) -> Option<&Dude> { - self.dude.as_ref().map(|v| &**v) + self.dude.as_deref() } pub fn try_dude_mut(&mut self) -> Option<&mut Dude> { - self.dude.as_mut().map(|v| &mut **v) + self.dude.as_deref_mut() } } diff --git a/src/game/rpg.rs b/src/game/rpg.rs index f9b110e..e4fdc5e 100644 --- a/src/game/rpg.rs +++ b/src/game/rpg.rs @@ -12,7 +12,7 @@ use std::io; use crate::asset::{DamageKind, ExactEntityKind, Perk, PCStat, Skill, Stat, Trait}; use crate::asset::message::{Messages, MessageId}; use crate::asset::proto::ProtoId; -use crate::game::object::{DamageFlag, EquipmentSlot, Object, Objects}; +use crate::game::object::{DamageFlag, EquipmentSlot, Hand, Object, Objects}; use crate::fs::FileSystem; use crate::util::random::*; @@ -225,8 +225,8 @@ impl Rpg { Endurance => pei(GainEndurance), Charisma => { let wearing_shades = [ - EquipmentSlot::LeftHand, - EquipmentSlot::RightHand + EquipmentSlot::Hand(Hand::Left), + EquipmentSlot::Hand(Hand::Right) ].iter() .flat_map(|&s| obj.equipment(s, objs).into_iter()) .flat_map(|o| objs.get(o).proto_id().into_iter()) @@ -492,8 +492,6 @@ impl Rpg { self.pc_stats[pc_stat] = value; if pc_stat == PCStat::Experience { // TODO statPCAddExperienceCheckPMs_(0, 1); - } else { - } } else { // TODO statPcResetExperience_(value) diff --git a/src/game/state.rs b/src/game/state.rs index a8608b8..f3f4338 100644 --- a/src/game/state.rs +++ b/src/game/state.rs @@ -185,6 +185,8 @@ impl GameState { poison: 0, combat: Default::default(), dude: Some(Box::new(Dude { + naked_fidx: 0x3e, + active_hand: Hand::Left, })), })); self.world.borrow_mut().insert_object(dude_obj);