Merge branch 'juliancoffee/bigger-kit' into 'master'

Make /kit use all weapons from possible modulars

See merge request veloren/veloren!3595
This commit is contained in:
Samuel Keiffer 2022-09-06 04:45:12 +00:00
commit 34fa03a425
4 changed files with 185 additions and 80 deletions

View File

@ -28,7 +28,6 @@
(2, Item("common.items.weapons.axe.parashu")),
(2, Item("common.items.weapons.bow.sagitta")),
(2, Item("common.items.weapons.staff.laevateinn")),
(1, Item("common.items.weapons.sceptre.root_evil")),
(1, Item("common.items.weapons.sceptre.caduceus")),
]), None)),
)),
@ -39,4 +38,4 @@
meta: [
SkillSetAsset("common.skillset.preset.rank5.fullskill"),
],
)
)

View File

@ -74,25 +74,6 @@
(Item("common.items.armor.misc.ring.gold"), 2),
(Item("common.items.armor.cultist.ring"), 2),
],
"endgame": [
// Cultist weapons
(Item("common.items.weapons.hammer.cultist_purp_2h-0"),1),
(Item("common.items.weapons.staff.cultist_staff"),1),
(Item("common.items.weapons.sword.cultist"),1),
(Item("common.items.weapons.bow.velorite"),1),
(Item("common.items.weapons.axe.malachite_axe-0"),1),
(Item("common.items.weapons.sceptre.sceptre_velorite_0"),1),
// Legendaries
(Item("common.items.weapons.hammer.mjolnir"),1),
(Item("common.items.weapons.staff.laevateinn"),1),
(Item("common.items.weapons.sword.caladbolg"),1),
(Item("common.items.weapons.bow.sagitta"),1),
(Item("common.items.weapons.axe.parashu"),1),
(Item("common.items.weapons.sceptre.caduceus"),1),
(Item("common.items.weapons.sceptre.root_evil"),1),
],
"tier-5": [
// Hide
(Item("common.items.armor.hide.dragonscale.back"), 1),
@ -129,6 +110,14 @@
(ModularWeapon(tool: Staff, material: Eldwood),1),
(ModularWeapon(tool: Sceptre, material: Eldwood),1),
// Legendaries
(Item("common.items.weapons.hammer.mjolnir"),1),
(Item("common.items.weapons.staff.laevateinn"),1),
(Item("common.items.weapons.sword.caladbolg"),1),
(Item("common.items.weapons.bow.sagitta"),1),
(Item("common.items.weapons.axe.parashu"),1),
(Item("common.items.weapons.sceptre.caduceus"),1),
// Potion Kit
(Item("common.items.consumable.potion_med"), 100),
],
@ -337,6 +326,14 @@
(Item("common.items.armor.ferocious.back"),1),
],
"cultist": [
// Cultist weapons
(Item("common.items.weapons.hammer.cultist_purp_2h-0"),1),
(Item("common.items.weapons.staff.cultist_staff"),1),
(Item("common.items.weapons.sword.cultist"),1),
(Item("common.items.weapons.bow.velorite"),1),
(Item("common.items.weapons.axe.malachite_axe-0"),1),
(Item("common.items.weapons.sceptre.sceptre_velorite_0"),1),
// Clothes
(Item("common.items.armor.cultist.chest"), 1),
(Item("common.items.armor.cultist.pants"), 1),
(Item("common.items.armor.cultist.hand"), 1),

View File

@ -317,24 +317,37 @@ lazy_static! {
static ref PRIMARY_COMPONENT_POOL: PrimaryComponentPool = {
let mut component_pool = HashMap::new();
// Load recipe book (done to check that material is valid for a particular component)
// Load recipe book
// (done to check that material is valid for a particular component)
use crate::recipe::ComponentKey;
let recipes = recipe::default_component_recipe_book().read();
let ability_map = &AbilityMap::load().read();
let msm = &MaterialStatManifest::load().read();
recipes
.iter()
.for_each(|(ComponentKey { toolkind, material, .. }, recipe)| {
recipes.iter().for_each(
|(
ComponentKey {
toolkind, material, ..
},
recipe,
)| {
let component = recipe.item_output(ability_map, msm);
let hand_restriction = if let ItemKind::ModularComponent(ModularComponent::ToolPrimaryComponent { hand_restriction, .. }) = &*component.kind() {
*hand_restriction
} else {
return;
};
let entry = component_pool.entry((*toolkind, String::from(material))).or_insert(Vec::new());
let hand_restriction =
if let ItemKind::ModularComponent(ModularComponent::ToolPrimaryComponent {
hand_restriction,
..
}) = &*component.kind()
{
*hand_restriction
} else {
return;
};
let entry = component_pool
.entry((*toolkind, String::from(material)))
.or_insert(Vec::new());
entry.push((component, hand_restriction));
});
},
);
component_pool
};
@ -352,7 +365,12 @@ lazy_static! {
.filter_map(|comp| Some(comp.item_definition_id().itemdef_id()?.to_owned()))
.filter_map(|id| Arc::<ItemDef>::load_cloned(&id).ok())
.for_each(|comp_def| {
if let ItemKind::ModularComponent(ModularComponent::ToolSecondaryComponent { hand_restriction, .. }) = comp_def.kind {
if let ItemKind::ModularComponent(
ModularComponent::ToolSecondaryComponent {
hand_restriction, ..
},
) = comp_def.kind
{
let entry = component_pool.entry(toolkind).or_insert(Vec::new());
entry.push((Arc::clone(&comp_def), hand_restriction));
}
@ -371,14 +389,49 @@ pub enum ModularWeaponCreationError {
SecondaryComponentNotFound,
}
/// Check if hand restrictions are compatible.
///
/// If at least on of them is omitted, check is passed.
fn compatible_handndess(a: Option<Hands>, b: Option<Hands>) -> bool {
match (a, b) {
(Some(a), Some(b)) => a == b,
_ => true,
}
}
/// Generate all primary components for specific tool and material.
///
/// Read [random_weapon_primary_component] for more.
pub fn generate_weapon_primary_components(
tool: ToolKind,
material: Material,
hand_restriction: Option<Hands>,
) -> Result<Vec<(Item, Option<Hands>)>, ModularWeaponCreationError> {
if let Some(material_id) = material.asset_identifier() {
// Loads default ability map and material stat manifest for later use
let ability_map = &AbilityMap::load().read();
let msm = &MaterialStatManifest::load().read();
Ok(PRIMARY_COMPONENT_POOL
.get(&(tool, material_id.to_owned()))
.into_iter()
.flatten()
.filter(|(_comp, hand)| compatible_handndess(hand_restriction, *hand))
.map(|(c, h)| (c.duplicate(ability_map, msm), hand_restriction.or(*h)))
.collect())
} else {
Err(ModularWeaponCreationError::MaterialNotFound)
}
}
/// Creates a random modular weapon primary component when provided with a
/// toolkind, material, and optionally the handedness
///
/// Note: The component produced is not necessarily restricted to that
/// NOTE: The component produced is not necessarily restricted to that
/// handedness, but rather is able to produce a weapon of that handedness
/// depending on what secondary component is used
///
/// Returns the comptabile handednesses that can be used with provided
/// Returns the compatible handednesses that can be used with provided
/// restriction and generated component (useful for cases where no restriction
/// was passed in, but generated component has a restriction)
pub fn random_weapon_primary_component(
@ -387,7 +440,7 @@ pub fn random_weapon_primary_component(
hand_restriction: Option<Hands>,
mut rng: &mut impl Rng,
) -> Result<(Item, Option<Hands>), ModularWeaponCreationError> {
let result = (|| {
let result = {
if let Some(material_id) = material.asset_identifier() {
// Loads default ability map and material stat manifest for later use
let ability_map = &AbilityMap::load().read();
@ -397,10 +450,7 @@ pub fn random_weapon_primary_component(
.get(&(tool, material_id.to_owned()))
.into_iter()
.flatten()
.filter(|(_comp, hand)| match (hand_restriction, hand) {
(Some(restriction), Some(hand)) => restriction == *hand,
(None, _) | (_, None) => true,
})
.filter(|(_comp, hand)| compatible_handndess(hand_restriction, *hand))
.collect::<Vec<_>>();
let (comp, hand) = primary_components
@ -411,7 +461,8 @@ pub fn random_weapon_primary_component(
} else {
Err(ModularWeaponCreationError::MaterialNotFound)
}
})();
};
if let Err(err) = &result {
let error_str = format!(
"Failed to synthesize a primary component for a modular {tool:?} made of {material:?} \
@ -422,6 +473,45 @@ pub fn random_weapon_primary_component(
result
}
pub fn generate_weapons(
tool: ToolKind,
material: Material,
hand_restriction: Option<Hands>,
) -> Result<Vec<Item>, ModularWeaponCreationError> {
// Loads default ability map and material stat manifest for later use
let ability_map = &AbilityMap::load().read();
let msm = &MaterialStatManifest::load().read();
let primaries = generate_weapon_primary_components(tool, material, hand_restriction)?;
let mut weapons = Vec::new();
for (comp, comp_hand) in primaries {
let secondaries = SECONDARY_COMPONENT_POOL
.get(&tool)
.into_iter()
.flatten()
.filter(|(_def, hand)| compatible_handndess(hand_restriction, *hand))
.filter(|(_def, hand)| compatible_handndess(comp_hand, *hand));
for (def, _hand) in secondaries {
let secondary = Item::new_from_item_base(
ItemBase::Simple(Arc::clone(def)),
Vec::new(),
ability_map,
msm,
);
weapons.push(Item::new_from_item_base(
ItemBase::Modular(ModularBase::Tool),
vec![comp.duplicate(ability_map, msm), secondary],
ability_map,
msm,
));
}
}
Ok(weapons)
}
/// Creates a random modular weapon when provided with a toolkind, material, and
/// optionally the handedness
pub fn random_weapon(
@ -430,7 +520,7 @@ pub fn random_weapon(
hand_restriction: Option<Hands>,
mut rng: &mut impl Rng,
) -> Result<Item, ModularWeaponCreationError> {
let result = (|| {
let result = {
// Loads default ability map and material stat manifest for later use
let ability_map = &AbilityMap::load().read();
let msm = &MaterialStatManifest::load().read();
@ -442,10 +532,7 @@ pub fn random_weapon(
.get(&tool)
.into_iter()
.flatten()
.filter(|(_def, hand)| match (hand_restriction, hand) {
(Some(restriction), Some(hand)) => restriction == *hand,
(None, _) | (_, None) => true,
})
.filter(|(_def, hand)| compatible_handndess(hand_restriction, *hand))
.collect::<Vec<_>>();
let secondary_component = {
@ -453,6 +540,7 @@ pub fn random_weapon(
.choose(&mut rng)
.ok_or(ModularWeaponCreationError::SecondaryComponentNotFound)?
.0;
Item::new_from_item_base(
ItemBase::Simple(Arc::clone(def)),
Vec::new(),
@ -468,7 +556,7 @@ pub fn random_weapon(
ability_map,
msm,
))
})();
};
if let Err(err) = &result {
let error_str = format!(
"Failed to synthesize a modular {tool:?} made of {material:?} that had a hand \

View File

@ -1944,51 +1944,72 @@ where
if target_inventory.free_slots() < count {
return Err("Inventory doesn't have enough slots".to_owned());
}
let mut rng = thread_rng();
for (item_id, quantity) in kit {
let mut item = match &item_id {
KitSpec::Item(item_id) => Item::new_from_asset(item_id)
.map_err(|_| format!("Unknown item: {:#?}", item_id))?,
KitSpec::ModularWeapon { tool, material } => {
comp::item::modular::random_weapon(*tool, *material, None, &mut rng)
.map_err(|err| format!("{:#?}", err))?
},
};
let mut res = Ok(());
// Either push stack or push one by one.
if item.is_stackable() {
// FIXME: in theory, this can fail,
// but we don't have stack sizes yet.
let _ = item.set_amount(quantity);
res = target_inventory.push(item);
for (item_id, quantity) in kit {
push_item(item_id, quantity, server, &mut |item| {
let res = target_inventory.push(item);
let _ = target_inv_update.insert(
target,
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug),
);
} else {
let ability_map = server.state.ecs().read_resource::<AbilityMap>();
let msm = server.state.ecs().read_resource::<MaterialStatManifest>();
for _ in 0..quantity {
res = target_inventory.push(item.duplicate(&ability_map, &msm));
let _ = target_inv_update.insert(
target,
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug),
);
}
}
// I think it's possible to pick-up item during this loop
// and fail into case where you had space but now you don't?
if res.is_err() {
return Err("Can't fit item to inventory".to_owned());
}
res
})?;
}
Ok(())
} else {
Err("Could not get inventory".to_string())
}
}
fn push_item(
item_id: KitSpec,
quantity: u32,
server: &Server,
push: &mut dyn FnMut(Item) -> Result<(), Item>,
) -> CmdResult<()> {
let items = match &item_id {
KitSpec::Item(item_id) => vec![
Item::new_from_asset(item_id).map_err(|_| format!("Unknown item: {:#?}", item_id))?,
],
KitSpec::ModularWeapon { tool, material } => {
comp::item::modular::generate_weapons(*tool, *material, None)
.map_err(|err| format!("{:#?}", err))?
},
};
let mut res = Ok(());
for mut item in items {
// Either push stack or push one by one.
if item.is_stackable() {
// FIXME: in theory, this can fail,
// but we don't have stack sizes yet.
let _ = item.set_amount(quantity);
res = push(item);
} else {
let ability_map = server.state.ecs().read_resource::<AbilityMap>();
let msm = server.state.ecs().read_resource::<MaterialStatManifest>();
for _ in 0..quantity {
res = push(item.duplicate(&ability_map, &msm));
if res.is_err() {
break;
}
}
}
// I think it's possible to pick-up item during this loop
// and fail into case where you had space but now you don't?
if res.is_err() {
return Err("Can't fit item to inventory".to_owned());
}
}
Ok(())
}
fn handle_object(
server: &mut Server,
client: EcsEntity,