diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 59425332d7..1ab4cde9a7 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -98,6 +98,11 @@ pub enum CraftEvent { slots: Vec<(u32, InvSlotId)>, }, Salvage(InvSlotId), + // TODO: Maybe look at making this more general when there are more modular recipes? + ModularWeapon { + damage_component: InvSlotId, + held_component: InvSlotId, + }, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/inventory/item/modular.rs b/common/src/comp/inventory/item/modular.rs index 3299d7b14c..8168946118 100644 --- a/common/src/comp/inventory/item/modular.rs +++ b/common/src/comp/inventory/item/modular.rs @@ -355,6 +355,11 @@ fn make_mod_comp_dir_def(tool: ToolKind, mod_kind: ModularComponentKind) -> Stri ) } +/// Creates initial item for a modular weapon +pub fn initialize_modular_weapon(toolkind: ToolKind) -> Item { + Item::new_from_asset_expect(&make_weapon_def(toolkind).0) +} + /// Creates a random modular weapon when provided with a toolkind, material, and /// optionally the handedness pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option) -> Item { @@ -372,7 +377,7 @@ pub fn random_weapon(tool: ToolKind, material: super::Material, hands: Option Result { + use modular::{ModularComponent, ModularComponentKind}; + // Closure to get inner modular component info from item in a given slot + let unwrap_modular = |slot| -> Option<&ModularComponent> { + if let Some(ItemKind::ModularComponent(mod_comp)) = inv.get(slot).map(|item| &item.kind) { + Some(mod_comp) + } else { + None + } + }; + + // Checks if both components are comptabile, and if so returns the toolkind to + // make weapon of + let compatiblity = if let (Some(damage_component), Some(held_component)) = ( + unwrap_modular(damage_component), + unwrap_modular(held_component), + ) { + // Checks that damage and held component slots each contain a damage and held + // modular component respectively + if matches!(damage_component.modkind, ModularComponentKind::Damage) + && matches!(held_component.modkind, ModularComponentKind::Held) + { + // Checks that both components are of the same tool kind + if damage_component.toolkind == held_component.toolkind { + // Checks that if both components have a hand restriction, they are the same + let hands_check = damage_component.hand_restriction.map_or(true, |hands| { + held_component + .hand_restriction + .map_or(true, |hands2| hands == hands2) + }); + if hands_check { + Ok(damage_component.toolkind) + } else { + Err(ModularWeaponError::DifferentHands) + } + } else { + Err(ModularWeaponError::DifferentTools) + } + } else { + Err(ModularWeaponError::ComponentMismatch) + } + } else { + Err(ModularWeaponError::InvalidSlot) + }; + + match compatiblity { + Ok(tool_kind) => { + // Remove components from inventory + let damage_component = inv + .take(damage_component, ability_map, msm) + .expect("Expected component to exist"); + let held_component = inv + .take(held_component, ability_map, msm) + .expect("Expected component to exist"); + + // Initialize modular weapon + let mut modular_weapon = modular::initialize_modular_weapon(tool_kind); + + // Insert components into modular weapon item + modular_weapon.add_component(damage_component, ability_map, msm); + modular_weapon.add_component(held_component, ability_map, msm); + + Ok(modular_weapon) + }, + Err(err) => Err(err), + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RecipeBook { recipes: HashMap, diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 4d38a33d16..99f8f641d6 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -660,6 +660,46 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv None } }, + CraftEvent::ModularWeapon { + damage_component, + held_component, + } => { + let sprite = craft_sprite + .filter(|pos| { + let entity_cylinder = get_cylinder(state, entity); + if !within_pickup_range(entity_cylinder, || { + Some(find_dist::Cube { + min: pos.as_(), + side_length: 1.0, + }) + }) { + debug!( + ?entity_cylinder, + "Failed to craft recipe as not within range of required \ + sprite, sprite pos: {}", + pos + ); + false + } else { + true + } + }) + .and_then(|pos| state.terrain().get(pos).ok().copied()) + .and_then(|block| block.get_sprite()); + if matches!(sprite, Some(SpriteKind::CraftingBench)) { + recipe::modular_weapon( + &mut inventory, + damage_component, + held_component, + ability_map, + &msm, + ) + .ok() + .map(|item| vec![item]) + } else { + None + } + }, }; // Attempt to insert items into inventory, dropping them if there is not enough