mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Fixed loading of modular weapons from the loadout to work with nested components.
Modular weapons can now be correctly dispalyed when wielded.
This commit is contained in:
parent
0a38567e8d
commit
38cb465722
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
||||
({
|
||||
"common.items.crafting_ing.modular.damage.sword.tier5": Damage((
|
||||
"weapon_components.sword.cultist.cultist_purp_2h-0_blade", (1, 2, 11),
|
||||
)),
|
||||
"common.items.crafting_ing.modular.held.sword.tier5": Held((
|
||||
"weapon_components.sword.cultist.cultist_purp_2h-0_handle", (-2.0, -4.5, -7.5), (0, 0, 0),
|
||||
)),
|
||||
})
|
@ -138,11 +138,10 @@ impl Loadout {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_item_at_slot_using_persistence_key<F: FnOnce(&mut Item)>(
|
||||
pub fn get_mut_item_at_slot_using_persistence_key(
|
||||
&mut self,
|
||||
persistence_key: &str,
|
||||
f: F,
|
||||
) -> Result<(), LoadoutError> {
|
||||
) -> Result<&mut Item, LoadoutError> {
|
||||
self.slots
|
||||
.iter_mut()
|
||||
.find(|loadout_slot| loadout_slot.persistence_key == persistence_key)
|
||||
@ -150,11 +149,17 @@ impl Loadout {
|
||||
loadout_slot
|
||||
.slot
|
||||
.as_mut()
|
||||
.map_or(Err(LoadoutError::NoParentAtSlot), |item| {
|
||||
f(item);
|
||||
Ok(())
|
||||
})
|
||||
.ok_or(LoadoutError::NoParentAtSlot)
|
||||
})
|
||||
// .map_or(Err(LoadoutError::InvalidPersistenceKey), |loadout_slot| {
|
||||
// loadout_slot
|
||||
// .slot
|
||||
// .as_mut()
|
||||
// .map_or(Err(LoadoutError::NoParentAtSlot), |item| {
|
||||
// f(item);
|
||||
// Ok(())
|
||||
// })
|
||||
// })
|
||||
}
|
||||
|
||||
/// Swaps the contents of two loadout slots
|
||||
|
@ -256,6 +256,56 @@ pub fn convert_waypoint_from_database_json(
|
||||
))
|
||||
}
|
||||
|
||||
// Used to handle cases of modular items that are composed of components.
|
||||
// Returns a mutable reference to the parent of an item that is a component. If
|
||||
// parent item is itself a component, recursively goes through inventory until
|
||||
// it grabs component.
|
||||
fn get_mutable_parent_item<'a, 'b, T>(
|
||||
index: usize,
|
||||
inventory_items: &'a [Item],
|
||||
item_indices: &'a HashMap<i64, usize>,
|
||||
inventory: &'b mut T,
|
||||
get_mut_item: &'a dyn Fn(&'b mut T, &str) -> Option<&'b mut VelorenItem>,
|
||||
) -> Option<&'a mut VelorenItem>
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
// First checks if parent item is itself also a component, if it is, tries to
|
||||
// get a mutable reference to itself by getting a mutable reference to the item
|
||||
// that is its own parent
|
||||
if inventory_items[index].position.contains("component_") {
|
||||
if let Some(parent) = item_indices
|
||||
.get(&inventory_items[index].parent_container_item_id)
|
||||
.and_then(move |i| {
|
||||
get_mutable_parent_item(
|
||||
*i,
|
||||
inventory_items,
|
||||
item_indices,
|
||||
inventory,
|
||||
// slot,
|
||||
get_mut_item,
|
||||
)
|
||||
})
|
||||
{
|
||||
// Parses component index
|
||||
let component_index = inventory_items[index]
|
||||
.position
|
||||
.split('_')
|
||||
.collect::<Vec<_>>()
|
||||
.get(1)
|
||||
.and_then(|s| s.parse::<usize>().ok());
|
||||
// Returns mutable reference to parent item of original item, by grabbing
|
||||
// the component representing the parent item from the parent item's parent
|
||||
// item
|
||||
component_index.and_then(move |i| parent.component_mut(i))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
get_mut_item(inventory, &inventory_items[index].position)
|
||||
}
|
||||
}
|
||||
|
||||
/// Properly-recursive items (currently modular weapons) occupy the same
|
||||
/// inventory slot as their parent. The caller is responsible for ensuring that
|
||||
/// inventory_items and loadout_items are topologically sorted (i.e. forall i,
|
||||
@ -344,59 +394,13 @@ pub fn convert_inventory_from_database_items(
|
||||
));
|
||||
}
|
||||
} else if let Some(&j) = item_indices.get(&db_item.parent_container_item_id) {
|
||||
// Returns a mutable reference to the parent of an item that is a component. If
|
||||
// parent item is itself a component, recursively goes through inventory until
|
||||
// it grabs component.
|
||||
fn get_mutable_parent_item<'a>(
|
||||
index: usize,
|
||||
inventory_items: &'a [Item],
|
||||
item_indices: &'a HashMap<i64, usize>,
|
||||
inventory: &'a mut Inventory,
|
||||
slot: &dyn Fn(&str) -> Result<InvSlotId, PersistenceError>,
|
||||
) -> Option<&'a mut VelorenItem> {
|
||||
// First checks if parent item is itself also a component, if it is, tries to
|
||||
// get a mutable reference to itself by getting a mutable reference to the item
|
||||
// that is its own parent
|
||||
if inventory_items[index].position.contains("component_") {
|
||||
if let Some(parent) = item_indices
|
||||
.get(&inventory_items[index].parent_container_item_id)
|
||||
.and_then(move |i| {
|
||||
get_mutable_parent_item(
|
||||
*i,
|
||||
inventory_items,
|
||||
item_indices,
|
||||
inventory,
|
||||
slot,
|
||||
)
|
||||
})
|
||||
{
|
||||
// Parses component index
|
||||
let component_index = inventory_items[index]
|
||||
.position
|
||||
.split('_')
|
||||
.collect::<Vec<_>>()
|
||||
.get(1)
|
||||
.and_then(|s| s.parse::<usize>().ok());
|
||||
// Returns mutable reference to parent item of original item, by grabbing
|
||||
// the component representing the parent item from the parent item's parent
|
||||
// item
|
||||
component_index.and_then(move |i| parent.component_mut(i))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if let Some(parent) =
|
||||
// Parent item is not itself a component, just grab it from the
|
||||
// inventory
|
||||
inventory.slot_mut(slot(&inventory_items[index].position).ok()?)
|
||||
{
|
||||
parent.as_mut()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
if let Some(parent) =
|
||||
get_mutable_parent_item(j, inventory_items, &item_indices, &mut inventory, &slot)
|
||||
{
|
||||
if let Some(parent) = get_mutable_parent_item(
|
||||
j,
|
||||
inventory_items,
|
||||
&item_indices,
|
||||
&mut inventory,
|
||||
&|inv, s| inv.slot_mut(slot(s).ok()?).and_then(|a| a.as_mut()),
|
||||
) {
|
||||
parent.add_component(item, &ABILITY_MAP, &MATERIAL_STATS_MANIFEST);
|
||||
} else {
|
||||
return Err(PersistenceError::ConversionError(format!(
|
||||
@ -451,11 +455,25 @@ pub fn convert_loadout_from_database_items(
|
||||
.set_item_at_slot_using_persistence_key(&db_item.position, item)
|
||||
.map_err(convert_error)?;
|
||||
} else if let Some(&j) = item_indices.get(&db_item.parent_container_item_id) {
|
||||
loadout
|
||||
.update_item_at_slot_using_persistence_key(&database_items[j].position, |parent| {
|
||||
parent.add_component(item, &ABILITY_MAP, &MATERIAL_STATS_MANIFEST);
|
||||
if let Some(parent) =
|
||||
get_mutable_parent_item(j, database_items, &item_indices, &mut loadout, &|l, s| {
|
||||
l.get_mut_item_at_slot_using_persistence_key(s).ok()
|
||||
})
|
||||
.map_err(convert_error)?;
|
||||
{
|
||||
parent.add_component(item, &ABILITY_MAP, &MATERIAL_STATS_MANIFEST);
|
||||
} else {
|
||||
return Err(PersistenceError::ConversionError(format!(
|
||||
"Parent slot {} for component {} was empty even though it occurred earlier in \
|
||||
the loop?",
|
||||
db_item.parent_container_item_id, db_item.item_id
|
||||
)));
|
||||
}
|
||||
// loadout
|
||||
// .update_item_at_slot_using_persistence_key(&
|
||||
// database_items[j].position, |parent| {
|
||||
// parent.add_component(item, &ABILITY_MAP,
|
||||
// &MATERIAL_STATS_MANIFEST); })
|
||||
// .map_err(convert_error)?;
|
||||
} else {
|
||||
return Err(PersistenceError::ConversionError(format!(
|
||||
"Couldn't find parent item {} before item {} in loadout",
|
||||
|
@ -15,7 +15,7 @@ use common::{
|
||||
item::{
|
||||
armor::{Armor, ArmorKind},
|
||||
item_key::ItemKey,
|
||||
Item, ItemKind,
|
||||
modular, Item, ItemKind,
|
||||
},
|
||||
CharacterState,
|
||||
},
|
||||
@ -26,6 +26,7 @@ use common::{
|
||||
use core::{hash::Hash, ops::Range};
|
||||
use crossbeam_utils::atomic;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
@ -71,10 +72,10 @@ pub struct FigureKey<Body> {
|
||||
pub(super) extra: Option<Arc<CharacterCacheKey>>,
|
||||
}
|
||||
|
||||
#[derive(Eq, Hash, PartialEq)]
|
||||
pub(super) struct ToolKey {
|
||||
pub name: String,
|
||||
pub components: Vec<String>,
|
||||
#[derive(Deserialize, Eq, Hash, PartialEq, Debug)]
|
||||
pub enum ToolKey {
|
||||
Tool(String),
|
||||
Modular(modular::ModularWeaponKey),
|
||||
}
|
||||
|
||||
/// Character data that should be visible when tools are visible (i.e. in third
|
||||
@ -214,13 +215,12 @@ impl CharacterCacheKey {
|
||||
})
|
||||
},
|
||||
tool: if are_tools_visible {
|
||||
let tool_key_from_item = |item: &Item| ToolKey {
|
||||
name: item.item_definition_id().to_owned(),
|
||||
components: item
|
||||
.components()
|
||||
.iter()
|
||||
.map(|comp| comp.item_definition_id().to_owned())
|
||||
.collect(),
|
||||
let tool_key_from_item = |item: &Item| {
|
||||
if item.is_modular() {
|
||||
ToolKey::Modular(modular::weapon_to_key(item))
|
||||
} else {
|
||||
ToolKey::Tool(item.item_definition_id().to_owned())
|
||||
}
|
||||
};
|
||||
Some(CharacterToolKey {
|
||||
active: inventory
|
||||
|
@ -12,7 +12,7 @@ use common::{
|
||||
fish_small::{self, BodyType as FSBodyType, Species as FSSpecies},
|
||||
golem::{self, BodyType as GBodyType, Species as GSpecies},
|
||||
humanoid::{self, Body, BodyType, EyeColor, Skin, Species},
|
||||
item::{item_key::ItemKey, ItemDef, ModularComponentKind},
|
||||
item::item_key::ItemKey,
|
||||
item_drop, object,
|
||||
quadruped_low::{self, BodyType as QLBodyType, Species as QLSpecies},
|
||||
quadruped_medium::{self, BodyType as QMBodyType, Species as QMSpecies},
|
||||
@ -28,7 +28,7 @@ use common::{
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use serde::Deserialize;
|
||||
use std::{fmt, hash::Hash, sync::Arc};
|
||||
use std::{fmt, hash::Hash};
|
||||
use tracing::{error, warn};
|
||||
use vek::*;
|
||||
|
||||
@ -339,6 +339,7 @@ impl HumHeadSpec {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Armor aspects should be in the same order, top to bottom.
|
||||
// These seem overly split up, but wanted to keep the armor seperated
|
||||
// unlike head which is done above.
|
||||
@ -365,7 +366,7 @@ struct HumArmorPantsSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
|
||||
#[derive(Deserialize)]
|
||||
struct HumArmorFootSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
|
||||
#[derive(Deserialize)]
|
||||
struct HumMainWeaponSpec(HashMap<String, ArmorVoxSpec>);
|
||||
struct HumMainWeaponSpec(HashMap<ToolKey, ArmorVoxSpec>);
|
||||
#[derive(Deserialize)]
|
||||
struct HumModularComponentSpec(HashMap<String, ModularComponentSpec>);
|
||||
#[derive(Deserialize)]
|
||||
@ -390,7 +391,6 @@ make_vox_spec!(
|
||||
armor_pants: HumArmorPantsSpec = "voxygen.voxel.humanoid_armor_pants_manifest",
|
||||
armor_foot: HumArmorFootSpec = "voxygen.voxel.humanoid_armor_foot_manifest",
|
||||
main_weapon: HumMainWeaponSpec = "voxygen.voxel.biped_weapon_manifest",
|
||||
modular_components: HumModularComponentSpec = "voxygen.voxel.humanoid_modular_component_manifest",
|
||||
armor_lantern: HumArmorLanternSpec = "voxygen.voxel.humanoid_lantern_manifest",
|
||||
armor_glider: HumArmorGliderSpec = "voxygen.voxel.humanoid_glider_manifest",
|
||||
armor_head: HumArmorHeadSpec = "voxygen.voxel.humanoid_armor_head_manifest",
|
||||
@ -499,14 +499,12 @@ make_vox_spec!(
|
||||
)),
|
||||
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
|
||||
spec.main_weapon.read().0.mesh_main_weapon(
|
||||
&spec.modular_components.read().0,
|
||||
tool,
|
||||
false,
|
||||
)
|
||||
}),
|
||||
tool.and_then(|tool| tool.second.as_ref()).map(|tool| {
|
||||
spec.main_weapon.read().0.mesh_main_weapon(
|
||||
&spec.modular_components.read().0,
|
||||
tool,
|
||||
true,
|
||||
)
|
||||
@ -876,68 +874,20 @@ impl HumArmorFootSpec {
|
||||
}
|
||||
|
||||
impl HumMainWeaponSpec {
|
||||
fn mesh_main_weapon(
|
||||
&self,
|
||||
modular_components: &HumModularComponentSpec,
|
||||
tool: &ToolKey,
|
||||
flipped: bool,
|
||||
) -> BoneMeshes {
|
||||
// TODO: resolve ItemDef info into the ToolKey earlier
|
||||
let itemdef = Arc::<ItemDef>::load_expect_cloned(&tool.name);
|
||||
let not_found = |name: &str| {
|
||||
error!(?name, "No tool/weapon specification exists");
|
||||
fn mesh_main_weapon(&self, tool: &ToolKey, flipped: bool) -> BoneMeshes {
|
||||
let not_found = |tool: &ToolKey| {
|
||||
error!(?tool, "No tool/weapon specification exists");
|
||||
load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0))
|
||||
};
|
||||
let (tool_kind_segment, mut offset) = if itemdef.is_modular() {
|
||||
let (mut damage, mut held) = (None, None);
|
||||
for comp in tool.components.iter() {
|
||||
let compdef = Arc::<ItemDef>::load_expect_cloned(comp);
|
||||
if compdef.is_component(ModularComponentKind::Held) {
|
||||
held = Some(comp.clone());
|
||||
}
|
||||
if compdef.is_component(ModularComponentKind::Damage) {
|
||||
damage = Some(comp.clone());
|
||||
}
|
||||
// TODO: when enhancements are added, line them up to make a
|
||||
// pretty row of gems on the hilt or something
|
||||
}
|
||||
if let (Some(damage), Some(held)) = (damage.as_ref(), held.as_ref()) {
|
||||
use ModularComponentSpec::*;
|
||||
let damagespec = match modular_components.0.get(&*damage) {
|
||||
Some(Damage(spec)) => spec,
|
||||
_ => return not_found(damage),
|
||||
};
|
||||
let heldspec = match modular_components.0.get(&*held) {
|
||||
Some(Held(spec)) => spec,
|
||||
_ => return not_found(held),
|
||||
};
|
||||
let damage_asset = graceful_load_vox(&damagespec.0).read();
|
||||
let held_asset = graceful_load_vox(&heldspec.0).read();
|
||||
|
||||
let (segment, segment_origin) = Segment::from_voxes(&[
|
||||
(&damage_asset.0, Vec3::from(damagespec.1), flipped),
|
||||
(&held_asset.0, Vec3::from(heldspec.2), flipped),
|
||||
]);
|
||||
(segment, segment_origin.map(|x: i32| x as f32) + heldspec.1)
|
||||
} else {
|
||||
error!(
|
||||
"A modular weapon is missing some components (damage: {:?}, held: {:?})",
|
||||
damage, held
|
||||
);
|
||||
return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0));
|
||||
}
|
||||
} else {
|
||||
let spec = match self.0.get(&tool.name) {
|
||||
Some(spec) => spec,
|
||||
None => return not_found(&tool.name),
|
||||
};
|
||||
|
||||
(
|
||||
graceful_load_segment_flipped(&spec.vox_spec.0, flipped),
|
||||
Vec3::from(spec.vox_spec.1),
|
||||
)
|
||||
let spec = match self.0.get(tool) {
|
||||
Some(spec) => spec,
|
||||
None => return not_found(tool),
|
||||
};
|
||||
|
||||
let tool_kind_segment = graceful_load_segment_flipped(&spec.vox_spec.0, flipped);
|
||||
let mut offset = Vec3::from(spec.vox_spec.1);
|
||||
|
||||
if flipped {
|
||||
offset.x = 0.0 - offset.x - (tool_kind_segment.sz.x as f32);
|
||||
}
|
||||
@ -2862,7 +2812,7 @@ impl FishSmallLateralSpec {
|
||||
////
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct BipedSmallWeaponSpec(HashMap<String, ArmorVoxSpec>);
|
||||
struct BipedSmallWeaponSpec(HashMap<ToolKey, ArmorVoxSpec>);
|
||||
#[derive(Deserialize)]
|
||||
struct BipedSmallArmorHeadSpec(ArmorVoxSpecMap<String, ArmorVoxSpec>);
|
||||
#[derive(Deserialize)]
|
||||
@ -2929,7 +2879,7 @@ make_vox_spec!(
|
||||
}),
|
||||
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
|
||||
spec.weapon.read().0.mesh_main(
|
||||
&tool.name,
|
||||
tool,
|
||||
false,
|
||||
)
|
||||
}),
|
||||
@ -3106,11 +3056,11 @@ impl BipedSmallArmorFootSpec {
|
||||
}
|
||||
|
||||
impl BipedSmallWeaponSpec {
|
||||
fn mesh_main(&self, item_definition_id: &str, flipped: bool) -> BoneMeshes {
|
||||
let spec = match self.0.get(item_definition_id) {
|
||||
fn mesh_main(&self, tool: &ToolKey, flipped: bool) -> BoneMeshes {
|
||||
let spec = match self.0.get(tool) {
|
||||
Some(spec) => spec,
|
||||
None => {
|
||||
error!(?item_definition_id, "No tool/weapon specification exists");
|
||||
error!(?tool, "No tool/weapon specification exists");
|
||||
return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0));
|
||||
},
|
||||
};
|
||||
@ -3905,9 +3855,9 @@ struct BipedLargeLateralSubSpec {
|
||||
lateral: VoxSimple,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct BipedLargeMainSpec(HashMap<String, ArmorVoxSpec>);
|
||||
struct BipedLargeMainSpec(HashMap<ToolKey, ArmorVoxSpec>);
|
||||
#[derive(Deserialize)]
|
||||
struct BipedLargeSecondSpec(HashMap<String, ArmorVoxSpec>);
|
||||
struct BipedLargeSecondSpec(HashMap<ToolKey, ArmorVoxSpec>);
|
||||
make_vox_spec!(
|
||||
biped_large::Body,
|
||||
struct BipedLargeSpec {
|
||||
@ -3954,13 +3904,13 @@ make_vox_spec!(
|
||||
)),
|
||||
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
|
||||
spec.main.read().0.mesh_main(
|
||||
&tool.name,
|
||||
tool,
|
||||
false,
|
||||
)
|
||||
}),
|
||||
tool.and_then(|tool| tool.active.as_ref()).map(|tool| {
|
||||
spec.second.read().0.mesh_second(
|
||||
&tool.name,
|
||||
tool,
|
||||
false,
|
||||
)
|
||||
}),
|
||||
@ -4212,11 +4162,11 @@ impl BipedLargeLateralSpec {
|
||||
}
|
||||
}
|
||||
impl BipedLargeMainSpec {
|
||||
fn mesh_main(&self, item_definition_id: &str, flipped: bool) -> BoneMeshes {
|
||||
let spec = match self.0.get(item_definition_id) {
|
||||
fn mesh_main(&self, tool: &ToolKey, flipped: bool) -> BoneMeshes {
|
||||
let spec = match self.0.get(tool) {
|
||||
Some(spec) => spec,
|
||||
None => {
|
||||
error!(?item_definition_id, "No tool/weapon specification exists");
|
||||
error!(?tool, "No tool/weapon specification exists");
|
||||
return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0));
|
||||
},
|
||||
};
|
||||
@ -4241,11 +4191,11 @@ impl BipedLargeMainSpec {
|
||||
}
|
||||
}
|
||||
impl BipedLargeSecondSpec {
|
||||
fn mesh_second(&self, item_definition_id: &str, flipped: bool) -> BoneMeshes {
|
||||
let spec = match self.0.get(item_definition_id) {
|
||||
fn mesh_second(&self, tool: &ToolKey, flipped: bool) -> BoneMeshes {
|
||||
let spec = match self.0.get(tool) {
|
||||
Some(spec) => spec,
|
||||
None => {
|
||||
error!(?item_definition_id, "No tool/weapon specification exists");
|
||||
error!(?tool, "No tool/weapon specification exists");
|
||||
return load_mesh("not_found", Vec3::new(-1.5, -1.5, -7.0));
|
||||
},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user