Initial implementation of pickaxe

This commit is contained in:
Joshua Barretto 2021-03-21 16:09:16 +00:00
parent 1afe3b7de5
commit 347e1022a0
45 changed files with 583 additions and 1398 deletions

View File

@ -0,0 +1,23 @@
ComboMelee(
stage_data: [(
stage: 1,
base_damage: 150,
damage_increase: 10,
base_poise_damage: 40,
poise_damage_increase: 0,
knockback: 10.0,
range: 4.5,
angle: 50.0,
base_buildup_duration: 0.4,
base_swing_duration: 0.1,
base_recover_duration: 0.25,
forward_movement: 0.0,
)],
initial_energy_gain: 50,
max_energy_gain: 150,
energy_increase: 50,
speed_increase: 0.1,
max_speed_increase: 0.4,
scales_from_combo: 2,
is_interruptible: false,
)

View File

@ -206,6 +206,11 @@
secondary: "common.abilities.farming.basic",
abilities: [],
),
Pick: (
primary: "common.abilities.pick.swing",
secondary: "common.abilities.pick.swing",
abilities: [],
),
Empty: (
primary: "common.abilities.empty.basic",
secondary: "common.abilities.empty.basic",

View File

@ -0,0 +1,18 @@
ItemDef(
name: "Stone Pickaxe",
description: "Strike the earth!",
kind: Tool((
kind: Pick,
hands: Two,
stats: Direct((
equip_time_secs: 0.25,
power: 0.25,
poise_strength: 0.25,
speed: 0.5,
crit_chance: 0.09375,
crit_mult: 2.2190475,
)),
)),
quality: Low,
tags: [],
)

View File

@ -1013,7 +1013,11 @@
"voxel.weapon.tool.shovel_gold",
(0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.2,
),
// Picks
Tool("common.items.tool.pick"): VoxTrans(
"voxel.weapon.tool.pickaxe_green-0",
(0.0, 0.0, 0.0), (-135.0, 90.0, 0.0), 1.0,
),
// Other
Utility(Coins): VoxTrans(
"voxel.object.coins",
@ -1709,7 +1713,7 @@
"voxel.armor.misc.head.assa_mask-0",
(0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.0,
),
// Bags
// Bags
Armor(Bag("RedFace")): Png (
"element.icons.item_bag_red_face",
),

View File

@ -168,7 +168,7 @@ void main() {
// reflectivity that accounts for physical surroundings like the ground
if ((material & (1u << 1u)) > 0u) {
vec3 reflect_ray_dir = reflect(cam_to_frag, f_norm);
surf_color *= dot(vec3(1.0) - abs(fract(reflect_ray_dir * 3.5) * 2.0 - 1.0) * 0.85, vec3(1));
surf_color *= dot(vec3(1.0) - abs(fract(reflect_ray_dir * 1.5) * 2.0 - 1.0) * 0.85, vec3(1));
}
vec3 emitted_light, reflected_light;

View File

@ -228,7 +228,7 @@ void main() {
// f_light = 1.0;
// if (select_pos.w > 0) */{
vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs;
f_light = (select_pos.w > 0 && select_pos.xyz == sprite_pos/* - vec3(0.5, 0.5, 0.0) * SCALE*/) ? 5.0 : 1.0;
f_light = (select_pos.w > 0 && select_pos.xyz == sprite_pos/* - vec3(0.5, 0.5, 0.0) * SCALE*/) ? 10.0 : 1.0;
// }
gl_Position =

View File

@ -255,8 +255,7 @@ void main() {
emitted_light = vec3(1.0);
reflected_light = vec3(1.0);
float f_select = (select_pos.w > 0 && select_pos.xyz == floor(f_pos - f_norm * 0.5)) ? 1.0 / PERSISTENT_AMBIANCE : 1.0;
max_light += get_sun_diffuse2(/*time_of_day.x, */sun_info, moon_info, f_norm, view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a * f_select/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
max_light += get_sun_diffuse2(/*time_of_day.x, */sun_info, moon_info, f_norm, view_dir, f_pos, mu, cam_attenuation, fluid_alt, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
// emitted_light *= f_light * point_shadow * max(shade_frac, MIN_SHADOW);
// reflected_light *= f_light * point_shadow * shade_frac;
@ -269,6 +268,9 @@ void main() {
vec3 glow = glow_light(f_pos) * (pow(f_glow, 6) * 5 + pow(f_glow, 1.5) * 2);
reflected_light += glow;
float f_select = (select_pos.w > 0 && select_pos.xyz == floor(f_pos - f_norm * 0.5)) ? 0.5 / PERSISTENT_AMBIANCE : 0.0;
reflected_light += f_select;
max_light += lights_at(f_pos, f_norm, view_dir, mu, cam_attenuation, fluid_alt, k_a, k_d, k_s, alpha, f_norm, 1.0, emitted_light, reflected_light);
// float f_ao = 1.0;

View File

@ -600,7 +600,7 @@
color: None
),
"common.items.weapons.hammer.cultist_purp_2h-0": (
vox_spec: ("weapon.hammer.cult_purp-0", (-3.5, -4.5, -5.0)),
vox_spec: ("weapon.hammer.cult_purp-0", (-3.5, -4.5, -5.0)),
color: None
),
/*"Craftsman": ( //TODO This should be a 1h hammer!
@ -839,7 +839,7 @@
"common.items.weapons.tool.rake": (
vox_spec: ("weapon.tool.rake-0", (-1.0, -5.5, -4.0)),
color: None
),
),
"common.items.weapons.tool.pickaxe": (
vox_spec: ("weapon.tool.pickaxe_green-0", (-1.5, -7.5, -4.0)),
color: None
@ -876,43 +876,43 @@
"common.items.weapons.staff.phoenix": (
vox_spec: ("weapon.staff.phoenix", (-2.5, -3.5, -5.0)),
color: None
),
),
"common.items.weapons.staff.heated_arm": (
vox_spec: ("weapon.staff.heated_arm", (-2.5, -2.5, -3.0)),
color: None
),
),
"common.items.weapons.staff.aurora": (
vox_spec: ("weapon.staff.aurora", (-2.5, -2.5, -3.0)),
color: None
),
),
"common.items.weapons.staff.infused_tower": (
vox_spec: ("weapon.staff.infused_tower", (-2.5, -2.5, -3.0)),
color: None
),
),
"common.items.weapons.staff.flamethrower_0": (
vox_spec: ("weapon.staff.unstable_thrower", (-1.5, -2.5, -5.0)),
color: None
),
),
"common.items.weapons.staff.ley_seeker": (
vox_spec: ("weapon.staff.ley_seeker", (-2.5, -2.5, -3.0)),
color: None
),
),
"common.items.weapons.staff.lava_rod": (
vox_spec: ("weapon.staff.lava_rod", (-2.5, -2.5, -3.0)),
color: None
),
),
"common.items.weapons.staff.fiery_wishing_rod": (
vox_spec: ("weapon.staff.fiery_wishing_rod", (-2.5, -2.5, -3.0)),
color: None
),
),
"common.items.weapons.staff.orc_iron": (
vox_spec: ("weapon.staff.orcish_branding_iron", (-2.5, -2.5, -3.0)),
color: None
),
),
"common.items.weapons.staff.bent_fuse": (
vox_spec: ("weapon.staff.bent_fuse", (-2.0, -2.5, -5.0)),
color: None
),
),
// Healing sceptre
"common.items.weapons.sceptre.starter_sceptre": (
vox_spec: ("weapon.sceptre.wood-simple", (-1.5, -2.5, -6.0)),
@ -945,10 +945,15 @@
"common.items.weapons.sceptre.root_evil": (
vox_spec: ("weapon.sceptre.root_evil", (-1.5, -2.5, -6.0)),
color: None
),
),
"common.items.weapons.sceptre.sceptre_velorite_0": (
vox_spec: ("weapon.sceptre.ore-nature", (-2.0, -6.0, -5.0)),
color: None
),
// Picks
"common.items.tool.pick": (
vox_spec: ("weapon.tool.pickaxe_green-0", (-1.5, -7.5, -4.0)),
color: None
),
// Misc
"common.items.debug.admin_stick": (

File diff suppressed because it is too large Load Diff

View File

@ -61,7 +61,7 @@ use num::traits::FloatConst;
use rayon::prelude::*;
use specs::Component;
use std::{
collections::{BTreeSet, VecDeque},
collections::{BTreeMap, VecDeque},
sync::Arc,
time::{Duration, Instant},
};
@ -915,15 +915,18 @@ impl Client {
/// and sends the `ControlAction` event that signals to do the swap.
pub fn swap_loadout(&mut self) { self.control_action(ControlAction::SwapEquippedWeapons) }
pub fn toggle_wield(&mut self) {
let is_wielding = self
.state
/// Determine whether the player is wielding, if they're even capable of
/// being in a wield state.
pub fn is_wielding(&self) -> Option<bool> {
self.state
.ecs()
.read_storage::<comp::CharacterState>()
.get(self.entity())
.map(|cs| cs.is_wield());
.map(|cs| cs.is_wield())
}
match is_wielding {
pub fn toggle_wield(&mut self) {
match self.is_wielding() {
Some(true) => self.control_action(ControlAction::Unwield),
Some(false) => self.control_action(ControlAction::Wield),
None => warn!("Can't toggle wield, client entity doesn't have a `CharacterState`"),
@ -995,11 +998,12 @@ impl Client {
}
}
pub fn handle_input(&mut self, input: InputKind, pressed: bool) {
pub fn handle_input(&mut self, input: InputKind, pressed: bool, select_pos: Option<Vec3<f32>>) {
if pressed {
self.control_action(ControlAction::StartInput {
input,
target: None,
select_pos,
});
} else {
self.control_action(ControlAction::CancelInput(input));
@ -1148,7 +1152,7 @@ impl Client {
entry
.or_insert_with(|| Controller {
inputs: inputs.clone(),
queued_inputs: BTreeSet::new(),
queued_inputs: BTreeMap::new(),
events: Vec::new(),
actions: Vec::new(),
})

View File

@ -7,7 +7,13 @@ use crate::{
use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage, VecStorage};
use specs_idvs::IdvStorage;
use std::collections::{BTreeSet, VecDeque};
use std::collections::{BTreeMap, VecDeque};
use vek::*;
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct InputAttr {
pub select_pos: Option<Vec3<f32>>,
}
/// Data returned from character behavior fn's to Character Behavior System.
pub struct StateUpdate {
@ -17,10 +23,11 @@ pub struct StateUpdate {
pub ori: Ori,
pub energy: Energy,
pub swap_equipped_weapons: bool,
pub queued_inputs: BTreeSet<InputKind>,
pub queued_inputs: BTreeMap<InputKind, InputAttr>,
pub removed_inputs: Vec<InputKind>,
pub local_events: VecDeque<LocalEvent>,
pub server_events: VecDeque<ServerEvent>,
pub select_pos: Option<Vec3<f32>>,
}
impl From<&JoinData<'_>> for StateUpdate {
@ -32,10 +39,11 @@ impl From<&JoinData<'_>> for StateUpdate {
energy: *data.energy,
swap_equipped_weapons: false,
character: data.character.clone(),
queued_inputs: BTreeSet::new(),
queued_inputs: BTreeMap::new(),
removed_inputs: Vec::new(),
local_events: VecDeque::new(),
server_events: VecDeque::new(),
select_pos: None,
}
}
}
@ -192,6 +200,7 @@ pub struct Melee {
pub max_angle: f32,
pub applied: bool,
pub hit_count: u32,
pub break_block: Option<Vec3<i32>>,
}
impl Component for Melee {

View File

@ -2,7 +2,7 @@ use crate::{
comp::{
inventory::slot::{EquipSlot, InvSlotId, Slot},
invite::{InviteKind, InviteResponse},
BuffKind,
BuffKind, InputAttr,
},
trade::{TradeAction, TradeId},
uid::Uid,
@ -11,7 +11,7 @@ use crate::{
use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage};
use specs_idvs::IdvStorage;
use std::collections::BTreeSet;
use std::collections::BTreeMap;
use vek::*;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -111,10 +111,22 @@ pub enum ControlAction {
StartInput {
input: InputKind,
target: Option<Uid>,
// Some inputs need a selected position, such as mining
select_pos: Option<Vec3<f32>>,
},
CancelInput(InputKind),
}
impl ControlAction {
pub fn basic_start(input: InputKind) -> Self {
ControlAction::StartInput {
input,
target: None,
select_pos: None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Ord, PartialOrd)]
#[repr(u32)]
pub enum InputKind {
@ -151,7 +163,7 @@ pub struct ControllerInputs {
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Controller {
pub inputs: ControllerInputs,
pub queued_inputs: BTreeSet<InputKind>,
pub queued_inputs: BTreeMap<InputKind, InputAttr>,
// TODO: consider SmallVec
pub events: Vec<ControlEvent>,
pub actions: Vec<ControlAction>,

View File

@ -25,6 +25,7 @@ use specs::{Component, DerefFlaggedStorage};
use specs_idvs::IdvStorage;
use std::{
num::{NonZeroU32, NonZeroU64},
ops::Deref,
sync::Arc,
};
use vek::Rgb;
@ -298,6 +299,12 @@ impl PartialEq for Item {
}
}
impl Deref for Item {
type Target = ItemDef;
fn deref(&self) -> &Self::Target { &self.item_def }
}
impl assets::Compound for ItemDef {
fn load<S: assets_manager::source::Source>(
cache: &assets_manager::AssetCache<S>,
@ -536,10 +543,6 @@ impl Item {
}
}
pub fn is_stackable(&self) -> bool { self.item_def.is_stackable() }
pub fn is_modular(&self) -> bool { self.item_def.is_modular() }
pub fn name(&self) -> &str { &self.item_def.name }
pub fn description(&self) -> &str { &self.item_def.description }
@ -666,6 +669,14 @@ pub trait ItemDesc {
fn item_definition_id(&self) -> &str;
fn components(&self) -> &[Item];
fn tags(&self) -> &[ItemTag];
fn tool(&self) -> Option<&Tool> {
if let ItemKind::Tool(tool) = self.kind() {
Some(tool)
} else {
None
}
}
}
impl ItemDesc for Item {

View File

@ -49,6 +49,7 @@ impl TagExampleInfo for ModularComponentTag {
ToolKind::Unique(_) => "unique damage component",
ToolKind::Debug => "debug damage component",
ToolKind::Farming => "farming damage component",
ToolKind::Pick => "pickaxe head",
ToolKind::Empty => "empty damage component",
},
ModularComponentKind::Held => match self.toolkind {
@ -65,6 +66,7 @@ impl TagExampleInfo for ModularComponentTag {
ToolKind::Unique(_) => "unique held component",
ToolKind::Debug => "debug held component",
ToolKind::Farming => "farming held component",
ToolKind::Pick => "pickaxe handle",
ToolKind::Empty => "empty held component",
},
}
@ -95,6 +97,7 @@ impl TagExampleInfo for ModularComponentTag {
ToolKind::Unique(_) => "common.items.tag_examples.modular.damage.unique",
ToolKind::Debug => "common.items.tag_examples.modular.damage.debug",
ToolKind::Farming => "common.items.tag_examples.modular.damage.farming",
ToolKind::Pick => "common.items.tag_examples.modular.damage.pick",
ToolKind::Empty => "common.items.tag_examples.modular.damage.empty",
},
ModularComponentKind::Held => match self.toolkind {
@ -116,6 +119,7 @@ impl TagExampleInfo for ModularComponentTag {
ToolKind::Unique(_) => "common.items.tag_examples.modular.held.unique",
ToolKind::Debug => "common.items.tag_examples.modular.held.debug",
ToolKind::Farming => "common.items.tag_examples.modular.held.farming",
ToolKind::Pick => "common.items.tag_examples.modular.held.pick",
ToolKind::Empty => "common.items.tag_examples.modular.held.empty",
},
}

View File

@ -32,6 +32,7 @@ pub enum ToolKind {
Unique(UniqueKind),
Debug,
Farming,
Pick,
/// This is an placeholder item, it is used by non-humanoid npcs to attack
Empty,
}
@ -56,6 +57,7 @@ impl ToolKind {
ToolKind::Unique(_) => "unique",
ToolKind::Debug => "debug",
ToolKind::Farming => "farming",
ToolKind::Pick => "pickaxe",
ToolKind::Empty => "empty",
}
}

View File

@ -55,7 +55,7 @@ pub use self::{
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
ModifierKind,
},
character_state::{CharacterState, Melee, StateUpdate},
character_state::{CharacterState, InputAttr, Melee, StateUpdate},
chat::{
ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg,
},
@ -69,8 +69,7 @@ pub use self::{
home_chunk::HomeChunk,
inputs::CanBuild,
inventory::{
item,
item::{Item, ItemConfig, ItemDrop},
item::{self, tool, Item, ItemConfig, ItemDrop},
slot, Inventory, InventoryUpdate, InventoryUpdateEvent,
},
last::Last,

View File

@ -149,6 +149,10 @@ pub enum ServerEvent {
entity: EcsEntity,
id: SiteId,
},
// Attempt to mine a block, turning it into an item
MineBlock {
pos: Vec3<i32>,
},
}
pub struct EventBus<E> {

View File

@ -1,6 +1,6 @@
use crate::{
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
comp::{CharacterState, Melee, StateUpdate},
comp::{tool::ToolKind, CharacterState, InputAttr, Melee, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
@ -122,6 +122,10 @@ impl CharacterBehavior for Data {
max_angle: self.static_data.max_angle,
applied: false,
hit_count: 0,
break_block: update
.select_pos
.filter(|_| self.static_data.ability_info.tool == Some(ToolKind::Pick))
.map(|p| p.map(|e| e.floor() as i32)),
});
} else if self.timer < self.static_data.swing_duration {
// Swings
@ -178,5 +182,10 @@ impl CharacterBehavior for Data {
}
fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) {
handle_input(join, update, data.static_data.ability_info.input);
handle_input(
join,
update,
data.static_data.ability_info.input,
InputAttr::default(),
);
}

View File

@ -1,5 +1,7 @@
use crate::{
comp::{Body, CharacterState, Gravity, LightEmitter, ProjectileConstructor, StateUpdate},
comp::{
Body, CharacterState, Gravity, InputAttr, LightEmitter, ProjectileConstructor, StateUpdate,
},
event::ServerEvent,
states::{
behavior::{CharacterBehavior, JoinData},
@ -125,5 +127,10 @@ impl CharacterBehavior for Data {
}
fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) {
handle_input(join, update, data.static_data.ability_info.input);
handle_input(
join,
update,
data.static_data.ability_info.input,
InputAttr::default(),
);
}

View File

@ -1,8 +1,8 @@
use crate::{
comp::{
item::MaterialStatManifest, Beam, Body, CharacterState, Combo, ControlAction, Controller,
ControllerInputs, Energy, Health, InputKind, Inventory, InventoryAction, Melee, Ori,
PhysicsState, Pos, StateUpdate, Stats, Vel,
ControllerInputs, Energy, Health, InputAttr, InputKind, Inventory, InventoryAction, Melee,
Ori, PhysicsState, Pos, StateUpdate, Stats, Vel,
},
resources::DeltaTime,
uid::Uid,
@ -13,6 +13,7 @@ use specs::{
DerefFlaggedStorage, Entity, LazyUpdate,
};
use specs_idvs::IdvStorage;
use vek::*;
pub trait CharacterBehavior {
fn behavior(&self, data: &JoinData) -> StateUpdate;
@ -29,9 +30,16 @@ pub trait CharacterBehavior {
fn sneak(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
fn stand(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
fn talk(&self, data: &JoinData) -> StateUpdate { StateUpdate::from(data) }
fn start_input(&self, data: &JoinData, input: InputKind, _target: Option<Uid>) -> StateUpdate {
fn start_input(
&self,
data: &JoinData,
input: InputKind,
_target: Option<Uid>,
select_pos: Option<Vec3<f32>>,
) -> StateUpdate {
let mut update = StateUpdate::from(data);
update.queued_inputs.insert(input);
update.select_pos = select_pos;
update.queued_inputs.insert(input, InputAttr { select_pos });
update
}
fn cancel_input(&self, data: &JoinData, input: InputKind) -> StateUpdate {
@ -51,7 +59,11 @@ pub trait CharacterBehavior {
ControlAction::Sneak => self.sneak(data),
ControlAction::Stand => self.stand(data),
ControlAction::Talk => self.talk(data),
ControlAction::StartInput { input, target } => self.start_input(data, input, target),
ControlAction::StartInput {
input,
target,
select_pos,
} => self.start_input(data, input, target, select_pos),
ControlAction::CancelInput(input) => self.cancel_input(data, input),
}
}

View File

@ -1,5 +1,5 @@
use crate::{
comp::{CharacterState, StateUpdate},
comp::{CharacterState, InputAttr, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
@ -59,5 +59,10 @@ impl CharacterBehavior for Data {
}
fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) {
handle_input(join, update, data.static_data.ability_info.input);
handle_input(
join,
update,
data.static_data.ability_info.input,
InputAttr::default(),
);
}

View File

@ -1,6 +1,6 @@
use crate::{
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
comp::{CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
comp::{tool::ToolKind, CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::{StageSection, *},
@ -187,6 +187,10 @@ impl CharacterBehavior for Data {
max_angle: self.static_data.max_angle.to_radians(),
applied: false,
hit_count: 0,
break_block: update
.select_pos
.filter(|_| self.static_data.ability_info.tool == Some(ToolKind::Pick))
.map(|p| p.map(|e| e.floor() as i32)),
});
} else if self.timer < self.static_data.swing_duration {
// Swings

View File

@ -1,6 +1,6 @@
use crate::{
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
comp::{CharacterState, Melee, StateUpdate},
comp::{tool::ToolKind, CharacterState, InputAttr, Melee, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
@ -213,6 +213,12 @@ impl CharacterBehavior for Data {
max_angle: self.static_data.stage_data[stage_index].angle.to_radians(),
applied: false,
hit_count: 0,
break_block: self
.static_data
.ability_info
.select_pos
.filter(|_| self.static_data.ability_info.tool == Some(ToolKind::Pick))
.map(|p| p.map(|e| e.floor() as i32)),
});
}
},
@ -285,7 +291,12 @@ impl CharacterBehavior for Data {
}
fn reset_state(data: &Data, join: &JoinData, update: &mut StateUpdate) {
handle_input(join, update, data.static_data.ability_info.input);
handle_input(
join,
update,
data.static_data.ability_info.input,
InputAttr::default(),
);
if let CharacterState::ComboMelee(c) = &mut update.character {
c.stage = (data.stage % data.static_data.num_stages) + 1;

View File

@ -1,6 +1,6 @@
use crate::{
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
comp::{CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
comp::{tool::ToolKind, CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
@ -164,6 +164,12 @@ impl CharacterBehavior for Data {
max_angle: self.static_data.angle.to_radians(),
applied: false,
hit_count: 0,
break_block: update
.select_pos
.filter(|_| {
self.static_data.ability_info.tool == Some(ToolKind::Pick)
})
.map(|p| p.map(|e| e.floor() as i32)),
});
}
update.character = CharacterState::DashMelee(Data {

View File

@ -1,6 +1,6 @@
use crate::{
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
comp::{CharacterState, Melee, StateUpdate},
comp::{tool::ToolKind, CharacterState, Melee, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::{StageSection, *},
@ -177,6 +177,10 @@ impl CharacterBehavior for Data {
max_angle: self.static_data.max_angle.to_radians(),
applied: false,
hit_count: 0,
break_block: update
.select_pos
.filter(|_| self.static_data.ability_info.tool == Some(ToolKind::Pick))
.map(|p| p.map(|e| e.floor() as i32)),
});
update.character = CharacterState::LeapMelee(Data {

View File

@ -1,5 +1,5 @@
use crate::{
comp::{CharacterState, InputKind, StateUpdate},
comp::{CharacterState, InputAttr, InputKind, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
@ -116,7 +116,7 @@ impl CharacterBehavior for Data {
// Done
if let Some((input, stage)) = self.was_combo {
if input_is_pressed(data, input) {
handle_input(data, &mut update, input);
handle_input(data, &mut update, input, InputAttr::default());
// If other states are introduced that progress through stages, add them
// here
if let CharacterState::ComboMelee(c) = &mut update.character {

View File

@ -1,6 +1,6 @@
use crate::{
combat::{Attack, AttackDamage, AttackEffect, CombatBuff, CombatEffect, CombatRequirement},
comp::{CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
comp::{tool::ToolKind, CharacterState, EnergyChange, EnergySource, Melee, StateUpdate},
consts::GRAVITY,
states::{
behavior::{CharacterBehavior, JoinData},
@ -141,6 +141,10 @@ impl CharacterBehavior for Data {
max_angle: 180_f32.to_radians(),
applied: false,
hit_count: 0,
break_block: update
.select_pos
.filter(|_| self.static_data.ability_info.tool == Some(ToolKind::Pick))
.map(|p| p.map(|e| e.floor() as i32)),
});
} else if self.timer < self.static_data.swing_duration {
if matches!(

View File

@ -5,7 +5,8 @@ use crate::{
item::{Hands, ItemKind, Tool, ToolKind},
quadruped_low, quadruped_medium, quadruped_small, ship,
skills::Skill,
theropod, Body, CharacterAbility, CharacterState, InputKind, InventoryAction, StateUpdate,
theropod, Body, CharacterAbility, CharacterState, InputAttr, InputKind, InventoryAction,
StateUpdate,
},
consts::{FRIC_GROUND, GRAVITY},
event::{LocalEvent, ServerEvent},
@ -345,7 +346,7 @@ fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
/// Checks if an input related to an attack is held. If one is, moves entity
/// into wielding state
pub fn handle_wield(data: &JoinData, update: &mut StateUpdate) {
if data.controller.queued_inputs.iter().any(|i| i.is_ability()) {
if data.controller.queued_inputs.keys().any(|i| i.is_ability()) {
attempt_wield(data, update);
}
}
@ -461,7 +462,12 @@ pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) {
}
}
fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
fn handle_ability(
data: &JoinData,
update: &mut StateUpdate,
input: InputKind,
input_attr: InputAttr,
) {
let hands = |equip_slot| match data.inventory.equipped(equip_slot).map(|i| i.kind()) {
Some(ItemKind::Tool(tool)) => Some(tool.hands),
_ => None,
@ -511,7 +517,12 @@ fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
{
update.character = (
&ability,
AbilityInfo::from_input(data, matches!(equip_slot, EquipSlot::Offhand), input),
AbilityInfo::from_input(
data,
matches!(equip_slot, EquipSlot::Offhand),
input,
input_attr,
),
)
.into();
}
@ -519,20 +530,25 @@ fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
}
pub fn handle_ability_input(data: &JoinData, update: &mut StateUpdate) {
if let Some(input) = data
if let Some((input, input_attr)) = data
.controller
.queued_inputs
.iter()
.find(|i| i.is_ability())
.find(|(i, _)| i.is_ability())
{
handle_ability(data, update, *input);
handle_ability(data, update, *input, input_attr.clone());
}
}
pub fn handle_input(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
pub fn handle_input(
data: &JoinData,
update: &mut StateUpdate,
input: InputKind,
input_attr: InputAttr,
) {
match input {
InputKind::Primary | InputKind::Secondary | InputKind::Ability(_) => {
handle_ability(data, update, input)
handle_ability(data, update, input, input_attr)
},
InputKind::Roll => handle_dodge_input(data, update),
InputKind::Jump => handle_jump(data, update),
@ -542,8 +558,8 @@ pub fn handle_input(data: &JoinData, update: &mut StateUpdate, input: InputKind)
pub fn attempt_input(data: &JoinData, update: &mut StateUpdate) {
// TODO: look into using first() when it becomes stable
if let Some(input) = data.controller.queued_inputs.iter().next() {
handle_input(data, update, *input);
if let Some((input, input_attr)) = data.controller.queued_inputs.iter().next() {
handle_input(data, update, *input, input_attr.clone());
}
}
@ -565,7 +581,7 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
if let CharacterState::ComboMelee(c) = data.character {
update.character = (
&ability,
AbilityInfo::from_input(data, false, InputKind::Roll),
AbilityInfo::from_input(data, false, InputKind::Roll, InputAttr::default()),
)
.into();
if let CharacterState::Roll(roll) = &mut update.character {
@ -575,7 +591,7 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
} else if data.character.is_wield() {
update.character = (
&ability,
AbilityInfo::from_input(data, false, InputKind::Roll),
AbilityInfo::from_input(data, false, InputKind::Roll, InputAttr::default()),
)
.into();
if let CharacterState::Roll(roll) = &mut update.character {
@ -584,7 +600,7 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
} else if data.character.is_stealthy() {
update.character = (
&ability,
AbilityInfo::from_input(data, false, InputKind::Roll),
AbilityInfo::from_input(data, false, InputKind::Roll, InputAttr::default()),
)
.into();
if let CharacterState::Roll(roll) = &mut update.character {
@ -593,7 +609,7 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
} else {
update.character = (
&ability,
AbilityInfo::from_input(data, false, InputKind::Roll),
AbilityInfo::from_input(data, false, InputKind::Roll, InputAttr::default()),
)
.into();
}
@ -635,7 +651,7 @@ pub fn handle_state_interrupt(data: &JoinData, update: &mut StateUpdate, attacks
}
pub fn input_is_pressed(data: &JoinData, input: InputKind) -> bool {
data.controller.queued_inputs.contains(&input)
data.controller.queued_inputs.contains_key(&input)
}
/// Determines what portion a state is in. Used in all attacks (eventually). Is
@ -691,10 +707,16 @@ pub struct AbilityInfo {
pub tool: Option<ToolKind>,
pub hand: Option<HandInfo>,
pub input: InputKind,
pub select_pos: Option<Vec3<f32>>,
}
impl AbilityInfo {
pub fn from_input(data: &JoinData, from_offhand: bool, input: InputKind) -> Self {
pub fn from_input(
data: &JoinData,
from_offhand: bool,
input: InputKind,
input_attr: InputAttr,
) -> Self {
let tool_data = if from_offhand {
unwrap_tool_data(data, EquipSlot::Offhand)
} else {
@ -709,7 +731,12 @@ impl AbilityInfo {
)
};
Self { tool, hand, input }
Self {
tool,
hand,
input,
select_pos: input_attr.select_pos,
}
}
}

View File

@ -1,5 +1,5 @@
use super::SpriteKind;
use crate::make_case_elim;
use crate::{comp::tool::ToolKind, make_case_elim};
use enum_iterator::IntoEnumIterator;
use hashbrown::HashMap;
use lazy_static::lazy_static;
@ -238,6 +238,16 @@ impl Block {
.unwrap_or(false)
}
/// The tool required to mine this block. For blocks that cannot be mined,
/// `None` is returned.
#[inline]
pub fn mine_tool(&self) -> Option<ToolKind> {
match self.kind() {
BlockKind::WeakRock => Some(ToolKind::Pick),
_ => self.get_sprite().and_then(|s| s.mine_tool()),
}
}
#[inline]
pub fn is_opaque(&self) -> bool { self.kind().is_filled() }

View File

@ -1,4 +1,4 @@
use crate::make_case_elim;
use crate::{comp::tool::ToolKind, make_case_elim};
use enum_iterator::IntoEnumIterator;
use hashbrown::HashMap;
use lazy_static::lazy_static;
@ -222,8 +222,8 @@ impl SpriteKind {
SpriteKind::Apple => true,
SpriteKind::Mushroom => true,
SpriteKind::CaveMushroom => true,
SpriteKind::Velorite => true,
SpriteKind::VeloriteFrag => true,
// SpriteKind::Velorite => true,
// SpriteKind::VeloriteFrag => true,
SpriteKind::Chest => true,
SpriteKind::Coconut => true,
SpriteKind::Stones => true,
@ -235,23 +235,44 @@ impl SpriteKind {
SpriteKind::Bowl => true,
SpriteKind::ChestBurried => true,
SpriteKind::Mud => true,
SpriteKind::Amethyst => true,
SpriteKind::Ruby => true,
SpriteKind::Diamond => true,
SpriteKind::Sapphire => true,
SpriteKind::Emerald => true,
SpriteKind::Topaz => true,
SpriteKind::AmethystSmall => true,
SpriteKind::TopazSmall => true,
SpriteKind::DiamondSmall => true,
SpriteKind::RubySmall => true,
SpriteKind::EmeraldSmall => true,
SpriteKind::SapphireSmall => true,
// SpriteKind::Amethyst => true,
// SpriteKind::Ruby => true,
// SpriteKind::Diamond => true,
// SpriteKind::Sapphire => true,
// SpriteKind::Emerald => true,
// SpriteKind::Topaz => true,
// SpriteKind::AmethystSmall => true,
// SpriteKind::TopazSmall => true,
// SpriteKind::DiamondSmall => true,
// SpriteKind::RubySmall => true,
// SpriteKind::EmeraldSmall => true,
// SpriteKind::SapphireSmall => true,
SpriteKind::Seashells => true,
_ => false,
}
}
pub fn mine_tool(&self) -> Option<ToolKind> {
match self {
SpriteKind::Velorite
| SpriteKind::VeloriteFrag
// Gems
| SpriteKind::Amethyst
| SpriteKind::Ruby
| SpriteKind::Diamond
| SpriteKind::Sapphire
| SpriteKind::Emerald
| SpriteKind::Topaz
| SpriteKind::AmethystSmall
| SpriteKind::TopazSmall
| SpriteKind::DiamondSmall
| SpriteKind::RubySmall
| SpriteKind::EmeraldSmall
| SpriteKind::SapphireSmall => Some(ToolKind::Pick),
_ => None,
}
}
pub fn has_ori(&self) -> bool {
matches!(
self,

View File

@ -63,6 +63,21 @@ impl<'a> System<'a> for Sys {
}
melee_attack.applied = true;
// Scales
let eye_pos = pos.0 + Vec3::unit_z() * body.eye_height();
let scale = read_data.scales.get(attacker).map_or(1.0, |s| s.0);
let rad = body.radius() * scale;
// Mine blocks broken by the attack
if let Some(block_pos) = melee_attack.break_block {
// Check distance to block
if eye_pos.distance_squared(block_pos.map(|e| e as f32 + 0.5))
< (rad + scale * melee_attack.range).powi(2)
{
server_emitter.emit(ServerEvent::MineBlock { pos: block_pos });
}
}
// Go through all other entities
for (target, pos_b, health_b, body_b) in (
&read_data.entities,
@ -80,9 +95,7 @@ impl<'a> System<'a> for Sys {
let ori2 = Vec2::from(look_dir);
// Scales
let scale = read_data.scales.get(attacker).map_or(1.0, |s| s.0);
let scale_b = read_data.scales.get(target).map_or(1.0, |s| s.0);
let rad = body.radius() * scale;
let rad_b = body_b.radius() * scale_b;
// Check if entity is dodging

View File

@ -1,10 +1,12 @@
use specs::{world::WorldExt, Entity as EcsEntity};
use specs::{world::WorldExt, Builder, Entity as EcsEntity};
use tracing::error;
use vek::*;
use common::{
comp::{self, agent::AgentEvent, inventory::slot::EquipSlot, item, slot::Slot, Inventory, Pos},
consts::MAX_MOUNT_RANGE,
uid::Uid,
vol::ReadVol,
};
use common_net::{msg::ServerGeneral, sync::WorldSyncExt};
@ -254,3 +256,21 @@ fn within_mounting_range(player_position: Option<&Pos>, mount_position: Option<&
_ => false,
}
}
pub fn handle_mine_block(server: &mut Server, pos: Vec3<i32>) {
let state = server.state_mut();
if state.can_set_block(pos) {
let block = state.terrain().get(pos).ok().copied();
if let Some(block) = block {
if let Some(item) = comp::Item::try_reclaim_from_block(block) {
state
.create_object(Default::default(), comp::object::Body::Pouch)
.with(comp::Pos(pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)))
.with(item)
.build();
}
state.set_block(pos, block.into_vacant());
}
}
}

View File

@ -13,7 +13,8 @@ use entity_manipulation::{
use group_manip::handle_group;
use information::handle_site_info;
use interaction::{
handle_lantern, handle_mount, handle_npc_interaction, handle_possess, handle_unmount,
handle_lantern, handle_mine_block, handle_mount, handle_npc_interaction, handle_possess,
handle_unmount,
};
use inventory_manip::handle_inventory;
use invite::{handle_invite, handle_invite_response};
@ -190,6 +191,7 @@ impl Server {
handle_combo_change(&self, entity, change)
},
ServerEvent::RequestSiteInfo { entity, id } => handle_site_info(&self, entity, id),
ServerEvent::MineBlock { pos } => handle_mine_block(self, pos),
}
}

View File

@ -150,6 +150,7 @@ pub fn skill_to_db_string(skill: comp::skills::Skill) -> String {
| UnlockGroup(SkillGroupKind::Weapon(ToolKind::SwordSimple))
| UnlockGroup(SkillGroupKind::Weapon(ToolKind::Debug))
| UnlockGroup(SkillGroupKind::Weapon(ToolKind::Farming))
| UnlockGroup(SkillGroupKind::Weapon(ToolKind::Pick))
| UnlockGroup(SkillGroupKind::Weapon(ToolKind::Empty))
| UnlockGroup(SkillGroupKind::Weapon(ToolKind::Unique(_)))
| UnlockGroup(SkillGroupKind::General) => {
@ -292,6 +293,7 @@ pub fn skill_group_to_db_string(skill_group: comp::skills::SkillGroupKind) -> St
| Weapon(ToolKind::SwordSimple)
| Weapon(ToolKind::Debug)
| Weapon(ToolKind::Farming)
| Weapon(ToolKind::Pick)
| Weapon(ToolKind::Empty)
| Weapon(ToolKind::Unique(_)) => panic!(
"Tried to add unsupported skill group to database: {:?}",

View File

@ -233,10 +233,9 @@ impl<'a> System<'a> for Sys {
// If we need to be able to have entities (dragons maybe?) both fly and
// run/jump, we probably need to refactor to avoid resetting the controller
// every frame.
controller.actions.push(ControlAction::StartInput {
input: InputKind::Fly,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Fly));
}
let flees = alignment
@ -630,10 +629,9 @@ impl<'a> AgentData<'a> {
.1
.map_or(true, |b| b.is_some())
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Fly,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Fly));
} else {
controller
.actions
@ -1136,10 +1134,9 @@ impl<'a> AgentData<'a> {
fn jump_if(&self, controller: &mut Controller, condition: bool) {
if condition {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Jump,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Jump));
} else {
controller
.actions
@ -1259,10 +1256,9 @@ impl<'a> AgentData<'a> {
match tactic {
Tactic::Melee => {
if dist_sqrd < (min_attack_dist * self.scale).powi(2) {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
controller.inputs.move_dir = Vec2::zero();
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
if let Some((bearing, speed)) = agent.chaser.chase(
@ -1285,10 +1281,9 @@ impl<'a> AgentData<'a> {
&& dist_sqrd < 16.0f32.powi(2)
&& thread_rng().gen::<f32>() < 0.02
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Roll,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Roll));
}
} else {
agent.target = None;
@ -1303,10 +1298,9 @@ impl<'a> AgentData<'a> {
.push(ControlAction::CancelInput(InputKind::Secondary));
agent.action_timer = 0.0;
} else if agent.action_timer > 4.0 && self.energy.current() > 10 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer += dt.0;
} else if self
.stats
@ -1315,16 +1309,14 @@ impl<'a> AgentData<'a> {
&& self.energy.current() > 800
&& thread_rng().gen_bool(0.5)
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
agent.action_timer += dt.0;
} else {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
}
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
@ -1347,10 +1339,9 @@ impl<'a> AgentData<'a> {
&& dist_sqrd < 16.0f32.powi(2)
&& thread_rng().gen::<f32>() < 0.02
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Roll,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Roll));
}
} else {
agent.target = None;
@ -1365,10 +1356,9 @@ impl<'a> AgentData<'a> {
.push(ControlAction::CancelInput(InputKind::Secondary));
agent.action_timer = 0.0;
} else if agent.action_timer > 2.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer += dt.0;
} else if self
.stats
@ -1377,16 +1367,14 @@ impl<'a> AgentData<'a> {
&& self.energy.current() > 700
&& thread_rng().gen_bool(0.9)
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
agent.action_timer += dt.0;
} else {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
}
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
@ -1409,10 +1397,9 @@ impl<'a> AgentData<'a> {
.has_skill(Skill::Hammer(HammerSkill::UnlockLeap))
&& agent.action_timer > 5.0
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
agent.action_timer = 0.0;
} else {
agent.action_timer += dt.0;
@ -1428,10 +1415,9 @@ impl<'a> AgentData<'a> {
&& dist_sqrd < 16.0f32.powi(2)
&& thread_rng().gen::<f32>() < 0.02
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Roll,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Roll));
}
} else {
agent.target = None;
@ -1447,18 +1433,16 @@ impl<'a> AgentData<'a> {
&& agent.action_timer < 2.0
&& self.energy.current() > 600
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
agent.action_timer += dt.0;
} else if agent.action_timer > 2.0 {
agent.action_timer = 0.0;
} else {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
}
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
@ -1476,10 +1460,9 @@ impl<'a> AgentData<'a> {
controller.inputs.move_dir =
bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed;
if agent.action_timer > 4.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer = 0.0;
} else {
agent.action_timer += dt.0;
@ -1495,10 +1478,9 @@ impl<'a> AgentData<'a> {
&& dist_sqrd < 16.0f32.powi(2)
&& thread_rng().gen::<f32>() < 0.02
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Roll,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Roll));
}
} else {
agent.target = None;
@ -1508,10 +1490,9 @@ impl<'a> AgentData<'a> {
if self.body.map(|b| b.is_humanoid()).unwrap_or(false)
&& dist_sqrd < (2.0 * min_attack_dist * self.scale).powi(2)
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Roll,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Roll));
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
if let Some((bearing, speed)) = agent.chaser.chase(
&*terrain,
@ -1536,10 +1517,9 @@ impl<'a> AgentData<'a> {
.push(ControlAction::CancelInput(InputKind::Secondary));
agent.action_timer = 0.0;
} else if agent.action_timer > 2.0 && self.energy.current() > 300 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer += dt.0;
} else if self
.stats
@ -1551,19 +1531,17 @@ impl<'a> AgentData<'a> {
controller
.actions
.push(ControlAction::CancelInput(InputKind::Secondary));
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
agent.action_timer += dt.0;
} else {
controller
.actions
.push(ControlAction::CancelInput(InputKind::Secondary));
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
}
} else {
@ -1577,10 +1555,9 @@ impl<'a> AgentData<'a> {
&& dist_sqrd < 16.0f32.powi(2)
&& thread_rng().gen::<f32>() < 0.02
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Roll,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Roll));
}
} else if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) {
if let Some((bearing, speed)) = agent.chaser.chase(
@ -1606,10 +1583,9 @@ impl<'a> AgentData<'a> {
if self.body.map(|b| b.is_humanoid()).unwrap_or(false)
&& dist_sqrd < (min_attack_dist * self.scale).powi(2)
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Roll,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Roll));
} else if dist_sqrd < (5.0 * min_attack_dist * self.scale).powi(2) {
if agent.action_timer < 1.5 {
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
@ -1635,20 +1611,17 @@ impl<'a> AgentData<'a> {
&& self.energy.current() > 800
&& thread_rng().gen::<f32>() > 0.8
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
} else if self.energy.current() > 10 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
} else {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
}
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
if let Some((bearing, speed)) = agent.chaser.chase(
@ -1668,10 +1641,9 @@ impl<'a> AgentData<'a> {
.try_normalized()
.unwrap_or_else(Vec2::zero)
* speed;
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
} else {
controller.inputs.move_dir =
bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed;
@ -1683,10 +1655,9 @@ impl<'a> AgentData<'a> {
&& dist_sqrd < 16.0f32.powi(2)
&& thread_rng().gen::<f32>() < 0.02
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Roll,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Roll));
}
} else if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) {
if let Some((bearing, speed)) = agent.chaser.chase(
@ -1713,17 +1684,15 @@ impl<'a> AgentData<'a> {
// 2.0 is temporary correction factor to allow them to melee with their
// large hitbox
controller.inputs.move_dir = Vec2::zero();
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
//controller.inputs.primary.set_state(true);
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
if self.vel.0.is_approx_zero() {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
}
if let Some((bearing, speed)) = agent.chaser.chase(
&*terrain,
@ -1739,10 +1708,9 @@ impl<'a> AgentData<'a> {
controller.inputs.move_dir =
bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed;
if agent.action_timer > 5.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer = 0.0;
} else {
agent.action_timer += dt.0;
@ -1765,10 +1733,9 @@ impl<'a> AgentData<'a> {
if dist_sqrd < (min_attack_dist * self.scale).powi(2) && thread_rng().gen_bool(0.5)
{
controller.inputs.move_dir = Vec2::zero();
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
} else if dist_sqrd < (radius as f32 * min_attack_dist * self.scale).powi(2) {
controller.inputs.move_dir = (self.pos.0 - tgt_pos.0)
.xy()
@ -1785,10 +1752,9 @@ impl<'a> AgentData<'a> {
.unwrap_or_else(Vec2::unit_y);
agent.action_timer += dt.0;
} else if agent.action_timer < circle_time as f32 + 0.5 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer += dt.0;
} else if agent.action_timer < 2.0 * circle_time as f32 + 0.5 {
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
@ -1798,10 +1764,9 @@ impl<'a> AgentData<'a> {
.unwrap_or_else(Vec2::unit_y);
agent.action_timer += dt.0;
} else if agent.action_timer < 2.0 * circle_time as f32 + 1.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer += dt.0;
} else {
agent.action_timer = 0.0;
@ -1832,10 +1797,9 @@ impl<'a> AgentData<'a> {
.xy()
.try_normalized()
.unwrap_or_else(Vec2::unit_y);
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
if let Some((bearing, speed)) = agent.chaser.chase(
&*terrain,
@ -1867,10 +1831,9 @@ impl<'a> AgentData<'a> {
* speed;
agent.action_timer += dt.0;
}
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
self.jump_if(controller, bearing.z > 1.5);
controller.inputs.move_z = bearing.z;
} else {
@ -1894,16 +1857,14 @@ impl<'a> AgentData<'a> {
.push(ControlAction::CancelInput(InputKind::Primary));
agent.action_timer = 0.0;
} else if agent.action_timer > 1.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
} else {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer += dt.0;
}
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
@ -1934,17 +1895,15 @@ impl<'a> AgentData<'a> {
Tactic::QuadLowQuick => {
if dist_sqrd < (1.5 * min_attack_dist * self.scale).powi(2) {
controller.inputs.move_dir = Vec2::zero();
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
} else if dist_sqrd < (3.0 * min_attack_dist * self.scale).powi(2)
&& dist_sqrd > (2.0 * min_attack_dist * self.scale).powi(2)
{
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
.xy()
.rotated_z(-0.47 * PI)
@ -1976,16 +1935,14 @@ impl<'a> AgentData<'a> {
if agent.action_timer > 5.0 {
agent.action_timer = 0.0;
} else if agent.action_timer > 2.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer += dt.0;
} else {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
}
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
@ -2011,15 +1968,13 @@ impl<'a> AgentData<'a> {
Tactic::QuadMedJump => {
if dist_sqrd < (1.5 * min_attack_dist * self.scale).powi(2) {
controller.inputs.move_dir = Vec2::zero();
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
} else if dist_sqrd < (5.0 * min_attack_dist * self.scale).powi(2) {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
if let Some((bearing, speed)) = agent.chaser.chase(
&*terrain,
@ -2032,10 +1987,9 @@ impl<'a> AgentData<'a> {
},
) {
if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
controller.inputs.move_dir =
bearing.xy().try_normalized().unwrap_or_else(Vec2::zero) * speed;
} else {
@ -2053,16 +2007,14 @@ impl<'a> AgentData<'a> {
if dist_sqrd < (min_attack_dist * self.scale).powi(2) {
controller.inputs.move_dir = Vec2::zero();
if agent.action_timer < 2.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
agent.action_timer += dt.0;
} else if agent.action_timer < 3.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
} else {
agent.action_timer = 0.0;
@ -2090,10 +2042,9 @@ impl<'a> AgentData<'a> {
Tactic::Lavadrake | Tactic::QuadLowBeam => {
if dist_sqrd < (2.5 * min_attack_dist * self.scale).powi(2) {
controller.inputs.move_dir = Vec2::zero();
controller.actions.push(ControlAction::StartInput {
input: InputKind::Secondary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Secondary));
} else if dist_sqrd < (7.0 * min_attack_dist * self.scale).powi(2) {
if agent.action_timer < 2.0 {
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
@ -2101,10 +2052,9 @@ impl<'a> AgentData<'a> {
.rotated_z(0.47 * PI)
.try_normalized()
.unwrap_or_else(Vec2::unit_y);
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
} else if agent.action_timer < 4.0 {
controller.inputs.move_dir = (tgt_pos.0 - self.pos.0)
@ -2112,16 +2062,14 @@ impl<'a> AgentData<'a> {
.rotated_z(-0.47 * PI)
.try_normalized()
.unwrap_or_else(Vec2::unit_y);
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
agent.action_timer += dt.0;
} else if agent.action_timer < 6.0 {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Ability(0),
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Ability(0)));
agent.action_timer += dt.0;
} else {
agent.action_timer = 0.0;
@ -2149,10 +2097,9 @@ impl<'a> AgentData<'a> {
Tactic::Theropod => {
if dist_sqrd < (2.0 * min_attack_dist * self.scale).powi(2) {
controller.inputs.move_dir = Vec2::zero();
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
} else if dist_sqrd < MAX_CHASE_DIST.powi(2) {
if let Some((bearing, speed)) = agent.chaser.chase(
&*terrain,
@ -2175,10 +2122,9 @@ impl<'a> AgentData<'a> {
},
Tactic::Turret => {
if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
} else {
agent.target = None;
}
@ -2186,10 +2132,9 @@ impl<'a> AgentData<'a> {
Tactic::FixedTurret => {
controller.inputs.look_dir = self.ori.look_dir();
if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
} else {
agent.target = None;
}
@ -2203,10 +2148,9 @@ impl<'a> AgentData<'a> {
.unwrap_or_default(),
);
if can_see_tgt(&*terrain, self.pos, tgt_pos, dist_sqrd) {
controller.actions.push(ControlAction::StartInput {
input: InputKind::Primary,
target: None,
});
controller
.actions
.push(ControlAction::basic_start(InputKind::Primary));
} else {
agent.target = None;
}

View File

@ -116,7 +116,7 @@ impl Animation for AlphaAnimation {
next.head.orientation =
Quaternion::rotation_z(0.0 + move1 * -1.5 + move2 * 2.5 + move3 * -1.0);
},
Some(ToolKind::Hammer) | Some(ToolKind::HammerSimple) => {
Some(ToolKind::Hammer) | Some(ToolKind::HammerSimple) | Some(ToolKind::Pick) => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0),
Some(StageSection::Swing) => (1.0, anim_time, 0.0),

View File

@ -52,7 +52,7 @@ impl Animation for ChargeswingAnimation {
let slowrise = test * (1.0 - move3);
let move2 = move2base * (1.0 - move3);
if let Some(ToolKind::Hammer) = active_tool_kind {
if let Some(ToolKind::Hammer | ToolKind::Pick) = active_tool_kind {
next.main.position = Vec3::new(0.0, 0.0, 0.0);
next.main.orientation = Quaternion::rotation_x(0.0);
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2 + (move2 * -8.0));

View File

@ -40,7 +40,7 @@ impl Animation for EquipAnimation {
next.hand_l.position = Vec3::new(-7.0, -5.0, 17.0);
next.hand_r.position = Vec3::new(-5.0, -4.5, 14.0);
},
Some(ToolKind::Hammer) => {
Some(ToolKind::Hammer | ToolKind::Pick) => {
next.hand_l.position = Vec3::new(-5.0, -5.0, 13.0);
next.hand_r.position = Vec3::new(-3.0, -4.5, 10.0);
},

View File

@ -39,7 +39,7 @@ impl Animation for LeapAnimation {
_ => (0.0, 0.0, 0.0, 0.0),
};
if let Some(ToolKind::Hammer) = active_tool_kind {
if let Some(ToolKind::Hammer | ToolKind::Pick) = active_tool_kind {
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
next.hand_l.orientation = Quaternion::rotation_x(s_a.hhl.3);
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);

View File

@ -92,7 +92,7 @@ impl Animation for StaggeredAnimation {
* Quaternion::rotation_y(s_a.ac.4)
* Quaternion::rotation_z(s_a.ac.5);
},
Some(ToolKind::Hammer) => {
Some(ToolKind::Hammer | ToolKind::Pick) => {
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_y(s_a.hhl.4);

View File

@ -90,7 +90,7 @@ impl Animation for StunnedAnimation {
* Quaternion::rotation_y(s_a.ac.4)
* Quaternion::rotation_z(s_a.ac.5);
},
Some(ToolKind::Hammer) => {
Some(ToolKind::Hammer | ToolKind::Pick) => {
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_y(s_a.hhl.4);

View File

@ -217,7 +217,7 @@ impl Animation for WieldAnimation {
* Quaternion::rotation_y(s_a.ac.4)
* Quaternion::rotation_z(s_a.ac.5);
},
Some(ToolKind::Hammer) | Some(ToolKind::HammerSimple) => {
Some(ToolKind::Hammer) | Some(ToolKind::HammerSimple) | Some(ToolKind::Pick) => {
next.main.position = Vec3::new(0.0, 0.0, 0.0);
next.main.orientation = Quaternion::rotation_x(0.0);
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);

View File

@ -217,5 +217,6 @@ fn empty_ability_info() -> states::utils::AbilityInfo {
tool: None,
hand: None,
input: InputKind::Primary,
select_pos: None,
}
}

View File

@ -227,6 +227,7 @@ fn tool_desc(tool: &Tool, components: &[Item], msm: &MaterialStatManifest, desc:
ToolKind::Unique(_) => "Unique",
ToolKind::Debug => "Debug",
ToolKind::Farming => "Farming Tool",
ToolKind::Pick => "Pickaxe",
ToolKind::Empty => "Empty",
};

View File

@ -10,8 +10,10 @@ use common::{
assets::AssetExt,
comp,
comp::{
inventory::slot::Slot, invite::InviteKind, ChatMsg, ChatType, InputKind,
InventoryUpdateEvent, Pos, Vel,
inventory::slot::{EquipSlot, Slot},
invite::InviteKind,
item::{tool::ToolKind, ItemDesc},
ChatMsg, ChatType, InputKind, InventoryUpdateEvent, Pos, Vel,
},
consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE},
outcome::Outcome,
@ -302,30 +304,44 @@ impl PlayState for SessionState {
// Throw out distance info, it will be useful in the future
self.target_entity = target_entity.map(|x| x.0);
let player_entity = self.client.borrow().entity();
let can_build = self
.client
.borrow()
.state()
.read_storage::<comp::CanBuild>()
.get(self.client.borrow().entity())
.get(player_entity)
.is_some();
let is_mining = self
.client
.borrow()
.inventories()
.get(player_entity)
.and_then(|inv| inv.equipped(EquipSlot::Mainhand))
.and_then(|item| item.tool())
.map_or(false, |tool| tool.kind == ToolKind::Pick)
&& self.client.borrow().is_wielding() == Some(true);
self.interactable = select_interactable(
&self.client.borrow(),
target_entity,
select_pos,
select_pos.map(|sp| sp.map(|e| e.floor() as i32)),
&self.scene,
);
// Only highlight interactables
// unless in build mode where select_pos highlighted
self.scene
.set_select_pos(select_pos.filter(|_| can_build).or_else(
|| match self.interactable {
self.scene.set_select_pos(
select_pos
.map(|sp| sp.map(|e| e.floor() as i32))
.filter(|_| can_build || is_mining)
.or_else(|| match self.interactable {
Some(Interactable::Block(_, block_pos)) => Some(block_pos),
_ => None,
},
));
}),
);
// Handle window events.
for event in events {
@ -351,10 +367,10 @@ impl PlayState for SessionState {
let mut client = self.client.borrow_mut();
if state && can_build {
if let Some(select_pos) = select_pos {
client.remove_block(select_pos);
client.remove_block(select_pos.map(|e| e.floor() as i32));
}
} else {
client.handle_input(InputKind::Primary, state);
client.handle_input(InputKind::Primary, state, select_pos);
}
},
GameInput::Secondary => {
@ -362,10 +378,13 @@ impl PlayState for SessionState {
if state && can_build {
if let Some(build_pos) = build_pos {
client.place_block(build_pos, self.selected_block);
client.place_block(
build_pos.map(|e| e.floor() as i32),
self.selected_block,
);
}
} else {
client.handle_input(InputKind::Secondary, state);
client.handle_input(InputKind::Secondary, state, select_pos);
}
},
GameInput::Roll => {
@ -373,13 +392,18 @@ impl PlayState for SessionState {
if can_build {
if state {
if let Some(block) = select_pos.and_then(|sp| {
client.state().terrain().get(sp).ok().copied()
client
.state()
.terrain()
.get(sp.map(|e| e.floor() as i32))
.ok()
.copied()
}) {
self.selected_block = block;
}
}
} else {
client.handle_input(InputKind::Roll, state);
client.handle_input(InputKind::Roll, state, select_pos);
}
},
GameInput::Respawn => {
@ -390,7 +414,7 @@ impl PlayState for SessionState {
},
GameInput::Jump => {
let mut client = self.client.borrow_mut();
client.handle_input(InputKind::Jump, state);
client.handle_input(InputKind::Jump, state, select_pos);
},
GameInput::SwimUp => {
self.key_state.swim_up = state;
@ -451,7 +475,7 @@ impl PlayState for SessionState {
// controller change
self.key_state.fly ^= state;
let mut client = self.client.borrow_mut();
client.handle_input(InputKind::Fly, self.key_state.fly);
client.handle_input(InputKind::Fly, self.key_state.fly, select_pos);
},
GameInput::Climb => {
self.key_state.climb_up = state;
@ -1209,11 +1233,11 @@ impl PlayState for SessionState {
},
HudEvent::Ability3(state) => {
let mut client = self.client.borrow_mut();
client.handle_input(InputKind::Ability(0), state);
client.handle_input(InputKind::Ability(0), state, select_pos);
},
HudEvent::Ability4(state) => {
let mut client = self.client.borrow_mut();
client.handle_input(InputKind::Ability(1), state);
client.handle_input(InputKind::Ability(1), state, select_pos);
},
HudEvent::ChangeFOV(new_fov) => {
global_state.settings.graphics.fov = new_fov;
@ -1528,8 +1552,8 @@ fn under_cursor(
cam_pos: Vec3<f32>,
cam_dir: Vec3<f32>,
) -> (
Option<Vec3<i32>>,
Option<Vec3<i32>>,
Option<Vec3<f32>>,
Option<Vec3<f32>>,
Option<(specs::Entity, f32)>,
) {
span!(_guard, "under_cursor");
@ -1555,7 +1579,7 @@ fn under_cursor(
let cam_ray = terrain
.ray(cam_pos, cam_pos + cam_dir * 100.0)
.until(|block| block.is_filled() || block.is_collectible())
.until(|block| block.is_filled() || block.is_collectible() || block.mine_tool().is_some())
.cast();
let cam_dist = cam_ray.0;
@ -1566,8 +1590,8 @@ fn under_cursor(
<= MAX_PICKUP_RANGE)
{
(
Some((cam_pos + cam_dir * (cam_dist - 0.01)).map(|e| e.floor() as i32)),
Some((cam_pos + cam_dir * (cam_dist + 0.01)).map(|e| e.floor() as i32)),
Some(cam_pos + cam_dir * (cam_dist - 0.01)),
Some(cam_pos + cam_dir * (cam_dist + 0.01)),
)
} else {
(None, None)
@ -1677,7 +1701,8 @@ fn select_interactable(
.and_then(|(e, dist_to_player)| (dist_to_player < MAX_PICKUP_RANGE).then_some(Interactable::Entity(e)))
.or_else(|| selected_pos.and_then(|sp|
client.state().terrain().get(sp).ok().copied()
.filter(Block::is_collectible).map(|b| Interactable::Block(b, sp))
.filter(|b| b.is_collectible() || b.mine_tool().is_some())
.map(|b| Interactable::Block(b, sp))
))
.or_else(|| {
let ecs = client.state().ecs();