mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'juliancoffee/glider-ability' into 'master'
Vroom Glider See merge request veloren/veloren!4282
This commit is contained in:
commit
840e95f21c
@ -933,6 +933,13 @@
|
||||
Simple(None, "common.abilities.debug.evolve"),
|
||||
],
|
||||
),
|
||||
Custom("Admin's Eagle"): (
|
||||
primary: Simple(None, "common.abilities.debug.glide_speeder"),
|
||||
secondary: Simple(None, "common.abilities.debug.glide_boost"),
|
||||
abilities: [
|
||||
Simple(None, "common.abilities.debug.eaglify"),
|
||||
],
|
||||
),
|
||||
Tool(Farming): (
|
||||
primary: Simple(None, "common.abilities.farming.basic"),
|
||||
secondary: Simple(None, "common.abilities.farming.basic"),
|
||||
|
7
assets/common/abilities/debug/eaglify.ron
Normal file
7
assets/common/abilities/debug/eaglify.ron
Normal file
@ -0,0 +1,7 @@
|
||||
Transform(
|
||||
buildup_duration: 2.0,
|
||||
recover_duration: 0.5,
|
||||
target: "common.entity.wild.peaceful.eagle",
|
||||
specifier: Some(Evolve),
|
||||
allow_players: true,
|
||||
)
|
3
assets/common/abilities/debug/glide_boost.ron
Normal file
3
assets/common/abilities/debug/glide_boost.ron
Normal file
@ -0,0 +1,3 @@
|
||||
GlideBoost(
|
||||
booster: Upward(100.0),
|
||||
)
|
3
assets/common/abilities/debug/glide_speeder.ron
Normal file
3
assets/common/abilities/debug/glide_speeder.ron
Normal file
@ -0,0 +1,3 @@
|
||||
GlideBoost(
|
||||
booster: Forward(0.75),
|
||||
)
|
@ -1974,6 +1974,9 @@
|
||||
Simple(
|
||||
"common.items.debug.admin",
|
||||
): "armor-tabard-admin_tabard",
|
||||
Simple(
|
||||
"common.items.debug.glider",
|
||||
): "other-glider-vroom-debug",
|
||||
Simple(
|
||||
"common.items.debug.admin_back",
|
||||
): "armor-misc-back-admin_back",
|
||||
|
8
assets/common/items/debug/glider.ron
Normal file
8
assets/common/items/debug/glider.ron
Normal file
@ -0,0 +1,8 @@
|
||||
ItemDef(
|
||||
legacy_name: "Vroom Glider",
|
||||
legacy_description: "goes brrr.",
|
||||
kind: Glider,
|
||||
quality: Debug,
|
||||
tags: [],
|
||||
ability_spec: Some(Custom("Admin's Eagle")),
|
||||
)
|
@ -5,6 +5,7 @@
|
||||
(Item("common.items.debug.admin_sword"),1),
|
||||
(Item("common.items.debug.velorite_bow_debug"), 1),
|
||||
(Item("common.items.debug.admin"),1),
|
||||
(Item("common.items.debug.glider"),1),
|
||||
(Item("common.items.debug.golden_cheese"),100),
|
||||
],
|
||||
"consumables": [
|
||||
|
@ -1,20 +1,7 @@
|
||||
common-abilities-debug-possess = Possessing Arrow
|
||||
.desc = Shoots a poisonous arrow. Lets you control your target.
|
||||
common-abilities-debug-evolve = Evolve
|
||||
.desc = You become your better self.
|
||||
common-abilities-hammer-leap = Smash of Doom
|
||||
.desc = An AOE attack with knockback. Leaps to position of cursor.
|
||||
common-abilities-bow-shotgun = Burst
|
||||
.desc = Launches a burst of arrows.
|
||||
common-abilities-staff-fireshockwave = Ring of Fire
|
||||
.desc = Ignites the ground with fiery shockwave.
|
||||
common-abilities-sceptre-wardingaura = Warding Aura
|
||||
.desc = Wards your allies against enemy attacks.
|
||||
|
||||
# Internal terms, currently only used in zh-Hans.
|
||||
# If we remove them here, they also get auto-removed in zh-Hans,
|
||||
# so please keep them, even when not used in English file.
|
||||
# See https://github.com/WeblateOrg/weblate/issues/9895
|
||||
## Internal terms, currently only used in zh-Hans.
|
||||
## If we remove them here, they also get auto-removed in zh-Hans,
|
||||
## so please keep them, even when not used in English file.
|
||||
## See https://github.com/WeblateOrg/weblate/issues/9895
|
||||
|
||||
-heavy_stance = ""
|
||||
-agile_stance = ""
|
||||
@ -39,7 +26,27 @@ common-abilities-sceptre-wardingaura = Warding Aura
|
||||
-enter_stance = ""
|
||||
-require_stance = ""
|
||||
|
||||
# Sword abilities
|
||||
## Debug abilities
|
||||
common-abilities-debug-possess = Possessing Arrow
|
||||
.desc = Shoots a poisonous arrow. Lets you control your target.
|
||||
common-abilities-debug-evolve = Evolve
|
||||
.desc = You become your better self.
|
||||
common-abilities-debug-glide_boost = Vroom
|
||||
.desc = Gives you the force to reach the sky
|
||||
common-abilities-debug-glide_speeder = Vroom
|
||||
.desc = Gives you the force to reach wherever your eyes look
|
||||
|
||||
## Hotbar abilities
|
||||
common-abilities-hammer-leap = Smash of Doom
|
||||
.desc = An AOE attack with knockback. Leaps to position of cursor.
|
||||
common-abilities-bow-shotgun = Burst
|
||||
.desc = Launches a burst of arrows
|
||||
common-abilities-staff-fireshockwave = Ring of Fire
|
||||
.desc = Ignites the ground with fiery shockwave.
|
||||
common-abilities-sceptre-wardingaura = Warding Aura
|
||||
.desc = Wards your allies against enemy attacks.
|
||||
|
||||
## Sword abilities
|
||||
veloren-core-pseudo_abilities-sword-heavy_stance = Heavy Stance
|
||||
.desc = Attacks in this stance can stagger enemies and deal more damage to staggered enemies but are slower.
|
||||
veloren-core-pseudo_abilities-sword-agile_stance = Agile Stance
|
||||
@ -295,7 +302,7 @@ common-abilities-sword-cleaving_sky_splitter = Sky Splitter
|
||||
A powerful strike that purportedly can even split the sky, but will split through enemies.
|
||||
Requires cleaving stance.
|
||||
|
||||
# Axe abilities
|
||||
## Axe abilities
|
||||
common-abilities-axe-triple_chop = Triple Chop
|
||||
.desc =
|
||||
Three quick strikes.
|
||||
|
@ -1,3 +1,12 @@
|
||||
## internal terms, currently only used in ES
|
||||
## If we remove them here, they also get auto-removed in ES,
|
||||
## so please keep them, even when not used in English file.
|
||||
## See https://github.com/WeblateOrg/weblate/issues/9895
|
||||
-hud-skill-sc_wardaura_title = ""
|
||||
-hud-skill-bow_shotgun_title = ""
|
||||
-hud-skill-st_shockwave_title = ""
|
||||
|
||||
## SkillTree UI
|
||||
hud-rank_up = New skillpoint
|
||||
hud-skill-sp_available =
|
||||
{ $number ->
|
||||
@ -13,11 +22,6 @@ hud-skill-inc_health = Increases your maximum health by { $boost } points.{ $SP
|
||||
hud-skill-inc_energy_title = Increase energy
|
||||
hud-skill-inc_energy = Increases your maximum energy by { $boost } points.{ $SP }
|
||||
|
||||
# internally translations, used by ES file currently
|
||||
-hud-skill-sc_wardaura_title = ""
|
||||
-hud-skill-bow_shotgun_title = ""
|
||||
-hud-skill-st_shockwave_title = ""
|
||||
|
||||
hud-skill-unlck_sword_title = Sword proficiency
|
||||
hud-skill-unlck_sword = Unlocks the sword skill tree.{ $SP }
|
||||
hud-skill-unlck_axe_title = Axe proficiency
|
||||
|
@ -51,3 +51,5 @@ weapon-sword-frost-admin_sword = Admin Greatsword
|
||||
|
||||
weapon-bow-velorite-debug = Admin Velorite Bow
|
||||
.desc = Infused with Velorite power.
|
||||
other-glider-vroom-debug = Vroom Glider
|
||||
.desc = goes brrr
|
||||
|
@ -3816,6 +3816,10 @@
|
||||
"voxel.weapon.tool.broom_belzeshrub_purple",
|
||||
(0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.1,
|
||||
),
|
||||
Simple("common.items.debug.glider"): VoxTrans(
|
||||
"voxel.glider.cloverleaf",
|
||||
(-2.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.9,
|
||||
),
|
||||
// Misc
|
||||
Simple("common.items.weapons.tool.golf_club"): VoxTrans(
|
||||
"voxel.weapon.tool.golf_club",
|
||||
|
@ -68,5 +68,9 @@
|
||||
vox_spec: ("glider.winter_wings", (-26.0, -26.0, 0.0)),
|
||||
color: None
|
||||
),
|
||||
"common.items.debug.glider": (
|
||||
vox_spec: ("glider.cloverleaf", (-19.0, -5.0, 0.0)),
|
||||
color: None
|
||||
),
|
||||
},
|
||||
))
|
||||
|
@ -39,6 +39,9 @@ use std::{borrow::Cow, convert::TryFrom, time::Duration};
|
||||
use super::shockwave::ShockwaveDodgeable;
|
||||
|
||||
pub const BASE_ABILITY_LIMIT: usize = 5;
|
||||
|
||||
// NOTE: different AbilitySpec on same ToolKind share the same key
|
||||
/// Descriptor to pick the right (auxiliary) ability set
|
||||
pub type AuxiliaryKey = (Option<ToolKind>, Option<ToolKind>);
|
||||
|
||||
// TODO: Potentially look into storing previous ability sets for weapon
|
||||
@ -72,6 +75,25 @@ impl Default for ActiveAbilities {
|
||||
}
|
||||
}
|
||||
|
||||
// make it pub, for UI stuff, if you want
|
||||
enum AbilitySource {
|
||||
Weapons,
|
||||
Glider,
|
||||
}
|
||||
|
||||
impl AbilitySource {
|
||||
// Get all needed data here and pick the right ability source
|
||||
//
|
||||
// make it pub, for UI stuff, if you want
|
||||
fn determine(char_state: Option<&CharacterState>) -> Self {
|
||||
if char_state.is_some_and(|c| c.is_glide_wielded()) {
|
||||
Self::Glider
|
||||
} else {
|
||||
Self::Weapons
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveAbilities {
|
||||
pub fn from_auxiliary(
|
||||
auxiliary_sets: HashMap<AuxiliaryKey, Vec<AuxiliaryAbility>>,
|
||||
@ -176,7 +198,7 @@ impl ActiveAbilities {
|
||||
|
||||
let ability_set = |equip_slot| {
|
||||
inv.and_then(|inv| inv.equipped(equip_slot))
|
||||
.map(|i| &i.item_config_expect().abilities)
|
||||
.and_then(|i| i.item_config().map(|c| &c.abilities))
|
||||
};
|
||||
|
||||
let scale_ability = |ability: CharacterAbility, equip_slot| {
|
||||
@ -194,64 +216,62 @@ impl ActiveAbilities {
|
||||
context_index,
|
||||
};
|
||||
|
||||
// This function is an attempt to generalize ability handling
|
||||
let inst_ability = |slot: EquipSlot, offhand: bool| {
|
||||
ability_set(slot).and_then(|abilities| {
|
||||
// We use AbilityInput here as an object to match on, which
|
||||
// roughly corresponds to all needed data we need to know about
|
||||
// ability.
|
||||
use AbilityInput as I;
|
||||
|
||||
// Also we don't provide `ability`, nor `ability_input` as an
|
||||
// argument to the closure, and that wins us a bit of code
|
||||
// duplication we would need to do otherwise, but it's
|
||||
// important that we can and do re-create all needed Ability
|
||||
// information here to make decisions.
|
||||
//
|
||||
// For example, we should't take `input` argument provided to
|
||||
// activate_abilities, because in case of Auxiliary abilities,
|
||||
// it has wrong index.
|
||||
//
|
||||
// We could alternatively just take `ability`, but it works too.
|
||||
let dispatched = match ability.try_ability_set_key()? {
|
||||
I::Guard => abilities.guard(Some(skill_set), context),
|
||||
I::Primary => abilities.primary(Some(skill_set), context),
|
||||
I::Secondary => abilities.secondary(Some(skill_set), context),
|
||||
I::Auxiliary(index) => abilities.auxiliary(index, Some(skill_set), context),
|
||||
I::Movement => return None,
|
||||
};
|
||||
|
||||
dispatched
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
.map(|(a, i)| (scale_ability(a, slot), offhand, spec_ability(i)))
|
||||
})
|
||||
};
|
||||
|
||||
let source = AbilitySource::determine(char_state);
|
||||
|
||||
match ability {
|
||||
Ability::ToolGuard => {
|
||||
Ability::ToolGuard => match source {
|
||||
AbilitySource::Weapons => {
|
||||
let equip_slot = combat::get_equip_slot_by_block_priority(inv);
|
||||
ability_set(equip_slot)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.guard(Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, equip_slot),
|
||||
matches!(equip_slot, EquipSlot::ActiveOffhand),
|
||||
spec_ability(i),
|
||||
)
|
||||
})
|
||||
inst_ability(equip_slot, matches!(equip_slot, EquipSlot::ActiveOffhand))
|
||||
},
|
||||
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.primary(Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, EquipSlot::ActiveMainhand),
|
||||
false,
|
||||
spec_ability(i),
|
||||
)
|
||||
}),
|
||||
Ability::ToolSecondary => ability_set(EquipSlot::ActiveOffhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.secondary(Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, EquipSlot::ActiveOffhand),
|
||||
true,
|
||||
spec_ability(i),
|
||||
)
|
||||
})
|
||||
.or_else(|| {
|
||||
ability_set(EquipSlot::ActiveMainhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.secondary(Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, EquipSlot::ActiveMainhand),
|
||||
false,
|
||||
spec_ability(i),
|
||||
)
|
||||
})
|
||||
}),
|
||||
AbilitySource::Glider => None,
|
||||
},
|
||||
Ability::ToolPrimary => match source {
|
||||
AbilitySource::Weapons => inst_ability(EquipSlot::ActiveMainhand, false),
|
||||
AbilitySource::Glider => inst_ability(EquipSlot::Glider, false),
|
||||
},
|
||||
Ability::ToolSecondary => match source {
|
||||
AbilitySource::Weapons => inst_ability(EquipSlot::ActiveOffhand, true)
|
||||
.or_else(|| inst_ability(EquipSlot::ActiveMainhand, false)),
|
||||
AbilitySource::Glider => inst_ability(EquipSlot::Glider, false),
|
||||
},
|
||||
Ability::MainWeaponAux(_) => inst_ability(EquipSlot::ActiveMainhand, false),
|
||||
Ability::OffWeaponAux(_) => inst_ability(EquipSlot::ActiveOffhand, true),
|
||||
Ability::GliderAux(_) => inst_ability(EquipSlot::Glider, false),
|
||||
Ability::Empty => None,
|
||||
Ability::SpeciesMovement => matches!(body, Some(Body::Humanoid(_)))
|
||||
.then(|| CharacterAbility::default_roll(char_state))
|
||||
.map(|ability| {
|
||||
@ -261,44 +281,17 @@ impl ActiveAbilities {
|
||||
spec_ability(None),
|
||||
)
|
||||
}),
|
||||
Ability::MainWeaponAux(index) => ability_set(EquipSlot::ActiveMainhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.auxiliary(index, Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, EquipSlot::ActiveMainhand),
|
||||
false,
|
||||
spec_ability(i),
|
||||
)
|
||||
}),
|
||||
Ability::OffWeaponAux(index) => ability_set(EquipSlot::ActiveOffhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.auxiliary(index, Some(skill_set), context)
|
||||
.map(|(a, i)| (a.ability.clone(), i))
|
||||
})
|
||||
.map(|(ability, i)| {
|
||||
(
|
||||
scale_ability(ability, EquipSlot::ActiveOffhand),
|
||||
true,
|
||||
spec_ability(i),
|
||||
)
|
||||
}),
|
||||
Ability::Empty => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_available_abilities<'a>(
|
||||
pub fn iter_available_abilities_on<'a>(
|
||||
inv: Option<&'a Inventory>,
|
||||
skill_set: Option<&'a SkillSet>,
|
||||
equip_slot: EquipSlot,
|
||||
) -> impl Iterator<Item = usize> + 'a {
|
||||
inv.and_then(|inv| inv.equipped(equip_slot))
|
||||
inv.and_then(|inv| inv.equipped(equip_slot).and_then(|i| i.item_config()))
|
||||
.into_iter()
|
||||
.flat_map(|i| &i.item_config_expect().abilities.abilities)
|
||||
.flat_map(|config| &config.abilities.abilities)
|
||||
.enumerate()
|
||||
.filter_map(move |(i, a)| match a {
|
||||
AbilityKind::Simple(skill, _) => skill
|
||||
@ -316,15 +309,54 @@ impl ActiveAbilities {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all_available_abilities(
|
||||
inv: Option<&Inventory>,
|
||||
skill_set: Option<&SkillSet>,
|
||||
) -> Vec<AuxiliaryAbility> {
|
||||
let mut ability_buff = vec![];
|
||||
// Check if uses combo of two "equal" weapons
|
||||
let paired = inv
|
||||
.and_then(|inv| {
|
||||
let a = inv.equipped(EquipSlot::ActiveMainhand)?;
|
||||
let b = inv.equipped(EquipSlot::ActiveOffhand)?;
|
||||
|
||||
if let (ItemKind::Tool(tool_a), ItemKind::Tool(tool_b)) = (&*a.kind(), &*b.kind()) {
|
||||
Some((a.ability_spec(), tool_a.kind, b.ability_spec(), tool_b.kind))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.is_some_and(|(a_spec, a_kind, b_spec, b_kind)| (a_spec, a_kind) == (b_spec, b_kind));
|
||||
|
||||
// Push main weapon abilities
|
||||
Self::iter_available_abilities_on(inv, skill_set, EquipSlot::ActiveMainhand)
|
||||
.map(AuxiliaryAbility::MainWeapon)
|
||||
.for_each(|a| ability_buff.push(a));
|
||||
|
||||
// Push secondary weapon abilities, if different
|
||||
// If equal, just take the first
|
||||
if !paired {
|
||||
Self::iter_available_abilities_on(inv, skill_set, EquipSlot::ActiveOffhand)
|
||||
.map(AuxiliaryAbility::OffWeapon)
|
||||
.for_each(|a| ability_buff.push(a));
|
||||
}
|
||||
// Push glider abilities
|
||||
Self::iter_available_abilities_on(inv, skill_set, EquipSlot::Glider)
|
||||
.map(AuxiliaryAbility::Glider)
|
||||
.for_each(|a| ability_buff.push(a));
|
||||
|
||||
ability_buff
|
||||
}
|
||||
|
||||
fn default_ability_set<'a>(
|
||||
inv: Option<&'a Inventory>,
|
||||
skill_set: Option<&'a SkillSet>,
|
||||
limit: Option<usize>,
|
||||
) -> Vec<AuxiliaryAbility> {
|
||||
let mut iter = Self::iter_available_abilities(inv, skill_set, EquipSlot::ActiveMainhand)
|
||||
let mut iter = Self::iter_available_abilities_on(inv, skill_set, EquipSlot::ActiveMainhand)
|
||||
.map(AuxiliaryAbility::MainWeapon)
|
||||
.chain(
|
||||
Self::iter_available_abilities(inv, skill_set, EquipSlot::ActiveOffhand)
|
||||
Self::iter_available_abilities_on(inv, skill_set, EquipSlot::ActiveOffhand)
|
||||
.map(AuxiliaryAbility::OffWeapon),
|
||||
);
|
||||
|
||||
@ -338,6 +370,7 @@ impl ActiveAbilities {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum AbilityInput {
|
||||
Guard,
|
||||
Primary,
|
||||
@ -354,21 +387,42 @@ pub enum Ability {
|
||||
SpeciesMovement,
|
||||
MainWeaponAux(usize),
|
||||
OffWeaponAux(usize),
|
||||
GliderAux(usize),
|
||||
Empty,
|
||||
/* For future use
|
||||
* ArmorAbility(usize), */
|
||||
}
|
||||
|
||||
impl Ability {
|
||||
// Used for generic ability dispatch (inst_ability) in this file
|
||||
//
|
||||
// It does use AbilityInput to avoid creating just another enum, but it is
|
||||
// semantically different.
|
||||
fn try_ability_set_key(&self) -> Option<AbilityInput> {
|
||||
let input = match self {
|
||||
Self::ToolGuard => AbilityInput::Guard,
|
||||
Self::ToolPrimary => AbilityInput::Primary,
|
||||
Self::ToolSecondary => AbilityInput::Secondary,
|
||||
Self::SpeciesMovement => AbilityInput::Movement,
|
||||
Self::GliderAux(idx) | Self::OffWeaponAux(idx) | Self::MainWeaponAux(idx) => {
|
||||
AbilityInput::Auxiliary(*idx)
|
||||
},
|
||||
Self::Empty => return None,
|
||||
};
|
||||
|
||||
Some(input)
|
||||
}
|
||||
|
||||
pub fn ability_id<'a>(
|
||||
self,
|
||||
char_state: Option<&CharacterState>,
|
||||
inv: Option<&'a Inventory>,
|
||||
skillset: Option<&'a SkillSet>,
|
||||
skill_set: Option<&'a SkillSet>,
|
||||
context: &AbilityContext,
|
||||
) -> Option<&'a str> {
|
||||
let ability_set = |equip_slot| {
|
||||
inv.and_then(|inv| inv.equipped(equip_slot))
|
||||
.map(|i| &i.item_config_expect().abilities)
|
||||
.and_then(|i| i.item_config().map(|c| &c.abilities))
|
||||
};
|
||||
|
||||
let contextual_id = |kind: Option<&'a AbilityKind<_>>| -> Option<&'a str> {
|
||||
@ -383,72 +437,75 @@ impl Ability {
|
||||
}
|
||||
};
|
||||
|
||||
match self {
|
||||
Ability::ToolGuard => ability_set(combat::get_equip_slot_by_block_priority(inv))
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.guard(skillset, context)
|
||||
.map(|a| a.0.id.as_str())
|
||||
.or_else(|| {
|
||||
abilities
|
||||
let inst_ability = |slot: EquipSlot| {
|
||||
ability_set(slot).and_then(|abilities| {
|
||||
use AbilityInput as I;
|
||||
|
||||
let dispatched = match self.try_ability_set_key()? {
|
||||
I::Guard => abilities.guard(skill_set, context),
|
||||
I::Primary => abilities.primary(skill_set, context),
|
||||
I::Secondary => abilities.secondary(skill_set, context),
|
||||
I::Auxiliary(index) => abilities.auxiliary(index, skill_set, context),
|
||||
I::Movement => return None,
|
||||
};
|
||||
|
||||
dispatched.map(|(a, _)| a.id.as_str()).or_else(|| {
|
||||
match self.try_ability_set_key()? {
|
||||
I::Guard => abilities
|
||||
.guard
|
||||
.as_ref()
|
||||
.and_then(|g| contextual_id(Some(g)))
|
||||
.and_then(|g| contextual_id(Some(g))),
|
||||
I::Primary => contextual_id(Some(&abilities.primary)),
|
||||
I::Secondary => contextual_id(Some(&abilities.secondary)),
|
||||
I::Auxiliary(index) => contextual_id(abilities.abilities.get(index)),
|
||||
I::Movement => None,
|
||||
}
|
||||
})
|
||||
}),
|
||||
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| {
|
||||
abilities
|
||||
.primary(skillset, context)
|
||||
.map(|a| a.0.id.as_str())
|
||||
.or_else(|| contextual_id(Some(&abilities.primary)))
|
||||
}),
|
||||
Ability::ToolSecondary => ability_set(EquipSlot::ActiveOffhand)
|
||||
.and_then(|abilities| {
|
||||
abilities
|
||||
.secondary(skillset, context)
|
||||
.map(|a| a.0.id.as_str())
|
||||
.or_else(|| contextual_id(Some(&abilities.secondary)))
|
||||
})
|
||||
.or_else(|| {
|
||||
ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| {
|
||||
abilities
|
||||
.secondary(skillset, context)
|
||||
.map(|a| a.0.id.as_str())
|
||||
.or_else(|| contextual_id(Some(&abilities.secondary)))
|
||||
})
|
||||
}),
|
||||
};
|
||||
|
||||
let source = AbilitySource::determine(char_state);
|
||||
match source {
|
||||
AbilitySource::Glider => match self {
|
||||
Ability::ToolGuard => None,
|
||||
Ability::ToolPrimary => inst_ability(EquipSlot::Glider),
|
||||
Ability::ToolSecondary => inst_ability(EquipSlot::Glider),
|
||||
Ability::SpeciesMovement => None, // TODO: Make not None
|
||||
Ability::MainWeaponAux(index) => {
|
||||
ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| {
|
||||
abilities
|
||||
.auxiliary(index, skillset, context)
|
||||
.map(|a| a.0.id.as_str())
|
||||
.or_else(|| contextual_id(abilities.abilities.get(index)))
|
||||
})
|
||||
},
|
||||
Ability::OffWeaponAux(index) => {
|
||||
ability_set(EquipSlot::ActiveOffhand).and_then(|abilities| {
|
||||
abilities
|
||||
.auxiliary(index, skillset, context)
|
||||
.map(|a| a.0.id.as_str())
|
||||
.or_else(|| contextual_id(abilities.abilities.get(index)))
|
||||
})
|
||||
},
|
||||
Ability::MainWeaponAux(_) => inst_ability(EquipSlot::ActiveMainhand),
|
||||
Ability::OffWeaponAux(_) => inst_ability(EquipSlot::ActiveOffhand),
|
||||
Ability::GliderAux(_) => inst_ability(EquipSlot::Glider),
|
||||
Ability::Empty => None,
|
||||
},
|
||||
AbilitySource::Weapons => match self {
|
||||
Ability::ToolGuard => {
|
||||
let equip_slot = combat::get_equip_slot_by_block_priority(inv);
|
||||
inst_ability(equip_slot)
|
||||
},
|
||||
Ability::ToolPrimary => inst_ability(EquipSlot::ActiveMainhand),
|
||||
Ability::ToolSecondary => inst_ability(EquipSlot::ActiveOffhand)
|
||||
.or_else(|| inst_ability(EquipSlot::ActiveMainhand)),
|
||||
Ability::SpeciesMovement => None, // TODO: Make not None
|
||||
Ability::MainWeaponAux(_) => inst_ability(EquipSlot::ActiveMainhand),
|
||||
Ability::OffWeaponAux(_) => inst_ability(EquipSlot::ActiveOffhand),
|
||||
Ability::GliderAux(_) => inst_ability(EquipSlot::Glider),
|
||||
Ability::Empty => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_from_tool(&self) -> bool {
|
||||
pub fn is_from_wielded(&self) -> bool {
|
||||
match self {
|
||||
Ability::ToolPrimary
|
||||
| Ability::ToolSecondary
|
||||
| Ability::MainWeaponAux(_)
|
||||
| Ability::GliderAux(_)
|
||||
| Ability::OffWeaponAux(_)
|
||||
| Ability::ToolGuard => true,
|
||||
Ability::SpeciesMovement | Ability::Empty => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
|
||||
pub enum GuardAbility {
|
||||
Tool,
|
||||
@ -471,10 +528,14 @@ pub struct SpecifiedAbility {
|
||||
}
|
||||
|
||||
impl SpecifiedAbility {
|
||||
pub fn ability_id(self, inv: Option<&Inventory>) -> Option<&str> {
|
||||
pub fn ability_id<'a>(
|
||||
self,
|
||||
char_state: Option<&CharacterState>,
|
||||
inv: Option<&'a Inventory>,
|
||||
) -> Option<&'a str> {
|
||||
let ability_set = |equip_slot| {
|
||||
inv.and_then(|inv| inv.equipped(equip_slot))
|
||||
.map(|i| &i.item_config_expect().abilities)
|
||||
.and_then(|i| i.item_config().map(|c| &c.abilities))
|
||||
};
|
||||
|
||||
fn ability_id(spec_ability: SpecifiedAbility, ability: &AbilityKind<AbilityItem>) -> &str {
|
||||
@ -490,23 +551,44 @@ impl SpecifiedAbility {
|
||||
}
|
||||
}
|
||||
|
||||
match self.ability {
|
||||
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand)
|
||||
.map(|abilities| ability_id(self, &abilities.primary)),
|
||||
Ability::ToolSecondary => ability_set(EquipSlot::ActiveOffhand)
|
||||
.map(|abilities| ability_id(self, &abilities.secondary))
|
||||
.or_else(|| {
|
||||
ability_set(EquipSlot::ActiveMainhand)
|
||||
.map(|abilities| ability_id(self, &abilities.secondary))
|
||||
}),
|
||||
Ability::ToolGuard => ability_set(combat::get_equip_slot_by_block_priority(inv))
|
||||
.and_then(|abilities| abilities.guard.as_ref().map(|a| ability_id(self, a))),
|
||||
Ability::SpeciesMovement => None, // TODO: Make not None
|
||||
Ability::MainWeaponAux(index) => ability_set(EquipSlot::ActiveMainhand)
|
||||
.and_then(|abilities| abilities.abilities.get(index).map(|a| ability_id(self, a))),
|
||||
Ability::OffWeaponAux(index) => ability_set(EquipSlot::ActiveOffhand)
|
||||
.and_then(|abilities| abilities.abilities.get(index).map(|a| ability_id(self, a))),
|
||||
let inst_ability = |slot: EquipSlot| {
|
||||
ability_set(slot).and_then(|abilities| {
|
||||
use AbilityInput as I;
|
||||
|
||||
let dispatched = match self.ability.try_ability_set_key()? {
|
||||
I::Guard => abilities.guard.as_ref(),
|
||||
I::Primary => Some(&abilities.primary),
|
||||
I::Secondary => Some(&abilities.secondary),
|
||||
I::Auxiliary(index) => abilities.abilities.get(index),
|
||||
I::Movement => return None,
|
||||
};
|
||||
dispatched.map(|a| ability_id(self, a))
|
||||
})
|
||||
};
|
||||
|
||||
let source = AbilitySource::determine(char_state);
|
||||
match source {
|
||||
AbilitySource::Glider => match self.ability {
|
||||
Ability::ToolGuard => None,
|
||||
Ability::ToolPrimary => inst_ability(EquipSlot::Glider),
|
||||
Ability::ToolSecondary => inst_ability(EquipSlot::Glider),
|
||||
Ability::SpeciesMovement => None,
|
||||
Ability::MainWeaponAux(_) => inst_ability(EquipSlot::ActiveMainhand),
|
||||
Ability::OffWeaponAux(_) => inst_ability(EquipSlot::ActiveOffhand),
|
||||
Ability::GliderAux(_) => inst_ability(EquipSlot::Glider),
|
||||
Ability::Empty => None,
|
||||
},
|
||||
AbilitySource::Weapons => match self.ability {
|
||||
Ability::ToolGuard => inst_ability(combat::get_equip_slot_by_block_priority(inv)),
|
||||
Ability::ToolPrimary => inst_ability(EquipSlot::ActiveMainhand),
|
||||
Ability::ToolSecondary => inst_ability(EquipSlot::ActiveOffhand)
|
||||
.or_else(|| inst_ability(EquipSlot::ActiveMainhand)),
|
||||
Ability::SpeciesMovement => None, // TODO: Make not None
|
||||
Ability::MainWeaponAux(_) => inst_ability(EquipSlot::ActiveMainhand),
|
||||
Ability::OffWeaponAux(_) => inst_ability(EquipSlot::ActiveOffhand),
|
||||
Ability::GliderAux(_) => inst_ability(EquipSlot::Glider),
|
||||
Ability::Empty => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -560,6 +642,7 @@ impl From<MovementAbility> for Ability {
|
||||
pub enum AuxiliaryAbility {
|
||||
MainWeapon(usize),
|
||||
OffWeapon(usize),
|
||||
Glider(usize),
|
||||
Empty,
|
||||
}
|
||||
|
||||
@ -568,6 +651,7 @@ impl From<AuxiliaryAbility> for Ability {
|
||||
match primary {
|
||||
AuxiliaryAbility::MainWeapon(i) => Ability::MainWeaponAux(i),
|
||||
AuxiliaryAbility::OffWeapon(i) => Ability::OffWeaponAux(i),
|
||||
AuxiliaryAbility::Glider(i) => Ability::GliderAux(i),
|
||||
AuxiliaryAbility::Empty => Ability::Empty,
|
||||
}
|
||||
}
|
||||
@ -708,6 +792,11 @@ pub enum CharacterAbility {
|
||||
#[serde(default)]
|
||||
meta: AbilityMeta,
|
||||
},
|
||||
GlideBoost {
|
||||
booster: glide::Boost,
|
||||
#[serde(default)]
|
||||
meta: AbilityMeta,
|
||||
},
|
||||
DashMelee {
|
||||
energy_cost: f32,
|
||||
energy_drain: f32,
|
||||
@ -1125,6 +1214,7 @@ impl CharacterAbility {
|
||||
},
|
||||
CharacterAbility::ComboMeleeDeprecated { .. }
|
||||
| CharacterAbility::Boost { .. }
|
||||
| CharacterAbility::GlideBoost { .. }
|
||||
| CharacterAbility::BasicBeam { .. }
|
||||
| CharacterAbility::Blink { .. }
|
||||
| CharacterAbility::Music { .. }
|
||||
@ -1688,6 +1778,7 @@ impl CharacterAbility {
|
||||
*buildup_duration /= stats.speed;
|
||||
*recover_duration /= stats.speed;
|
||||
},
|
||||
GlideBoost { .. } => {},
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -1724,6 +1815,7 @@ impl CharacterAbility {
|
||||
}
|
||||
},
|
||||
Boost { .. }
|
||||
| GlideBoost { .. }
|
||||
| ComboMeleeDeprecated { .. }
|
||||
| Blink { .. }
|
||||
| Music { .. }
|
||||
@ -1773,6 +1865,7 @@ impl CharacterAbility {
|
||||
| RiposteMelee { .. }
|
||||
| BasicBeam { .. }
|
||||
| Boost { .. }
|
||||
| GlideBoost { .. }
|
||||
| ComboMeleeDeprecated { .. }
|
||||
| Blink { .. }
|
||||
| Music { .. }
|
||||
@ -1801,6 +1894,7 @@ impl CharacterAbility {
|
||||
| SelfBuff { meta, .. }
|
||||
| BasicBeam { meta, .. }
|
||||
| Boost { meta, .. }
|
||||
| GlideBoost { meta, .. }
|
||||
| ComboMeleeDeprecated { meta, .. }
|
||||
| ComboMelee2 { meta, .. }
|
||||
| Blink { meta, .. }
|
||||
@ -2312,6 +2406,13 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
},
|
||||
timer: Duration::default(),
|
||||
}),
|
||||
CharacterAbility::GlideBoost { booster, meta: _ } => {
|
||||
let scale = data.body.dimensions().z.sqrt();
|
||||
let mut glide_data = glide::Data::new(scale * 4.5, scale, *data.ori);
|
||||
glide_data.booster = Some(*booster);
|
||||
|
||||
CharacterState::Glide(glide_data)
|
||||
},
|
||||
CharacterAbility::DashMelee {
|
||||
energy_cost: _,
|
||||
energy_drain,
|
||||
|
@ -228,6 +228,13 @@ impl CharacterState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_glide_wielded(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
CharacterState::Glide { .. } | CharacterState::GlideWield { .. }
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_stealthy(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
|
@ -751,7 +751,8 @@ impl TryFrom<(&Item, &AbilityMap, &MaterialStatManifest)> for ItemConfig {
|
||||
// TODO: Either remove msm or use it as argument in fn kind
|
||||
(item, ability_map, _msm): (&Item, &AbilityMap, &MaterialStatManifest),
|
||||
) -> Result<Self, Self::Error> {
|
||||
if let ItemKind::Tool(tool) = &*item.kind() {
|
||||
match &*item.kind() {
|
||||
ItemKind::Tool(tool) => {
|
||||
// If no custom ability set is specified, fall back to abilityset of tool kind.
|
||||
let tool_default = |tool_kind| {
|
||||
let key = &AbilitySpec::Tool(tool_kind);
|
||||
@ -763,8 +764,8 @@ impl TryFrom<(&Item, &AbilityMap, &MaterialStatManifest)> for ItemConfig {
|
||||
.modified_by_tool(tool, item.stats_durability_multiplier())
|
||||
} else {
|
||||
error!(
|
||||
"Custom ability set: {:?} references non-existent set, falling back to \
|
||||
default ability set.",
|
||||
"Custom ability set: {:?} references non-existent set, falling back \
|
||||
to default ability set.",
|
||||
set_key
|
||||
);
|
||||
tool_default(tool.kind).cloned().unwrap_or_default()
|
||||
@ -774,15 +775,23 @@ impl TryFrom<(&Item, &AbilityMap, &MaterialStatManifest)> for ItemConfig {
|
||||
.modified_by_tool(tool, item.stats_durability_multiplier())
|
||||
} else {
|
||||
error!(
|
||||
"No ability set defined for tool: {:?}, falling back to default ability set.",
|
||||
"No ability set defined for tool: {:?}, falling back to default ability \
|
||||
set.",
|
||||
tool.kind
|
||||
);
|
||||
Default::default()
|
||||
};
|
||||
|
||||
Ok(ItemConfig { abilities })
|
||||
} else {
|
||||
Err(ItemConfigError::BadItemKind)
|
||||
},
|
||||
ItemKind::Glider => item
|
||||
.ability_spec()
|
||||
.and_then(|set_key| ability_map.get_ability_set(&set_key))
|
||||
.map(|abilities| ItemConfig {
|
||||
abilities: abilities.clone(),
|
||||
})
|
||||
.ok_or(ItemConfigError::BadItemKind),
|
||||
_ => Err(ItemConfigError::BadItemKind),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1314,11 +1323,7 @@ impl Item {
|
||||
|
||||
pub fn slots_mut(&mut self) -> &mut [InvSlot] { &mut self.slots }
|
||||
|
||||
pub fn item_config_expect(&self) -> &ItemConfig {
|
||||
self.item_config
|
||||
.as_ref()
|
||||
.expect("Item was expected to have an ItemConfig")
|
||||
}
|
||||
pub fn item_config(&self) -> Option<&ItemConfig> { self.item_config.as_deref() }
|
||||
|
||||
pub fn free_slots(&self) -> usize { self.slots.iter().filter(|x| x.is_none()).count() }
|
||||
|
||||
|
@ -37,14 +37,13 @@ pub enum ToolKind {
|
||||
Farming,
|
||||
Pick,
|
||||
Shovel,
|
||||
/// Music Instruments
|
||||
Instrument,
|
||||
// npcs
|
||||
/// Intended for invisible weapons (e.g. a creature using its claws or
|
||||
/// biting)
|
||||
Natural,
|
||||
/// This is an placeholder item, it is used by non-humanoid npcs to attack
|
||||
/// Music Instruments
|
||||
Instrument,
|
||||
/// This is an placeholder item, it is used by non-humanoid npcs to attack
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,14 @@ use vek::*;
|
||||
|
||||
const PITCH_SLOW_TIME: f32 = 0.5;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Boost {
|
||||
/// Slowly increases XY speed
|
||||
Forward(f32),
|
||||
/// Gives Z impulse
|
||||
Upward(f32),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Data {
|
||||
/// The aspect ratio is the ratio of the span squared to actual planform
|
||||
@ -28,6 +36,7 @@ pub struct Data {
|
||||
last_vel: Vel,
|
||||
pub timer: Duration,
|
||||
inputs_disabled: bool,
|
||||
pub booster: Option<Boost>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
@ -47,6 +56,7 @@ impl Data {
|
||||
last_vel: Vel::zero(),
|
||||
timer: Duration::default(),
|
||||
inputs_disabled: true,
|
||||
booster: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,12 +85,28 @@ impl Data {
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
// reset booster
|
||||
update.character = CharacterState::Glide(Self {
|
||||
booster: None,
|
||||
..*self
|
||||
});
|
||||
|
||||
// If player is on ground, end glide
|
||||
let gained_booster = self.booster;
|
||||
|
||||
handle_glider_input_or(data, &mut update, output_events, |_, _| {});
|
||||
|
||||
// If switched state, let it do its thing
|
||||
if !matches!(update.character, CharacterState::Glide { .. }) {
|
||||
return update;
|
||||
}
|
||||
|
||||
// If player is on the ground and effectively doesn't have any gliding
|
||||
// power left, end the glide
|
||||
if data.physics.on_ground.is_some()
|
||||
&& (data.vel.0 - data.physics.ground_vel).magnitude_squared() < 2_f32.powi(2)
|
||||
&& gained_booster.is_none()
|
||||
{
|
||||
update.character = CharacterState::GlideWield(glide_wield::Data::from(data));
|
||||
} else if data.physics.in_liquid().is_some()
|
||||
@ -189,11 +215,44 @@ impl CharacterBehavior for Data {
|
||||
slerp_s,
|
||||
)
|
||||
};
|
||||
|
||||
// If we gained a booster
|
||||
if let Some(booster) = gained_booster {
|
||||
match booster {
|
||||
Boost::Upward(speed) => {
|
||||
update.vel.0.z += speed * data.dt.0;
|
||||
},
|
||||
Boost::Forward(speed) => {
|
||||
if data.physics.on_ground.is_some() {
|
||||
// quality of life hack: help with starting
|
||||
//
|
||||
// other velocities are intentionally ignored
|
||||
update.vel.0.z += 500.0 * data.dt.0;
|
||||
} else {
|
||||
update.vel.0.x *= 1.0 + speed * data.dt.0;
|
||||
update.vel.0.y *= 1.0 + speed * data.dt.0;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Don't override gained booster, if any, otherwise set to None
|
||||
let next_booster = if let CharacterState::Glide(Data {
|
||||
booster: Some(booster),
|
||||
..
|
||||
}) = update.character
|
||||
{
|
||||
Some(booster)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
update.character = CharacterState::Glide(Self {
|
||||
ori,
|
||||
last_vel: *data.vel,
|
||||
timer: tick_attack_or_default(data, self.timer, None),
|
||||
inputs_disabled,
|
||||
booster: next_booster,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ impl CharacterBehavior for Data {
|
||||
if input_is_pressed(data, InputKind::Roll) {
|
||||
handle_input(data, output_events, &mut update, InputKind::Roll);
|
||||
}
|
||||
handle_wield(data, &mut update);
|
||||
handle_glider_input_or(data, &mut update, output_events, handle_wield);
|
||||
|
||||
// If still in this state, do the things
|
||||
if matches!(update.character, CharacterState::GlideWield(_)) {
|
||||
|
@ -95,7 +95,7 @@ impl CharacterBehavior for Data {
|
||||
.static_data
|
||||
.ability_info
|
||||
.ability
|
||||
.map_or(false, |a| a.ability.is_from_tool())
|
||||
.map_or(false, |a| a.ability.is_from_wielded())
|
||||
{
|
||||
vec![BuffCategory::RemoveOnLoadoutChange]
|
||||
} else {
|
||||
|
@ -1330,6 +1330,30 @@ pub fn handle_input(
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Quality of Life hack
|
||||
//
|
||||
// Uses glider ability if has any, otherwise fallback
|
||||
pub fn handle_glider_input_or(
|
||||
data: &JoinData<'_>,
|
||||
update: &mut StateUpdate,
|
||||
output_events: &mut OutputEvents,
|
||||
fallback_fn: fn(&JoinData<'_>, &mut StateUpdate),
|
||||
) {
|
||||
if data
|
||||
.inventory
|
||||
.and_then(|inv| inv.equipped(EquipSlot::Glider))
|
||||
.and_then(|glider| glider.item_config())
|
||||
.is_none()
|
||||
{
|
||||
fallback_fn(data, update);
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(input) = data.controller.queued_inputs.keys().next() {
|
||||
handle_ability(data, update, output_events, *input);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn attempt_input(
|
||||
data: &JoinData<'_>,
|
||||
output_events: &mut OutputEvents,
|
||||
|
@ -162,8 +162,8 @@ impl<'a> System<'a> for Sys {
|
||||
CharacterState::Idle(_)
|
||||
| CharacterState::Talk
|
||||
| CharacterState::Dance
|
||||
| CharacterState::Glide(_)
|
||||
| CharacterState::Skate(_)
|
||||
| CharacterState::Glide(_)
|
||||
| CharacterState::GlideWield(_)
|
||||
| CharacterState::Wielding(_)
|
||||
| CharacterState::Equipping(_)
|
||||
|
@ -127,6 +127,7 @@ fn aux_ability_to_string(ability: comp::ability::AuxiliaryAbility) -> String {
|
||||
match ability {
|
||||
AuxiliaryAbility::MainWeapon(index) => format!("Main Weapon:index:{}", index),
|
||||
AuxiliaryAbility::OffWeapon(index) => format!("Off Weapon:index:{}", index),
|
||||
AuxiliaryAbility::Glider(index) => format!("Glider:index:{}", index),
|
||||
AuxiliaryAbility::Empty => String::from("Empty"),
|
||||
}
|
||||
}
|
||||
@ -177,6 +178,27 @@ fn aux_ability_from_string(ability: &str) -> comp::ability::AuxiliaryAbility {
|
||||
AuxiliaryAbility::Empty
|
||||
},
|
||||
},
|
||||
Some("Glider") => match parts
|
||||
.next()
|
||||
.map(|index| index.parse::<usize>().map_err(|_| index))
|
||||
{
|
||||
Some(Ok(index)) => AuxiliaryAbility::Glider(index),
|
||||
Some(Err(error)) => {
|
||||
dev_panic!(format!(
|
||||
"Conversion from database to ability set failed. Unable to parse index for \
|
||||
offhand abilities: {}",
|
||||
error
|
||||
));
|
||||
AuxiliaryAbility::Empty
|
||||
},
|
||||
None => {
|
||||
dev_panic!(String::from(
|
||||
"Conversion from database to ability set failed. Unable to find an index for \
|
||||
offhand abilities"
|
||||
));
|
||||
AuxiliaryAbility::Empty
|
||||
},
|
||||
},
|
||||
Some("Empty") => AuxiliaryAbility::Empty,
|
||||
unknown => {
|
||||
dev_panic!(format!(
|
||||
|
@ -37,7 +37,7 @@ use common::{
|
||||
RollSkill, SceptreSkill, Skill, StaffSkill, SwimSkill, SwordSkill, SKILL_MODIFIERS,
|
||||
},
|
||||
skillset::{SkillGroupKind, SkillSet},
|
||||
Body, Energy, Health, Inventory, Poise,
|
||||
Body, CharacterState, Energy, Health, Inventory, Poise,
|
||||
},
|
||||
consts::{ENERGY_PER_LEVEL, HP_PER_LEVEL},
|
||||
};
|
||||
@ -212,6 +212,7 @@ pub struct Diary<'a> {
|
||||
skill_set: &'a SkillSet,
|
||||
active_abilities: &'a ActiveAbilities,
|
||||
inventory: &'a Inventory,
|
||||
char_state: &'a CharacterState,
|
||||
health: &'a Health,
|
||||
energy: &'a Energy,
|
||||
poise: &'a Poise,
|
||||
@ -258,6 +259,7 @@ impl<'a> Diary<'a> {
|
||||
skill_set: &'a SkillSet,
|
||||
active_abilities: &'a ActiveAbilities,
|
||||
inventory: &'a Inventory,
|
||||
char_state: &'a CharacterState,
|
||||
health: &'a Health,
|
||||
energy: &'a Energy,
|
||||
poise: &'a Poise,
|
||||
@ -280,6 +282,7 @@ impl<'a> Diary<'a> {
|
||||
skill_set,
|
||||
active_abilities,
|
||||
inventory,
|
||||
char_state,
|
||||
health,
|
||||
energy,
|
||||
poise,
|
||||
@ -838,6 +841,7 @@ impl<'a> Widget for Diary<'a> {
|
||||
self.inventory,
|
||||
self.skill_set,
|
||||
self.context,
|
||||
Some(self.char_state),
|
||||
),
|
||||
image_source: self.imgs,
|
||||
slot_manager: Some(self.slot_manager),
|
||||
@ -852,7 +856,12 @@ impl<'a> Widget for Diary<'a> {
|
||||
Some(self.inventory),
|
||||
Some(self.skill_set),
|
||||
)
|
||||
.ability_id(Some(self.inventory), Some(self.skill_set), self.context);
|
||||
.ability_id(
|
||||
Some(self.char_state),
|
||||
Some(self.inventory),
|
||||
Some(self.skill_set),
|
||||
self.context,
|
||||
);
|
||||
let (ability_title, ability_desc) = if let Some(ability_id) = ability_id {
|
||||
util::ability_description(ability_id, self.localized_strings)
|
||||
} else {
|
||||
@ -912,59 +921,23 @@ impl<'a> Widget for Diary<'a> {
|
||||
.set(state.ids.active_abilities_keys[i], ui);
|
||||
}
|
||||
|
||||
let same_weap_kinds = self
|
||||
.inventory
|
||||
.equipped(EquipSlot::ActiveMainhand)
|
||||
.zip(self.inventory.equipped(EquipSlot::ActiveOffhand))
|
||||
.map_or(false, |(a, b)| {
|
||||
if let (ItemKind::Tool(tool_a), ItemKind::Tool(tool_b)) =
|
||||
(&*a.kind(), &*b.kind())
|
||||
{
|
||||
(a.ability_spec(), tool_a.kind) == (b.ability_spec(), tool_b.kind)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
let main_weap_abilities = ActiveAbilities::iter_available_abilities(
|
||||
let abilities: Vec<_> = ActiveAbilities::all_available_abilities(
|
||||
Some(self.inventory),
|
||||
Some(self.skill_set),
|
||||
EquipSlot::ActiveMainhand,
|
||||
)
|
||||
.map(AuxiliaryAbility::MainWeapon)
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
(
|
||||
Ability::from(a).ability_id(
|
||||
Some(self.char_state),
|
||||
Some(self.inventory),
|
||||
Some(self.skill_set),
|
||||
self.context,
|
||||
),
|
||||
a,
|
||||
)
|
||||
});
|
||||
let off_weap_abilities = ActiveAbilities::iter_available_abilities(
|
||||
Some(self.inventory),
|
||||
Some(self.skill_set),
|
||||
EquipSlot::ActiveOffhand,
|
||||
)
|
||||
.map(AuxiliaryAbility::OffWeapon)
|
||||
.map(|a| {
|
||||
(
|
||||
Ability::from(a).ability_id(
|
||||
Some(self.inventory),
|
||||
Some(self.skill_set),
|
||||
self.context,
|
||||
),
|
||||
a,
|
||||
)
|
||||
});
|
||||
|
||||
let abilities: Vec<_> = if same_weap_kinds {
|
||||
// When the weapons have the same ability kind take only the main weapons,
|
||||
main_weap_abilities.collect()
|
||||
} else {
|
||||
main_weap_abilities.chain(off_weap_abilities).collect()
|
||||
};
|
||||
})
|
||||
.collect();
|
||||
|
||||
const ABILITIES_PER_PAGE: usize = 12;
|
||||
|
||||
@ -1065,12 +1038,27 @@ impl<'a> Widget for Diary<'a> {
|
||||
self.inventory,
|
||||
self.skill_set,
|
||||
self.context,
|
||||
Some(self.char_state),
|
||||
),
|
||||
image_source: self.imgs,
|
||||
slot_manager: Some(self.slot_manager),
|
||||
pulse: 0.0,
|
||||
};
|
||||
|
||||
let same_weap_kinds = self
|
||||
.inventory
|
||||
.equipped(EquipSlot::ActiveMainhand)
|
||||
.zip(self.inventory.equipped(EquipSlot::ActiveOffhand))
|
||||
.map_or(false, |(a, b)| {
|
||||
if let (ItemKind::Tool(tool_a), ItemKind::Tool(tool_b)) =
|
||||
(&*a.kind(), &*b.kind())
|
||||
{
|
||||
(a.ability_spec(), tool_a.kind) == (b.ability_spec(), tool_b.kind)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
for (id_index, (ability_id, ability)) in abilities
|
||||
.iter()
|
||||
.skip(ability_start)
|
||||
|
@ -3684,6 +3684,7 @@ impl Hud {
|
||||
if let (
|
||||
Some(skill_set),
|
||||
Some(inventory),
|
||||
Some(char_state),
|
||||
Some(health),
|
||||
Some(energy),
|
||||
Some(body),
|
||||
@ -3691,6 +3692,7 @@ impl Hud {
|
||||
) = (
|
||||
skill_sets.get(entity),
|
||||
inventories.get(entity),
|
||||
char_states.get(entity),
|
||||
healths.get(entity),
|
||||
energies.get(entity),
|
||||
bodies.get(entity),
|
||||
@ -3704,6 +3706,7 @@ impl Hud {
|
||||
skill_set,
|
||||
active_abilities.get(entity).unwrap_or(&Default::default()),
|
||||
inventory,
|
||||
char_state,
|
||||
health,
|
||||
energy,
|
||||
poise,
|
||||
|
@ -1043,6 +1043,7 @@ impl<'a> Skillbar<'a> {
|
||||
.get(i)
|
||||
.and_then(|a| {
|
||||
Ability::from(*a).ability_id(
|
||||
self.char_state,
|
||||
Some(inventory),
|
||||
Some(skill_set),
|
||||
contexts,
|
||||
@ -1118,6 +1119,7 @@ impl<'a> Skillbar<'a> {
|
||||
|
||||
let primary_ability_id = self.active_abilities.and_then(|a| {
|
||||
Ability::from(a.primary).ability_id(
|
||||
self.char_state,
|
||||
Some(self.inventory),
|
||||
Some(self.skillset),
|
||||
self.context,
|
||||
@ -1148,6 +1150,7 @@ impl<'a> Skillbar<'a> {
|
||||
|
||||
let secondary_ability_id = self.active_abilities.and_then(|a| {
|
||||
Ability::from(a.secondary).ability_id(
|
||||
self.char_state,
|
||||
Some(self.inventory),
|
||||
Some(self.skillset),
|
||||
self.context,
|
||||
|
@ -168,7 +168,12 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
|
||||
a.auxiliary_set(Some(inventory), Some(skillset))
|
||||
.get(i)
|
||||
.and_then(|a| {
|
||||
Ability::from(*a).ability_id(Some(inventory), Some(skillset), contexts)
|
||||
Ability::from(*a).ability_id(
|
||||
*char_state,
|
||||
Some(inventory),
|
||||
Some(skillset),
|
||||
contexts,
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
@ -241,6 +246,7 @@ type AbilitiesSource<'a> = (
|
||||
&'a Inventory,
|
||||
&'a SkillSet,
|
||||
&'a AbilityContext,
|
||||
Option<&'a CharacterState>,
|
||||
);
|
||||
|
||||
impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
|
||||
@ -248,7 +254,7 @@ impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
|
||||
|
||||
fn image_key(
|
||||
&self,
|
||||
(active_abilities, inventory, skillset, contexts): &AbilitiesSource<'a>,
|
||||
(active_abilities, inventory, skillset, contexts, char_state): &AbilitiesSource<'a>,
|
||||
) -> Option<(Self::ImageKey, Option<Color>)> {
|
||||
let ability_id = match self {
|
||||
Self::Slot(index) => active_abilities
|
||||
@ -257,10 +263,13 @@ impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
|
||||
Some(inventory),
|
||||
Some(skillset),
|
||||
)
|
||||
.ability_id(Some(inventory), Some(skillset), contexts),
|
||||
Self::Ability(ability) => {
|
||||
Ability::from(*ability).ability_id(Some(inventory), Some(skillset), contexts)
|
||||
},
|
||||
.ability_id(*char_state, Some(inventory), Some(skillset), contexts),
|
||||
Self::Ability(ability) => Ability::from(*ability).ability_id(
|
||||
*char_state,
|
||||
Some(inventory),
|
||||
Some(skillset),
|
||||
contexts,
|
||||
),
|
||||
};
|
||||
|
||||
ability_id.map(|id| (String::from(id), None))
|
||||
|
@ -590,6 +590,9 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
|
||||
"common.abilities.music.washboard" => imgs.instrument,
|
||||
"common.abilities.music.steeltonguedrum" => imgs.instrument,
|
||||
"common.abilities.music.shamisen" => imgs.instrument,
|
||||
// Glider
|
||||
"common.abilities.debug.glide_boost" => imgs.flyingrod_m2,
|
||||
"common.abilities.debug.glide_speeder" => imgs.flyingrod_m1,
|
||||
_ => imgs.not_found,
|
||||
}
|
||||
}
|
||||
|
@ -1062,7 +1062,7 @@ impl FigureMgr {
|
||||
let ability_id = character.and_then(|c| {
|
||||
c.ability_info()
|
||||
.and_then(|a| a.ability)
|
||||
.and_then(|a| a.ability_id(inventory))
|
||||
.and_then(|a| a.ability_id(Some(c), inventory))
|
||||
});
|
||||
|
||||
let move_dir = {
|
||||
|
Loading…
Reference in New Issue
Block a user