This commit is contained in:
Imbris 2023-03-11 07:59:30 -05:00
parent e8a5de4e65
commit b644ff7668
47 changed files with 373 additions and 251 deletions

View File

@ -80,7 +80,13 @@ impl Clock {
if self.last_dts.len() >= NUMBER_OF_DELTAS_COMPARED {
// Take the median of the last few tick times
let mut dts = [0.0; NUMBER_OF_DELTAS_COMPARED];
for (i, dt) in self.last_dts.iter().rev().take(NUMBER_OF_DELTAS_COMPARED).enumerate() {
for (i, dt) in self
.last_dts
.iter()
.rev()
.take(NUMBER_OF_DELTAS_COMPARED)
.enumerate()
{
dts[i] = **dt;
}
dts.sort_by_key(|x| ordered_float::OrderedFloat(*x));

View File

@ -824,9 +824,7 @@ impl ServerChatCommand {
}
/// Produce an iterator over all the available commands
pub fn iter() -> impl Iterator<Item = Self> + Clone {
<Self as IntoEnumIterator>::iter()
}
pub fn iter() -> impl Iterator<Item = Self> + Clone { <Self as IntoEnumIterator>::iter() }
/// A message that explains what the command does
pub fn help_string(&self) -> String {

View File

@ -1,4 +1,4 @@
use crate::{combat::DamageContributor, comp, uid::Uid, DamageSource, terrain::SpriteKind};
use crate::{combat::DamageContributor, comp, terrain::SpriteKind, uid::Uid, DamageSource};
use comp::{beam, item::Reagent, poise::PoiseState, skillset::SkillGroupKind, UtteranceKind};
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};

View File

@ -77,7 +77,6 @@ where
let max = (self.to - self.from).magnitude();
for _ in 0..self.max_iter {
// Allow one iteration above max.
if dist > max {
break;

View File

@ -110,7 +110,6 @@ impl SkillSetBuilder {
/// 2) If added skill already applied
/// 3) If added skill wasn't applied at the end
pub fn with_skill(mut self, skill: Skill, level: u16) -> Self {
let Some(group) = skill.skill_group_kind() else {
let err = format!(
"Tried to add skill: {:?} which does not have an associated skill group.",

View File

@ -96,7 +96,11 @@ impl CharacterBehavior for Data {
}
},
StageSection::Action => {
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
&& (self.static_data.energy_drain <= f32::EPSILON
|| update.energy.current() > 0.0)
{

View File

@ -1,9 +1,7 @@
use super::utils::*;
use crate::{
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
},
states::behavior::{CharacterBehavior, JoinData},
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -76,7 +74,11 @@ impl CharacterBehavior for Data {
},
StageSection::Action => {
if self.static_data.can_hold
&& self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
&& self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
// Block
update.character = CharacterState::BasicBlock(Data {

View File

@ -118,7 +118,12 @@ impl CharacterBehavior for Data {
});
} else {
// Done
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
reset_state(self, data, output_events, &mut update);
} else {
end_melee_ability(data, &mut update);
@ -145,11 +150,6 @@ fn reset_state(
update: &mut StateUpdate,
) {
if let Some(input) = data.static_data.ability_info.input {
handle_input(
join,
output_events,
update,
input,
);
handle_input(join, output_events, update, input);
}
}

View File

@ -1,9 +1,9 @@
use crate::{
combat::CombatEffect,
comp::{
character_state::OutputEvents, Body, CharacterState, LightEmitter, Pos,
ProjectileConstructor, StateUpdate,
},
combat::CombatEffect,
event::ServerEvent,
states::{
behavior::{CharacterBehavior, JoinData},
@ -127,7 +127,12 @@ impl CharacterBehavior for Data {
});
} else {
// Done
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
reset_state(self, data, output_events, &mut update);
} else {
end_ability(data, &mut update);
@ -154,11 +159,6 @@ fn reset_state(
update: &mut StateUpdate,
) {
if let Some(input) = data.static_data.ability_info.input {
handle_input(
join,
output_events,
update,
input,
);
handle_input(join, output_events, update, input);
}
}

View File

@ -1,9 +1,11 @@
use crate::{
comp::{
self, character_state::OutputEvents, item::{tool::AbilityMap, MaterialStatManifest}, ActiveAbilities, Beam,
Body, CharacterState, Combo, ControlAction, Controller, ControllerInputs, Density, Energy,
Health, InputAttr, InputKind, Inventory, InventoryAction, Mass, Melee, Ori, PhysicsState,
Pos, SkillSet, StateUpdate, Stats, Vel,
self,
character_state::OutputEvents,
item::{tool::AbilityMap, MaterialStatManifest},
ActiveAbilities, Beam, Body, CharacterState, Combo, ControlAction, Controller,
ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory,
InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, SkillSet, StateUpdate, Stats, Vel,
},
link::Is,
mounting::Rider,
@ -56,8 +58,8 @@ pub trait CharacterBehavior {
fn talk(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
StateUpdate::from(data)
}
// start_input has custom implementation in the following character states that may also need to be modified when changes are made here:
// ComboMelee2
// start_input has custom implementation in the following character states that
// may also need to be modified when changes are made here: ComboMelee2
fn start_input(
&self,
data: &JoinData,

View File

@ -46,7 +46,12 @@ impl CharacterBehavior for Data {
});
} else {
// Done
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
reset_state(self, data, output_events, &mut update);
} else {
update.vel.0 = update.vel.0.try_normalized().unwrap_or_default()
@ -70,11 +75,6 @@ fn reset_state(
update: &mut StateUpdate,
) {
if let Some(input) = data.static_data.ability_info.input {
handle_input(
join,
output_events,
update,
input,
);
handle_input(join, output_events, update, input);
}
}

View File

@ -1,6 +1,6 @@
use crate::{
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
combat::CombatEffect,
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
event::LocalEvent,
outcome::Outcome,
states::{
@ -61,7 +61,11 @@ impl CharacterBehavior for Data {
match self.stage_section {
StageSection::Charge => {
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
&& update.energy.current() >= self.static_data.energy_cost
&& self.timer < self.static_data.charge_duration
{
@ -80,7 +84,11 @@ impl CharacterBehavior for Data {
update
.energy
.change_by(-self.static_data.energy_drain * data.dt.0);
} else if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
} else if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
&& update.energy.current() >= self.static_data.energy_cost
{
// Maintains charge

View File

@ -1,9 +1,9 @@
use crate::{
combat::CombatEffect,
comp::{
character_state::OutputEvents, projectile::ProjectileConstructor, Body, CharacterState,
LightEmitter, Pos, StateUpdate,
},
combat::CombatEffect,
event::ServerEvent,
states::{
behavior::{CharacterBehavior, JoinData},
@ -99,7 +99,13 @@ impl CharacterBehavior for Data {
}
},
StageSection::Charge => {
if !self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) && !self.exhausted {
if !self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
&& !self.exhausted
{
let charge_frac = self.charge_frac();
let arrow = ProjectileConstructor::Arrow {
damage: self.static_data.initial_damage
@ -142,7 +148,11 @@ impl CharacterBehavior for Data {
..*self
});
} else if self.timer < self.static_data.charge_duration
&& self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
&& self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
// Charges
update.character = CharacterState::ChargedRanged(Data {
@ -154,7 +164,12 @@ impl CharacterBehavior for Data {
update
.energy
.change_by(-self.static_data.energy_drain * data.dt.0);
} else if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
} else if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
// Holds charge
update.character = CharacterState::ChargedRanged(Data {
timer: tick_attack_or_default(data, self.timer, None),

View File

@ -2,7 +2,8 @@ use crate::{
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
comp::{
character_state::OutputEvents,
tool::{Stats, ToolKind}, melee::MultiTarget,
melee::MultiTarget,
tool::{Stats, ToolKind},
CharacterState, Melee, StateUpdate,
},
states::{
@ -341,7 +342,12 @@ impl CharacterBehavior for Data {
});
} else {
// Done
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
reset_state(self, data, output_events, &mut update);
} else {
end_melee_ability(data, &mut update);
@ -362,12 +368,11 @@ impl CharacterBehavior for Data {
}
impl Data {
/// Index should be `self.stage - 1`, however in cases of client-server desync
/// this can cause panics. This ensures that `self.stage - 1` is valid, and if it
/// isn't, index of 0 is used, which is always safe.
/// Index should be `self.stage - 1`, however in cases of client-server
/// desync this can cause panics. This ensures that `self.stage - 1` is
/// valid, and if it isn't, index of 0 is used, which is always safe.
pub fn stage_index(&self) -> usize {
self
.static_data
self.static_data
.stage_data
.get(self.stage as usize - 1)
.map_or(0, |_| self.stage as usize - 1)
@ -381,12 +386,7 @@ fn reset_state(
update: &mut StateUpdate,
) {
if let Some(input) = data.static_data.ability_info.input {
handle_input(
join,
output_events,
update,
input,
);
handle_input(join, output_events, update, input);
if let CharacterState::ComboMelee(c) = &mut update.character {
c.stage = (data.stage % data.static_data.num_stages) + 1;

View File

@ -1,11 +1,15 @@
use crate::{
comp::{
character_state::OutputEvents, tool::Stats, CharacterState, InputKind,
MeleeConstructor, StateUpdate, InputAttr, InventoryAction, slot::{Slot, EquipSlot},
character_state::OutputEvents,
slot::{EquipSlot, Slot},
tool::Stats,
CharacterState, InputAttr, InputKind, InventoryAction, MeleeConstructor, StateUpdate,
},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*, idle, wielding,
idle,
utils::*,
wielding,
},
uid::Uid,
};
@ -117,7 +121,10 @@ impl CharacterBehavior for Data {
let ability_input = if self.static_data.is_stance {
InputKind::Primary
} else {
self.static_data.ability_info.input.unwrap_or(InputKind::Primary)
self.static_data
.ability_info
.input
.unwrap_or(InputKind::Primary)
};
handle_orientation(data, &mut update, 1.0, None);
@ -133,7 +140,8 @@ impl CharacterBehavior for Data {
match self.stage_section {
Some(StageSection::Ready) => {
// Adds a small duration to entering a stance to discourage spam swapping stances for ability activation benefits of matching stance
// Adds a small duration to entering a stance to discourage spam swapping
// stances for ability activation benefits of matching stance
if self.timer < STANCE_ENTER_TIME {
if let CharacterState::ComboMelee2(c) = &mut update.character {
c.timer = tick_attack_or_default(data, self.timer, None);
@ -166,8 +174,10 @@ impl CharacterBehavior for Data {
}
if input_is_pressed(data, ability_input) {
if let CharacterState::ComboMelee2(c) = &mut update.character {
// Only have the next strike skip the recover period of this strike if not every strike in the combo is complete yet
c.start_next_strike = (c.completed_strikes + 1) < c.static_data.strikes.len();
// Only have the next strike skip the recover period of this strike if not
// every strike in the combo is complete yet
c.start_next_strike =
(c.completed_strikes + 1) < c.static_data.strikes.len();
}
}
if self.timer.as_secs_f32()
@ -248,7 +258,13 @@ impl CharacterBehavior for Data {
if input_is_pressed(data, ability_input) {
next_strike(&mut update)
} else if !self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) && !interrupted {
} else if !self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
&& !interrupted
{
attempt_input(data, output_events, &mut update);
}
},
@ -266,7 +282,8 @@ impl CharacterBehavior for Data {
) -> StateUpdate {
let mut update = StateUpdate::from(data);
if matches!(data.character, CharacterState::ComboMelee2(data) if data.static_data.ability_info.input == Some(input) && input != InputKind::Primary && data.stage_section.is_none()) {
if matches!(data.character, CharacterState::ComboMelee2(data) if data.static_data.ability_info.input == Some(input) && input != InputKind::Primary && data.stage_section.is_none())
{
end_melee_ability(data, &mut update);
} else {
update.queued_inputs.insert(input, InputAttr {
@ -281,8 +298,9 @@ impl CharacterBehavior for Data {
let mut update = StateUpdate::from(data);
if let CharacterState::ComboMelee2(c) = data.character {
if c.stage_section.is_none() {
update.character =
CharacterState::Wielding(wielding::Data { is_sneaking: data.character.is_stealthy() });
update.character = CharacterState::Wielding(wielding::Data {
is_sneaking: data.character.is_stealthy(),
});
attempt_swap_equipped_weapons(data, &mut update);
}
}
@ -314,7 +332,11 @@ impl CharacterBehavior for Data {
let reset_to_idle = match inv_action {
InventoryAction::Drop(slot)
| InventoryAction::Swap(slot, _)
| InventoryAction::Swap(_, Slot::Equip(slot)) if matches!(slot, EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand) => true,
| InventoryAction::Swap(_, Slot::Equip(slot))
if matches!(slot, EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand) =>
{
true
},
InventoryAction::Use(_) => true,
_ => false,
};
@ -363,7 +385,10 @@ impl CharacterBehavior for Data {
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
if let CharacterState::ComboMelee2(c) = data.character {
if c.stage_section.is_none() && data.physics.on_ground.is_some() && data.body.is_humanoid() {
if c.stage_section.is_none()
&& data.physics.on_ground.is_some()
&& data.body.is_humanoid()
{
update.character = CharacterState::Wielding(wielding::Data { is_sneaking: true });
}
}

View File

@ -81,7 +81,11 @@ impl CharacterBehavior for Data {
} else {
// Transitions to charge section of stage
update.character = CharacterState::DashMelee(Data {
auto_charge: !self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)),
auto_charge: !self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input)),
timer: Duration::default(),
stage_section: StageSection::Charge,
..*self
@ -90,7 +94,11 @@ impl CharacterBehavior for Data {
},
StageSection::Charge => {
if self.timer < self.charge_end_timer
&& (self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
&& (self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
|| (self.auto_charge && self.timer < self.static_data.charge_duration))
&& update.energy.current() > 0.0
{

View File

@ -88,9 +88,9 @@ impl CharacterBehavior for Data {
self.static_data.combo_on_use as f32
/ self.static_data.minimum_combo as f32
},
ScalingKind::Sqrt => {
(self.static_data.combo_on_use as f32 / self.static_data.minimum_combo as f32).sqrt()
},
ScalingKind::Sqrt => (self.static_data.combo_on_use as f32
/ self.static_data.minimum_combo as f32)
.sqrt(),
};
match scaling.target {
ScalingTarget::Attack => {

View File

@ -1,6 +1,6 @@
use crate::{
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
combat::CombatEffect,
comp::{character_state::OutputEvents, CharacterState, MeleeConstructor, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::{StageSection, *},

View File

@ -3,7 +3,7 @@ use crate::{
Attack, AttackDamage, AttackEffect, CombatEffect, CombatRequirement, Damage, DamageKind,
DamageSource, GroupTarget, Knockback,
},
comp::{character_state::OutputEvents, CharacterState, shockwave, StateUpdate},
comp::{character_state::OutputEvents, shockwave, CharacterState, StateUpdate},
event::{LocalEvent, ServerEvent},
outcome::Outcome,
states::{
@ -88,7 +88,6 @@ impl CharacterBehavior for Data {
..*self
});
} else {
// Transitions to leap portion of state after buildup delay
update.character = CharacterState::LeapShockwave(Data {
timer: Duration::default(),
@ -102,7 +101,7 @@ impl CharacterBehavior for Data {
// Apply jumping force
let progress = 1.0
- self.timer.as_secs_f32()
/ self.static_data.movement_duration.as_secs_f32();
/ self.static_data.movement_duration.as_secs_f32();
handle_forced_movement(data, &mut update, ForcedMovement::Leap {
vertical: self.static_data.vertical_leap_strength,
forward: self.static_data.forward_leap_strength,
@ -142,12 +141,12 @@ impl CharacterBehavior for Data {
Some(GroupTarget::OutOfGroup),
CombatEffect::Poise(self.static_data.poise_damage),
)
.with_requirement(CombatRequirement::AnyDamage);
.with_requirement(CombatRequirement::AnyDamage);
let knockback = AttackEffect::new(
Some(GroupTarget::OutOfGroup),
CombatEffect::Knockback(self.static_data.knockback),
)
.with_requirement(CombatRequirement::AnyDamage);
.with_requirement(CombatRequirement::AnyDamage);
let mut damage = AttackDamage::new(
Damage {
source: DamageSource::Shockwave,
@ -185,9 +184,7 @@ impl CharacterBehavior for Data {
});
// Send local event used for frontend shenanigans
output_events.emit_local(LocalEvent::CreateOutcome(Outcome::IceSpikes {
pos: data.pos.0
+ *data.ori.look_dir()
* (data.body.max_radius()),
pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
}));
} else {
// Transitions to recover

View File

@ -56,7 +56,12 @@ impl CharacterBehavior for Data {
});
} else {
// Done
if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
reset_state(self, data, output_events, &mut update);
} else {
end_ability(data, &mut update);
@ -70,7 +75,12 @@ impl CharacterBehavior for Data {
}
// At end of state logic so an interrupt isn't overwritten
if !self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input)) {
if !self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
{
handle_dodge_input(data, &mut update);
}
@ -85,11 +95,6 @@ fn reset_state(
update: &mut StateUpdate,
) {
if let Some(input) = data.static_data.ability_info.input {
handle_input(
join,
output_events,
update,
input,
);
handle_input(join, output_events, update, input);
}
}
}

View File

@ -1,9 +1,9 @@
use crate::{
combat::CombatEffect,
comp::{
character_state::OutputEvents, Body, CharacterState, LightEmitter, Pos,
ProjectileConstructor, StateUpdate,
},
combat::CombatEffect,
event::ServerEvent,
states::{
behavior::{CharacterBehavior, JoinData},
@ -87,7 +87,11 @@ impl CharacterBehavior for Data {
.unwrap_or_default(),
..*self
});
} else if self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))
} else if self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))
&& update.energy.current() >= self.static_data.energy_cost
{
// Fire if input is pressed still

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
buff::{BuffChange, BuffKind},
character_state::{OutputEvents, AttackImmunities},
character_state::{AttackImmunities, OutputEvents},
CharacterState, InputKind, StateUpdate,
},
event::ServerEvent,

View File

@ -145,9 +145,7 @@ impl CharacterBehavior for Data {
// Send local event used for frontend shenanigans
if self.static_data.specifier == shockwave::FrontendSpecifier::IceSpikes {
output_events.emit_local(LocalEvent::CreateOutcome(Outcome::FlashFreeze {
pos: data.pos.0
+ *data.ori.look_dir()
* (data.body.max_radius()),
pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
}));
}
} else {

View File

@ -57,8 +57,7 @@ impl CharacterBehavior for Data {
data.sidewalk = 0.0;
}
// forward, max at 8u/s
(data.dt.0 * 3.0)
.clamp(0.0, 8.0 - current_planar_velocity)
(data.dt.0 * 3.0).clamp(0.0, 8.0 - current_planar_velocity)
} else {
if let CharacterState::Skate(data) = &mut update.character {
data.accelerate = -1.0;

View File

@ -121,7 +121,11 @@ impl CharacterBehavior for Data {
} else if update.energy.current() >= self.static_data.energy_cost
&& (self.consecutive_spins < self.static_data.num_spins
|| (self.static_data.is_infinite
&& self.static_data.ability_info.input.map_or(false, |input| input_is_pressed(data, input))))
&& self
.static_data
.ability_info
.input
.map_or(false, |input| input_is_pressed(data, input))))
{
update.character = CharacterState::SpinMelee(Data {
timer: Duration::default(),

View File

@ -1,11 +1,13 @@
use super::utils::*;
use crate::{
comp::{character_state::OutputEvents, CharacterState, InventoryManip, StateUpdate, item::{Item, ItemDefinitionIdOwned}},
event::{ServerEvent, LocalEvent},
outcome::Outcome,
states::{
behavior::{CharacterBehavior, JoinData},
comp::{
character_state::OutputEvents,
item::{Item, ItemDefinitionIdOwned},
CharacterState, InventoryManip, StateUpdate,
},
event::{LocalEvent, ServerEvent},
outcome::Outcome,
states::behavior::{CharacterBehavior, JoinData},
terrain::SpriteKind,
util::Dir,
};
@ -95,20 +97,43 @@ impl CharacterBehavior for Data {
}
} else {
// Create inventory manipulation event
let required_item = self.static_data.required_item
.as_ref()
.and_then(|(i, consume)| Some((
Item::new_from_item_definition_id(i.as_ref(), data.ability_map, data.msm).ok()?,
*consume,
)));
let has_required_item = required_item.as_ref().map_or(true, |(item, _consume)| data.inventory.map_or(false, |inv| inv.contains(item)));
let required_item =
self.static_data
.required_item
.as_ref()
.and_then(|(i, consume)| {
Some((
Item::new_from_item_definition_id(
i.as_ref(),
data.ability_map,
data.msm,
)
.ok()?,
*consume,
))
});
let has_required_item =
required_item.as_ref().map_or(true, |(item, _consume)| {
data.inventory.map_or(false, |inv| inv.contains(item))
});
if has_required_item {
let inv_slot = required_item.and_then(|(item, consume)| Some((data.inventory.and_then(|inv| inv.get_slot_of_item(&item))?, consume)));
let inv_manip = InventoryManip::Collect { sprite_pos: self.static_data.sprite_pos, required_item: inv_slot };
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
output_events.emit_local(LocalEvent::CreateOutcome(Outcome::SpriteUnlocked {
pos: self.static_data.sprite_pos,
}));
let inv_slot = required_item.and_then(|(item, consume)| {
Some((
data.inventory.and_then(|inv| inv.get_slot_of_item(&item))?,
consume,
))
});
let inv_manip = InventoryManip::Collect {
sprite_pos: self.static_data.sprite_pos,
required_item: inv_slot,
};
output_events
.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
output_events.emit_local(LocalEvent::CreateOutcome(
Outcome::SpriteUnlocked {
pos: self.static_data.sprite_pos,
},
));
}
// Done
end_ability(data, &mut update);

View File

@ -26,7 +26,8 @@ pub struct StaticData {
pub recover_duration: Duration,
/// What kind of sprite is created by this state
pub sprite: SpriteKind,
/// Duration until sprite-delete begins (in sec), randomization-range of sprite-delete-time (in sec)
/// Duration until sprite-delete begins (in sec), randomization-range of
/// sprite-delete-time (in sec)
pub del_timeout: Option<(f32, f32)>,
/// Range that sprites are created relative to the summonner
pub summon_distance: (f32, f32),
@ -92,7 +93,15 @@ impl CharacterBehavior for Data {
let spiral = Spiral2d::with_edge_radius(radius);
for point in spiral {
// If square is in the angle and is not sparse, generate sprite
if data.ori.look_vec().xy().angle_between(point.as_()).to_degrees() <= (self.static_data.angle / 2.0) && !thread_rng().gen_bool(self.static_data.sparseness) {
if data
.ori
.look_vec()
.xy()
.angle_between(point.as_())
.to_degrees()
<= (self.static_data.angle / 2.0)
&& !thread_rng().gen_bool(self.static_data.sparseness)
{
// The coordinates of where the sprite is created
let sprite_pos = Vec3::new(
data.pos.0.x.floor() as i32 + point.x,
@ -120,8 +129,7 @@ impl CharacterBehavior for Data {
let z = sprite_pos.z + (10.5 - obstacle_z).ceil() as i32;
// Location sprite will be created
let sprite_pos =
Vec3::new(sprite_pos.x, sprite_pos.y, z);
let sprite_pos = Vec3::new(sprite_pos.x, sprite_pos.y, z);
// Layers of sprites
let layers = match self.static_data.sprite {
SpriteKind::SeaUrchin => 2,
@ -132,7 +140,7 @@ impl CharacterBehavior for Data {
output_events.emit_server(ServerEvent::CreateSprite {
pos: Vec3::new(sprite_pos.x, sprite_pos.y, z + i),
sprite: self.static_data.sprite,
del_timeout: self.static_data.del_timeout,
del_timeout: self.static_data.del_timeout,
});
}
}
@ -147,9 +155,7 @@ impl CharacterBehavior for Data {
// Send local event used for frontend shenanigans
if self.static_data.sprite == SpriteKind::IceSpike {
output_events.emit_local(LocalEvent::CreateOutcome(Outcome::IceCrack {
pos: data.pos.0
+ *data.ori.look_dir()
* (data.body.max_radius()),
pos: data.pos.0 + *data.ori.look_dir() * (data.body.max_radius()),
}));
}
} else {

View File

@ -1,9 +1,7 @@
use super::utils::*;
use crate::{
comp::{character_state::OutputEvents, CharacterState, PoiseState, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
},
states::behavior::{CharacterBehavior, JoinData},
};
use serde::{Deserialize, Serialize};
use std::time::Duration;

View File

@ -10,9 +10,7 @@ use crate::{
CharacterState, InventoryManip, StateUpdate,
},
event::ServerEvent,
states::{
behavior::{CharacterBehavior, JoinData},
},
states::behavior::{CharacterBehavior, JoinData},
};
use serde::{Deserialize, Serialize};
use std::time::Duration;

View File

@ -7,7 +7,7 @@ use crate::{
character_state::OutputEvents,
controller::InventoryManip,
inventory::slot::{ArmorSlot, EquipSlot, Slot},
item::{armor::Friction, tool::AbilityContext, Hands, ItemKind, ToolKind, Item},
item::{armor::Friction, tool::AbilityContext, Hands, Item, ItemKind, ToolKind},
quadruped_low, quadruped_medium, quadruped_small,
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
@ -17,9 +17,9 @@ use crate::{
event::{LocalEvent, ServerEvent},
outcome::Outcome,
states::{behavior::JoinData, utils::CharacterState::Idle, *},
terrain::{TerrainChunkSize, UnlockKind},
util::Dir,
vol::{ReadVol, RectVolSize},
terrain::{TerrainChunkSize, UnlockKind},
};
use core::hash::BuildHasherDefault;
use fxhash::FxHasher64;
@ -826,8 +826,7 @@ pub fn handle_manipulate_loadout(
} else {
// Else emit inventory action instantaneously
let inv_manip = InventoryManip::Use(slot);
output_events
.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
}
},
InventoryAction::Collect(sprite_pos) => {
@ -913,15 +912,25 @@ pub fn handle_manipulate_loadout(
.into_path()
.is_some();
let required_item = sprite_at_pos.and_then(|s| match s.unlock_condition(sprite_cfg.cloned()) {
UnlockKind::Free => None,
UnlockKind::Requires(item) => Some((item, false)),
UnlockKind::Consumes(item) => Some((item, true)),
});
let required_item =
sprite_at_pos.and_then(|s| match s.unlock_condition(sprite_cfg.cloned()) {
UnlockKind::Free => None,
UnlockKind::Requires(item) => Some((item, false)),
UnlockKind::Consumes(item) => Some((item, true)),
});
let has_required_items = required_item
.as_ref()
.and_then(|(i, _consume)| Item::new_from_item_definition_id(i.as_ref(), data.ability_map, data.msm).ok())
.map_or(true, |i| data.inventory.map_or(false, |inv| inv.contains(&i)));
.and_then(|(i, _consume)| {
Item::new_from_item_definition_id(
i.as_ref(),
data.ability_map,
data.msm,
)
.ok()
})
.map_or(true, |i| {
data.inventory.map_or(false, |inv| inv.contains(&i))
});
// If path can be found between entity interacting with sprite and entity, start
// interaction with sprite
@ -934,25 +943,28 @@ pub fn handle_manipulate_loadout(
let (buildup_duration, use_duration, recover_duration) =
sprite_interact.durations();
update.character = CharacterState::SpriteInteract(sprite_interact::Data {
static_data: sprite_interact::StaticData {
buildup_duration,
use_duration,
recover_duration,
sprite_pos,
sprite_kind: sprite_interact,
was_wielded: data.character.is_wield(),
was_sneak: data.character.is_stealthy(),
required_item,
ability_info: AbilityInfo::from_forced_state_change(data.character),
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
})
update.character =
CharacterState::SpriteInteract(sprite_interact::Data {
static_data: sprite_interact::StaticData {
buildup_duration,
use_duration,
recover_duration,
sprite_pos,
sprite_kind: sprite_interact,
was_wielded: data.character.is_wield(),
was_sneak: data.character.is_stealthy(),
required_item,
ability_info: AbilityInfo::from_forced_state_change(
data.character,
),
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
})
} else {
output_events.emit_local(LocalEvent::CreateOutcome(Outcome::FailedSpriteUnlock {
pos: sprite_pos,
}));
output_events.emit_local(LocalEvent::CreateOutcome(
Outcome::FailedSpriteUnlock { pos: sprite_pos },
));
}
}
}
@ -968,7 +980,10 @@ pub fn handle_manipulate_loadout(
output_events.emit_server(ServerEvent::InventoryManip(data.entity, inv_manip));
},
InventoryAction::Sort => {
output_events.emit_server(ServerEvent::InventoryManip(data.entity, InventoryManip::Sort));
output_events.emit_server(ServerEvent::InventoryManip(
data.entity,
InventoryManip::Sort,
));
},
InventoryAction::Use(slot @ Slot::Equip(_)) => {
let inv_manip = InventoryManip::Use(slot);

View File

@ -26,8 +26,7 @@ impl CharacterBehavior for Data {
let lift = WALLRUN_ANTIGRAV;
update.vel.0.z += data.dt.0
* lift
* (Vec2::<f32>::from(update.vel.0).magnitude() * 0.075)
.clamp(0.2, 1.0);
* (Vec2::<f32>::from(update.vel.0).magnitude() * 0.075).clamp(0.2, 1.0);
}
// fall off wall, hit ground, or enter water

View File

@ -52,7 +52,11 @@ impl CharacterBehavior for Data {
let reset_to_idle = match inv_action {
InventoryAction::Drop(slot)
| InventoryAction::Swap(slot, _)
| InventoryAction::Swap(_, Slot::Equip(slot)) if matches!(slot, EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand) => true,
| InventoryAction::Swap(_, Slot::Equip(slot))
if matches!(slot, EquipSlot::ActiveMainhand | EquipSlot::ActiveOffhand) =>
{
true
},
InventoryAction::Use(_) => true,
_ => false,
};

View File

@ -282,8 +282,7 @@ impl Block {
BlockKind::Lava => None,
_ => self.get_sprite().and_then(|sprite| match sprite {
sprite if sprite.is_container() => None,
SpriteKind::Keyhole
| SpriteKind::KeyDoor => None,
SpriteKind::Keyhole | SpriteKind::KeyDoor => None,
SpriteKind::Anvil
| SpriteKind::Cauldron
| SpriteKind::CookingPot
@ -307,9 +306,10 @@ impl Block {
| SpriteKind::SeaDecorWindowVer
| SpriteKind::Rope
| SpriteKind::GlassBarrier => None,
SpriteKind::EnsnaringVines | SpriteKind::EnsnaringWeb | SpriteKind::SeaUrchin | SpriteKind::IceSpike => {
Some(0.1)
},
SpriteKind::EnsnaringVines
| SpriteKind::EnsnaringWeb
| SpriteKind::SeaUrchin
| SpriteKind::IceSpike => Some(0.1),
_ => Some(0.25),
}),
}

View File

@ -198,8 +198,7 @@ impl<V, S: RectVolSize, M: Clone> ReadVol for Chonk<V, S, M> {
let rpos = pos
- Vec3::unit_z()
* (self.z_offset + sub_chunk_idx * SubChunkSize::<S>::SIZE.z as i32);
self.sub_chunks[sub_chunk_idx as usize]
.get_unchecked(rpos)
self.sub_chunks[sub_chunk_idx as usize].get_unchecked(rpos)
}
}
@ -208,12 +207,16 @@ impl<V, S: RectVolSize, M: Clone> ReadVol for Chonk<V, S, M> {
Self::Vox: Copy,
{
let idx = self.sub_chunk_idx(aabb.min.z);
// Special-case for the AABB being entirely within a single sub-chunk as this is very common.
// Special-case for the AABB being entirely within a single sub-chunk as this is
// very common.
if idx == self.sub_chunk_idx(aabb.max.z) && idx >= 0 && idx < self.sub_chunks.len() as i32 {
let sub_chunk = &self.sub_chunks[idx as usize];
let z_off = self.z_offset + idx * SubChunkSize::<S>::SIZE.z as i32;
sub_chunk.for_each_in(
Aabb { min: aabb.min.with_z(aabb.min.z - z_off), max: aabb.max.with_z(aabb.max.z - z_off) },
Aabb {
min: aabb.min.with_z(aabb.min.z - z_off),
max: aabb.max.with_z(aabb.max.z - z_off),
},
|pos, vox| f(pos.with_z(pos.z + z_off), vox),
);
} else {

View File

@ -202,9 +202,10 @@ impl MapSizeLg {
/// Determine whether a chunk position is in bounds.
pub const fn contains_chunk(&self, chunk_key: Vec2<i32>) -> bool {
let map_size = self.chunks();
chunk_key.x >= 0 && chunk_key.y >= 0 &&
chunk_key.x == chunk_key.x & ((map_size.x as i32) - 1) &&
chunk_key.y == chunk_key.y & ((map_size.y as i32) - 1)
chunk_key.x >= 0
&& chunk_key.y >= 0
&& chunk_key.x == chunk_key.x & ((map_size.x as i32) - 1)
&& chunk_key.y == chunk_key.y & ((map_size.y as i32) - 1)
}
}

View File

@ -12,12 +12,12 @@ pub use self::{
block::{Block, BlockKind},
map::MapSizeLg,
site::SiteKindMeta,
sprite::{SpriteKind, SpriteCfg, UnlockKind},
sprite::{SpriteCfg, SpriteKind, UnlockKind},
structure::{Structure, StructuresGroup},
};
use hashbrown::HashMap;
use roots::find_roots_cubic;
use serde::{Deserialize, Serialize};
use hashbrown::HashMap;
use crate::{
vol::{ReadVol, RectVolSize},
@ -100,9 +100,10 @@ impl CoordinateConversions for Vec2<f32> {
impl CoordinateConversions for Vec2<f64> {
#[inline]
fn wpos_to_cpos(&self) -> Self { self.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f64)}
fn wpos_to_cpos(&self) -> Self { self.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f64) }
#[inline]
fn cpos_to_wpos(&self) -> Self { self.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e * sz as f64)}
fn cpos_to_wpos(&self) -> Self { self.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e * sz as f64) }
}
// TerrainChunkMeta
@ -205,9 +206,7 @@ impl TerrainChunkMeta {
pub fn debug_lines(&self) -> &[LineSegment3<f32>] { &self.debug_lines }
pub fn add_debug_line(&mut self, line: LineSegment3<f32>) {
self.debug_lines.push(line);
}
pub fn add_debug_line(&mut self, line: LineSegment3<f32>) { self.debug_lines.push(line); }
pub fn sprite_cfg_at(&self, rpos: Vec3<i32>) -> Option<&SpriteCfg> {
self.sprite_cfgs.get(&rpos)

View File

@ -1,28 +1,22 @@
use crate::{
comp::{item::{ItemDefinitionId, ItemDefinitionIdOwned}, tool::ToolKind},
comp::{
item::{ItemDefinitionId, ItemDefinitionIdOwned},
tool::ToolKind,
},
lottery::LootSpec,
make_case_elim,
};
use strum::EnumIter;
use hashbrown::HashMap;
use lazy_static::lazy_static;
use num_derive::FromPrimitive;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt};
use strum::EnumIter;
make_case_elim!(
sprite_kind,
#[derive(
Copy,
Clone,
Debug,
Hash,
Eq,
PartialEq,
Serialize,
Deserialize,
EnumIter,
FromPrimitive,
Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, EnumIter, FromPrimitive,
)]
#[repr(u8)]
pub enum SpriteKind {
@ -230,9 +224,9 @@ make_case_elim!(
SeaDecorPillar = 0xC7,
SeashellLantern = 0xC8,
Rope = 0xC9,
IceSpike = 0xCA,
Bedroll = 0xCB,
BedrollSnow = 0xCC,
IceSpike = 0xCA,
Bedroll = 0xCB,
BedrollSnow = 0xCC,
BedrollPirate = 0xCD,
Tent = 0xCE,
Grave = 0xCF,
@ -242,10 +236,10 @@ make_case_elim!(
MagicalBarrier = 0xD3,
MagicalSeal = 0xD4,
WallLampWizard = 0xD5,
Candle = 0xD6,
Candle = 0xD6,
Keyhole = 0xD7,
KeyDoor = 0xD8,
CommonLockedChest = 0xD9,
KeyDoor = 0xD8,
CommonLockedChest = 0xD9,
}
);
@ -511,13 +505,17 @@ impl SpriteKind {
}
/// Requires this item in the inventory to harvest, uses item_definition_id
// TODO: Do we want to consolidate this with mine_tool at all? Main differences are that mine tool requires item to be an equippable tool, be equipped, and does not consume item while required_item requires that the item be in the inventory and will consume the item on collecting the sprite.
// TODO: Do we want to consolidate this with mine_tool at all? Main differences
// are that mine tool requires item to be an equippable tool, be equipped, and
// does not consume item while required_item requires that the item be in the
// inventory and will consume the item on collecting the sprite.
pub fn unlock_condition(&self, cfg: Option<SpriteCfg>) -> UnlockKind {
cfg
.and_then(|cfg| cfg.unlock)
cfg.and_then(|cfg| cfg.unlock)
.unwrap_or_else(|| match self {
// Example, do not let this merge with twigs requiring cheese to pick up
SpriteKind::CommonLockedChest => UnlockKind::Consumes(ItemDefinitionId::Simple("common.items.utility.lockpick_0").to_owned()),
SpriteKind::CommonLockedChest => UnlockKind::Consumes(
ItemDefinitionId::Simple("common.items.utility.lockpick_0").to_owned(),
),
_ => UnlockKind::Free,
})
}
@ -613,9 +611,8 @@ impl fmt::Display for SpriteKind {
use strum::IntoEnumIterator;
lazy_static! {
pub static ref SPRITE_KINDS: HashMap<String, SpriteKind> = SpriteKind::iter()
.map(|sk| (sk.to_string(), sk))
.collect();
pub static ref SPRITE_KINDS: HashMap<String, SpriteKind> =
SpriteKind::iter().map(|sk| (sk.to_string(), sk)).collect();
}
impl<'a> TryFrom<&'a str> for SpriteKind {
@ -629,9 +626,11 @@ impl<'a> TryFrom<&'a str> for SpriteKind {
pub enum UnlockKind {
/// The sprite can be freely unlocked without any conditions
Free,
/// The sprite requires that the opening character has a given item in their inventory
/// The sprite requires that the opening character has a given item in their
/// inventory
Requires(ItemDefinitionIdOwned),
/// The sprite will consume the given item from the opening character's inventory
/// The sprite will consume the given item from the opening character's
/// inventory
Consumes(ItemDefinitionIdOwned),
}

View File

@ -10,7 +10,6 @@ use serde::Deserialize;
use std::{num::NonZeroU8, sync::Arc};
use vek::*;
make_case_elim!(
structure_block,
#[derive(Clone, PartialEq, Debug, Deserialize)]

View File

@ -119,7 +119,7 @@ impl<Context, Target> SynthTyped<Context, Target> for WeakHead<Pure<Target>, Tar
/// # #[allow(non_snake_case)]
/// # #[allow(dead_code)]
/// mod my_type_module {
/// use ::serde::{Deserialize, Serialize};
/// use serde::{Deserialize, Serialize};
///
/// /// The number of variants in this enum.
/// pub const NUM_VARIANTS: usize = 2;

View File

@ -1,15 +1,20 @@
use std::hash::{Hasher, BuildHasher};
use std::hash::{BuildHasher, Hasher};
#[derive(Copy, Clone, Default)]
pub struct GridHasher(u64);
// It's extremely unlikely that the spatial grid can be used to viably DOS the server given that clients only have
// control over their player and a handful of entities in their near vicinity. For this reason, we just use an xor
// hash, which should keep collisions relatively low since the spatial coherence of the grid is distributed fairly
// evenly with the output of the hash function.
// It's extremely unlikely that the spatial grid can be used to viably DOS the
// server given that clients only have control over their player and a handful
// of entities in their near vicinity. For this reason, we just use an xor hash,
// which should keep collisions relatively low since the spatial coherence of
// the grid is distributed fairly evenly with the output of the hash function.
impl Hasher for GridHasher {
fn finish(&self) -> u64 { self.0 }
fn write(&mut self, _: &[u8]) { panic!("Hashing arbitrary bytes is unimplemented"); }
fn write(&mut self, _: &[u8]) {
panic!("Hashing arbitrary bytes is unimplemented");
}
fn write_i32(&mut self, x: i32) { self.0 = self.0.wrapping_mul(113989) ^ self.0 ^ x as u64; }
}

View File

@ -1,13 +1,13 @@
mod color;
pub mod dir;
pub mod find_dist;
mod grid_hasher;
mod option;
pub mod plane;
pub mod projection;
/// Contains [`SpatialGrid`] which is useful for accelerating queries of nearby
/// entities
mod spatial_grid;
mod grid_hasher;
pub const GIT_VERSION_BUILD: &str = include_str!(concat!(env!("OUT_DIR"), "/githash"));
pub const GIT_TAG_BUILD: &str = include_str!(concat!(env!("OUT_DIR"), "/gittag"));
@ -36,8 +36,8 @@ lazy_static::lazy_static! {
pub use color::*;
pub use dir::*;
pub use grid_hasher::GridHasher;
pub use option::either_with;
pub use plane::Plane;
pub use projection::Projection;
pub use spatial_grid::SpatialGrid;
pub use grid_hasher::GridHasher;

View File

@ -1,5 +1,5 @@
use vek::*;
use super::GridHasher;
use vek::*;
#[derive(Debug)]
pub struct SpatialGrid {

View File

@ -16,10 +16,10 @@ impl ViewDistances {
///
/// Also ensures both are at a minimum of 1 (unless the provided max is 0).
pub fn clamp(self, max: Option<u32>) -> Self {
let terrain = self.terrain.clamp(1,max.unwrap_or(u32::MAX));
let terrain = self.terrain.clamp(1, max.unwrap_or(u32::MAX));
Self {
terrain,
entity: self.entity.clamp(1,terrain),
entity: self.entity.clamp(1, terrain),
}
}
}

View File

@ -98,8 +98,9 @@ pub trait ReadVol: BaseVol {
/// Get a reference to the voxel at the provided position in the volume.
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Error>;
/// Get a reference to the voxel at the provided position in the volume. Many volumes provide a fast path,
/// provided the position is always in-bounds. Note that this function is still safe.
/// Get a reference to the voxel at the provided position in the volume.
/// Many volumes provide a fast path, provided the position is always
/// in-bounds. Note that this function is still safe.
fn get_unchecked(&self, pos: Vec3<i32>) -> &Self::Vox { self.get(pos).unwrap() }
/// NOTE: By default, this ray will simply run from `from` to `to` without

View File

@ -309,9 +309,7 @@ impl<V, S: VolSize, M> ReadVol for Chunk<V, S, M> {
}
#[inline(always)]
fn get_unchecked(&self, pos: Vec3<i32>) -> &Self::Vox {
self.get_unchecked(pos)
}
fn get_unchecked(&self, pos: Vec3<i32>) -> &Self::Vox { self.get_unchecked(pos) }
fn for_each_in(&self, mut aabb: Aabb<i32>, mut f: impl FnMut(Vec3<i32>, Self::Vox))
where

View File

@ -1,8 +1,8 @@
use crate::{
terrain::MapSizeLg,
util::GridHasher,
vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, WriteVol},
volumes::dyna::DynaError,
util::GridHasher,
};
use hashbrown::{hash_map, HashMap};
use std::{fmt::Debug, ops::Deref, sync::Arc};
@ -42,7 +42,8 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
#[inline(always)]
pub fn par_keys(&self) -> hashbrown::hash_map::rayon::ParKeys<Vec2<i32>, Arc<V>>
where V: Send + Sync,
where
V: Send + Sync,
{
self.chunks.par_keys()
}
@ -185,7 +186,8 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
#[inline(always)]
pub fn get_key_real(&self, key: Vec2<i32>) -> Option<&V> {
self.get_key_arc_real(key).map(|arc_chunk| arc_chunk.as_ref())
self.get_key_arc_real(key)
.map(|arc_chunk| arc_chunk.as_ref())
}
#[inline(always)]
@ -197,20 +199,21 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
}
#[inline(always)]
pub fn contains_key_real(&self, key: Vec2<i32>) -> bool {
self.chunks.contains_key(&key)
}
pub fn contains_key_real(&self, key: Vec2<i32>) -> bool { self.chunks.contains_key(&key) }
#[inline(always)]
pub fn get_key_arc(&self, key: Vec2<i32>) -> Option<&Arc<V>> {
self.get_key_arc_real(key)
.or_else(|| if !self.map_size_lg.contains_chunk(key) { Some(&self.default) } else { None })
self.get_key_arc_real(key).or_else(|| {
if !self.map_size_lg.contains_chunk(key) {
Some(&self.default)
} else {
None
}
})
}
#[inline(always)]
pub fn get_key_arc_real(&self, key: Vec2<i32>) -> Option<&Arc<V>> {
self.chunks.get(&key)
}
pub fn get_key_arc_real(&self, key: Vec2<i32>) -> Option<&Arc<V>> { self.chunks.get(&key) }
pub fn clear(&mut self) { self.chunks.clear(); }
@ -225,9 +228,7 @@ impl<V: RectRasterableVol> VolGrid2d<V> {
pub fn pos_key(&self, pos: Vec3<i32>) -> Vec2<i32> { Self::chunk_key(pos) }
#[inline(always)]
pub fn pos_chunk(&self, pos: Vec3<i32>) -> Option<&V> {
self.get_key(self.pos_key(pos))
}
pub fn pos_chunk(&self, pos: Vec3<i32>) -> Option<&V> { self.get_key(self.pos_key(pos)) }
pub fn iter(&self) -> ChunkIter<V> {
ChunkIter {

View File

@ -47,9 +47,7 @@ impl Weather {
}
// Get the wind velocity for this weather
pub fn wind_vel(&self) -> Vec2<f32> {
self.wind
}
pub fn wind_vel(&self) -> Vec2<f32> { self.wind }
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]