mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/small-fixes' into 'master'
Actually small fixes this time See merge request veloren/veloren!1175
This commit is contained in:
commit
1df9fd6600
@ -60,6 +60,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Connection screen fails after 4 minutes if it can't connect to the server instead of 80 minutes
|
||||
- Rebuilt quadruped_medium animation and assets
|
||||
- Disabled destruction of most blocks by explosions
|
||||
- Disable damage to pets
|
||||
- Made pets healable
|
||||
- Rebalanced fire staff
|
||||
- Animals are more effective in combat
|
||||
- Pathfinding is much smoother and pets are cleverer
|
||||
- Animals run/turn at different speeds
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -5,4 +5,12 @@ vec3 srgb_to_linear(vec3 srgb) {
|
||||
vec3 lower = srgb/vec3(12.92);
|
||||
|
||||
return mix(higher, lower, cutoff);
|
||||
}
|
||||
}
|
||||
|
||||
vec3 linear_to_srgb(vec3 linear) {
|
||||
bvec3 cutoff = lessThan(linear, vec3(0.0031308));
|
||||
vec3 higher = vec3(1.055) * pow(linear, vec3(1.0 / 2.4)) - vec3(0.055);
|
||||
vec3 lower = linear * vec3(12.92);
|
||||
|
||||
return mix(higher, lower, cutoff);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ void main() {
|
||||
|
||||
// Select glowing
|
||||
if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) {
|
||||
f_col *= 4.0;
|
||||
f_col *= 8.0;
|
||||
}
|
||||
|
||||
f_light = 1.0;
|
||||
|
@ -21,6 +21,10 @@ out vec4 tgt_color;
|
||||
#include <sky.glsl>
|
||||
#include <light.glsl>
|
||||
|
||||
float vmax(vec3 v) {
|
||||
return max(v.x, max(v.y, v.z));
|
||||
}
|
||||
|
||||
void main() {
|
||||
// First 3 normals are negative, next 3 are positive
|
||||
vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1));
|
||||
@ -46,6 +50,14 @@ void main() {
|
||||
diffuse_light += point_light * ao;
|
||||
|
||||
vec3 col = f_col + hash(vec4(floor(f_chunk_pos * 3.0 - f_norm * 0.5), 0)) * 0.02; // Small-scale noise
|
||||
|
||||
// Select glowing
|
||||
if (select_pos.w > 0 && select_pos.xyz == floor(f_pos - f_norm * 0.01)) {
|
||||
if (vmax(abs(mod(f_pos - f_norm * 0.5, 1.0) - 0.5)) > 0.45) {
|
||||
col *= 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 surf_color = illuminate(srgb_to_linear(col), light, diffuse_light, ambient_light);
|
||||
|
||||
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::path::Chaser;
|
||||
use specs::{Component, Entity as EcsEntity};
|
||||
use crate::{path::Chaser, sync::Uid};
|
||||
use specs::{Component, Entity as EcsEntity, FlaggedStorage};
|
||||
use specs_idvs::IdvStorage;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Alignment {
|
||||
/// Wild animals and gentle giants
|
||||
Wild,
|
||||
@ -14,7 +14,7 @@ pub enum Alignment {
|
||||
/// Farm animals and pets of villagers
|
||||
Tame,
|
||||
/// Pets you've tamed with a collar
|
||||
Owned(EcsEntity),
|
||||
Owned(Uid),
|
||||
}
|
||||
|
||||
impl Alignment {
|
||||
@ -40,10 +40,18 @@ impl Alignment {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove this hack
|
||||
pub fn is_friendly_to_players(&self) -> bool {
|
||||
match self {
|
||||
Alignment::Npc | Alignment::Tame | Alignment::Owned(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Alignment {
|
||||
type Storage = IdvStorage<Self>;
|
||||
type Storage = FlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -331,18 +331,18 @@ impl Tool {
|
||||
},
|
||||
BasicRanged {
|
||||
energy_cost: 0,
|
||||
holdable: true,
|
||||
holdable: false,
|
||||
prepare_duration: Duration::from_millis(250),
|
||||
recover_duration: Duration::from_millis(200),
|
||||
recover_duration: Duration::from_millis(600),
|
||||
projectile: Projectile {
|
||||
hit_solid: vec![projectile::Effect::Vanish],
|
||||
hit_entity: vec![
|
||||
projectile::Effect::Damage(HealthChange {
|
||||
// TODO: This should not be fixed (?)
|
||||
amount: -2,
|
||||
amount: -3,
|
||||
cause: HealthSource::Projectile { owner: None },
|
||||
}),
|
||||
projectile::Effect::RewardEnergy(100),
|
||||
projectile::Effect::RewardEnergy(150),
|
||||
projectile::Effect::Vanish,
|
||||
],
|
||||
time_left: Duration::from_secs(20),
|
||||
@ -350,7 +350,7 @@ impl Tool {
|
||||
},
|
||||
projectile_body: Body::Object(object::Body::BoltFire),
|
||||
projectile_light: Some(LightEmitter {
|
||||
col: (0.72, 0.11, 0.11).into(),
|
||||
col: (0.85, 0.5, 0.11).into(),
|
||||
..Default::default()
|
||||
}),
|
||||
|
||||
@ -375,7 +375,7 @@ impl Tool {
|
||||
},
|
||||
projectile_body: Body::Object(object::Body::BoltFireBig),
|
||||
projectile_light: Some(LightEmitter {
|
||||
col: (0.72, 0.11, 0.11).into(),
|
||||
col: (1.0, 0.75, 0.11).into(),
|
||||
..Default::default()
|
||||
}),
|
||||
|
||||
@ -449,8 +449,8 @@ impl Tool {
|
||||
buildup_duration: Duration::from_millis(0),
|
||||
recover_duration: Duration::from_millis(1000),
|
||||
base_healthchange: -2,
|
||||
range: 3.5,
|
||||
max_angle: 45.0,
|
||||
range: 5.0,
|
||||
max_angle: 60.0,
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,8 @@
|
||||
bool_to_option,
|
||||
label_break_value,
|
||||
trait_alias,
|
||||
type_alias_impl_trait
|
||||
type_alias_impl_trait,
|
||||
option_zip
|
||||
)]
|
||||
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
CharacterAbility, ItemConfig, Loadout,
|
||||
},
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Builder for character Loadouts, containing weapon and armour items belonging
|
||||
/// to a character, along with some helper methods for loading Items and
|
||||
@ -61,6 +62,40 @@ impl LoadoutBuilder {
|
||||
)))
|
||||
}
|
||||
|
||||
/// Default animal configuration
|
||||
pub fn animal() -> Self {
|
||||
Self(Loadout {
|
||||
active_item: Some(ItemConfig {
|
||||
item: assets::load_expect_cloned("common.items.weapons.empty"),
|
||||
ability1: Some(CharacterAbility::BasicMelee {
|
||||
energy_cost: 10,
|
||||
buildup_duration: Duration::from_millis(600),
|
||||
recover_duration: Duration::from_millis(100),
|
||||
base_healthchange: -6,
|
||||
range: 5.0,
|
||||
max_angle: 80.0,
|
||||
}),
|
||||
ability2: None,
|
||||
ability3: None,
|
||||
block_ability: None,
|
||||
dodge_ability: None,
|
||||
}),
|
||||
second_item: None,
|
||||
shoulder: None,
|
||||
chest: None,
|
||||
belt: None,
|
||||
hand: None,
|
||||
pants: None,
|
||||
foot: None,
|
||||
back: None,
|
||||
ring: None,
|
||||
neck: None,
|
||||
lantern: None,
|
||||
head: None,
|
||||
tabard: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the default [ItemConfig](../comp/struct.ItemConfig.html) for a tool
|
||||
/// (weapon). This information is required for the `active` and `second`
|
||||
/// weapon items in a loadout. If some customisation to the item's
|
||||
|
@ -17,6 +17,7 @@ sum_type! {
|
||||
LightEmitter(comp::LightEmitter),
|
||||
Item(comp::Item),
|
||||
Scale(comp::Scale),
|
||||
Alignment(comp::Alignment),
|
||||
MountState(comp::MountState),
|
||||
Mounting(comp::Mounting),
|
||||
Mass(comp::Mass),
|
||||
@ -43,6 +44,7 @@ sum_type! {
|
||||
LightEmitter(PhantomData<comp::LightEmitter>),
|
||||
Item(PhantomData<comp::Item>),
|
||||
Scale(PhantomData<comp::Scale>),
|
||||
Alignment(PhantomData<comp::Alignment>),
|
||||
MountState(PhantomData<comp::MountState>),
|
||||
Mounting(PhantomData<comp::Mounting>),
|
||||
Mass(PhantomData<comp::Mass>),
|
||||
@ -69,6 +71,7 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPacket::LightEmitter(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Item(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Scale(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Alignment(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::MountState(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Mounting(comp) => sync::handle_insert(comp, entity, world),
|
||||
EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world),
|
||||
@ -93,6 +96,7 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
EcsCompPacket::LightEmitter(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Item(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Scale(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Alignment(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::MountState(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Mounting(comp) => sync::handle_modify(comp, entity, world),
|
||||
EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world),
|
||||
@ -119,6 +123,7 @@ impl sync::CompPacket for EcsCompPacket {
|
||||
},
|
||||
EcsCompPhantom::Item(_) => sync::handle_remove::<comp::Item>(entity, world),
|
||||
EcsCompPhantom::Scale(_) => sync::handle_remove::<comp::Scale>(entity, world),
|
||||
EcsCompPhantom::Alignment(_) => sync::handle_remove::<comp::Alignment>(entity, world),
|
||||
EcsCompPhantom::MountState(_) => sync::handle_remove::<comp::MountState>(entity, world),
|
||||
EcsCompPhantom::Mounting(_) => sync::handle_remove::<comp::Mounting>(entity, world),
|
||||
EcsCompPhantom::Mass(_) => sync::handle_remove::<comp::Mass>(entity, world),
|
||||
|
@ -123,6 +123,7 @@ impl State {
|
||||
ecs.register::<comp::Gravity>();
|
||||
ecs.register::<comp::CharacterState>();
|
||||
ecs.register::<comp::Object>();
|
||||
ecs.register::<comp::Alignment>();
|
||||
|
||||
// Register components send from clients -> server
|
||||
ecs.register::<comp::Controller>();
|
||||
@ -146,7 +147,6 @@ impl State {
|
||||
ecs.register::<comp::Last<comp::Vel>>();
|
||||
ecs.register::<comp::Last<comp::Ori>>();
|
||||
ecs.register::<comp::Agent>();
|
||||
ecs.register::<comp::Alignment>();
|
||||
ecs.register::<comp::WaypointArea>();
|
||||
ecs.register::<comp::ForceUpdate>();
|
||||
ecs.register::<comp::InventoryUpdate>();
|
||||
|
@ -31,8 +31,12 @@ impl CharacterBehavior for Data {
|
||||
handle_move(data, &mut update, 0.3);
|
||||
handle_jump(data, &mut update);
|
||||
|
||||
if self.prepare_timer < self.prepare_duration
|
||||
|| self.holdable && !self.exhausted && data.inputs.holding_ability_key()
|
||||
if !self.exhausted
|
||||
&& if self.holdable {
|
||||
data.inputs.holding_ability_key() || self.prepare_timer < self.prepare_duration
|
||||
} else {
|
||||
self.prepare_timer < self.prepare_duration
|
||||
}
|
||||
{
|
||||
// Prepare (draw the bow)
|
||||
update.character = CharacterState::BasicRanged(Data {
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
use vek::vec::Vec2;
|
||||
|
||||
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
|
||||
const BASE_HUMANOID_AIR_ACCEL: f32 = 15.0;
|
||||
const BASE_HUMANOID_AIR_ACCEL: f32 = 8.0;
|
||||
const BASE_HUMANOID_WATER_ACCEL: f32 = 150.0;
|
||||
const BASE_HUMANOID_WATER_SPEED: f32 = 180.0;
|
||||
// const BASE_HUMANOID_CLIMB_ACCEL: f32 = 10.0;
|
||||
@ -45,6 +45,24 @@ impl Body {
|
||||
Body::QuadrupedLow(_) => 120.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base_ori_rate(&self) -> f32 {
|
||||
match self {
|
||||
Body::Humanoid(_) => 20.0,
|
||||
Body::QuadrupedSmall(_) => 15.0,
|
||||
Body::QuadrupedMedium(_) => 10.0,
|
||||
Body::BirdMedium(_) => 30.0,
|
||||
Body::FishMedium(_) => 5.0,
|
||||
Body::Dragon(_) => 5.0,
|
||||
Body::BirdSmall(_) => 35.0,
|
||||
Body::FishSmall(_) => 10.0,
|
||||
Body::BipedLarge(_) => 12.0,
|
||||
Body::Object(_) => 5.0,
|
||||
Body::Golem(_) => 8.0,
|
||||
Body::Critter(_) => 35.0,
|
||||
Body::QuadrupedLow(_) => 12.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles updating `Components` to move player based on state of `JoinData`
|
||||
@ -68,10 +86,10 @@ fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||
update.vel.0 =
|
||||
update.vel.0 + Vec2::broadcast(data.dt.0) * data.inputs.move_dir * accel * efficiency;
|
||||
|
||||
handle_orientation(data, update, 20.0);
|
||||
handle_orientation(data, update, data.body.base_ori_rate());
|
||||
}
|
||||
|
||||
pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, strength: f32) {
|
||||
pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, rate: f32) {
|
||||
// Set direction based on move direction
|
||||
let ori_dir = if update.character.is_attack() || update.character.is_block() {
|
||||
data.inputs.look_dir.xy()
|
||||
@ -82,7 +100,7 @@ pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, strength: f
|
||||
};
|
||||
|
||||
// Smooth orientation
|
||||
update.ori.0 = Dir::slerp_to_vec3(update.ori.0, ori_dir.into(), strength * data.dt.0);
|
||||
update.ori.0 = Dir::slerp_to_vec3(update.ori.0, ori_dir.into(), rate * data.dt.0);
|
||||
}
|
||||
|
||||
/// Updates components to move player as if theyre swimming
|
||||
|
@ -118,7 +118,7 @@ impl<'a> System<'a> for Sys {
|
||||
const LISTEN_DIST: f32 = 16.0;
|
||||
const SEARCH_DIST: f32 = 48.0;
|
||||
const SIGHT_DIST: f32 = 128.0;
|
||||
const MIN_ATTACK_DIST: f32 = 3.25;
|
||||
const MIN_ATTACK_DIST: f32 = 3.5;
|
||||
|
||||
let scale = scales.get(entity).map(|s| s.0).unwrap_or(1.0);
|
||||
|
||||
@ -191,9 +191,9 @@ impl<'a> System<'a> for Sys {
|
||||
if let (Some(tgt_pos), _tgt_stats) =
|
||||
(positions.get(*target), stats.get(*target))
|
||||
{
|
||||
let dist_sqrd = pos.0.distance_squared(tgt_pos.0);
|
||||
let dist = pos.0.distance(tgt_pos.0);
|
||||
// Follow, or return to idle
|
||||
if dist_sqrd > AVG_FOLLOW_DIST.powf(2.0) {
|
||||
if dist > AVG_FOLLOW_DIST {
|
||||
if let Some((bearing, speed)) = chaser.chase(
|
||||
&*terrain,
|
||||
pos.0,
|
||||
@ -204,7 +204,7 @@ impl<'a> System<'a> for Sys {
|
||||
) {
|
||||
inputs.move_dir =
|
||||
bearing.xy().try_normalized().unwrap_or(Vec2::zero())
|
||||
* speed;
|
||||
* speed.min(0.2 + (dist - AVG_FOLLOW_DIST) / 8.0);
|
||||
inputs.jump.set_state(bearing.z > 1.5);
|
||||
}
|
||||
} else {
|
||||
@ -242,10 +242,12 @@ impl<'a> System<'a> for Sys {
|
||||
if let (Some(tgt_pos), Some(tgt_stats), tgt_alignment) = (
|
||||
positions.get(*target),
|
||||
stats.get(*target),
|
||||
alignments
|
||||
.get(*target)
|
||||
.copied()
|
||||
.unwrap_or(Alignment::Owned(*target)),
|
||||
alignments.get(*target).copied().unwrap_or(
|
||||
uids.get(*target)
|
||||
.copied()
|
||||
.map(Alignment::Owned)
|
||||
.unwrap_or(Alignment::Wild),
|
||||
),
|
||||
) {
|
||||
if let Some(dir) = Dir::from_unnormalized(tgt_pos.0 - pos.0) {
|
||||
inputs.look_dir = dir;
|
||||
@ -264,10 +266,10 @@ impl<'a> System<'a> for Sys {
|
||||
let dist_sqrd = pos.0.distance_squared(tgt_pos.0);
|
||||
if dist_sqrd < (MIN_ATTACK_DIST * scale).powf(2.0) {
|
||||
// Close-range attack
|
||||
/*inputs.move_dir = Vec2::from(tgt_pos.0 - pos.0)
|
||||
.try_normalized()
|
||||
.unwrap_or(Vec2::unit_y())
|
||||
* 0.7;*/
|
||||
inputs.move_dir = Vec2::from(tgt_pos.0 - pos.0)
|
||||
.try_normalized()
|
||||
.unwrap_or(Vec2::unit_y())
|
||||
* 0.1;
|
||||
|
||||
match tactic {
|
||||
Tactic::Melee | Tactic::Staff => inputs.primary.set_state(true),
|
||||
@ -419,7 +421,10 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Follow owner if we're too far, or if they're under attack
|
||||
if let Some(Alignment::Owned(owner)) = alignment.copied() {
|
||||
if let Some(owner_pos) = positions.get(owner) {
|
||||
(|| {
|
||||
let owner = uid_allocator.retrieve_entity_internal(owner.id())?;
|
||||
|
||||
let owner_pos = positions.get(owner)?;
|
||||
let dist_sqrd = pos.0.distance_squared(owner_pos.0);
|
||||
if dist_sqrd > MAX_FOLLOW_DIST.powf(2.0) && !agent.activity.is_follow() {
|
||||
agent.activity = Activity::Follow {
|
||||
@ -429,28 +434,27 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Attack owner's attacker
|
||||
if let Some(owner_stats) = stats.get(owner) {
|
||||
if owner_stats.health.last_change.0 < 5.0 {
|
||||
if let comp::HealthSource::Attack { by } =
|
||||
owner_stats.health.last_change.1.cause
|
||||
{
|
||||
if !agent.activity.is_attack() {
|
||||
if let Some(attacker) =
|
||||
uid_allocator.retrieve_entity_internal(by.id())
|
||||
{
|
||||
agent.activity = Activity::Attack {
|
||||
target: attacker,
|
||||
chaser: Chaser::default(),
|
||||
time: time.0,
|
||||
been_close: false,
|
||||
powerup: 0.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
let owner_stats = stats.get(owner)?;
|
||||
if owner_stats.health.last_change.0 < 5.0 {
|
||||
if let comp::HealthSource::Attack { by } =
|
||||
owner_stats.health.last_change.1.cause
|
||||
{
|
||||
if !agent.activity.is_attack() {
|
||||
let attacker = uid_allocator.retrieve_entity_internal(by.id())?;
|
||||
|
||||
agent.activity = Activity::Attack {
|
||||
target: attacker,
|
||||
chaser: Chaser::default(),
|
||||
time: time.0,
|
||||
been_close: false,
|
||||
powerup: 0.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(())
|
||||
})();
|
||||
}
|
||||
|
||||
debug_assert!(inputs.move_dir.map(|e| !e.is_nan()).reduce_and());
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
Agent, Attacking, Body, CharacterState, HealthChange, HealthSource, Ori, Pos, Scale, Stats,
|
||||
Alignment, Attacking, Body, CharacterState, HealthChange, HealthSource, Ori, Pos, Scale,
|
||||
Stats,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
sync::Uid,
|
||||
@ -25,7 +26,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Ori>,
|
||||
ReadStorage<'a, Scale>,
|
||||
ReadStorage<'a, Agent>,
|
||||
ReadStorage<'a, Alignment>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, Stats>,
|
||||
WriteStorage<'a, Attacking>,
|
||||
@ -42,7 +43,7 @@ impl<'a> System<'a> for Sys {
|
||||
positions,
|
||||
orientations,
|
||||
scales,
|
||||
agents,
|
||||
alignments,
|
||||
bodies,
|
||||
stats,
|
||||
mut attacking_storage,
|
||||
@ -74,7 +75,7 @@ impl<'a> System<'a> for Sys {
|
||||
pos_b,
|
||||
ori_b,
|
||||
scale_b_maybe,
|
||||
agent_b_maybe,
|
||||
alignment_b_maybe,
|
||||
character_b,
|
||||
stats_b,
|
||||
body_b,
|
||||
@ -84,7 +85,7 @@ impl<'a> System<'a> for Sys {
|
||||
&positions,
|
||||
&orientations,
|
||||
scales.maybe(),
|
||||
agents.maybe(),
|
||||
alignments.maybe(),
|
||||
character_states.maybe(),
|
||||
&stats,
|
||||
&bodies,
|
||||
@ -110,6 +111,7 @@ impl<'a> System<'a> for Sys {
|
||||
{
|
||||
// Weapon gives base damage
|
||||
let mut healthchange = attack.base_healthchange as f32;
|
||||
let mut knockback = attack.knockback;
|
||||
|
||||
// TODO: remove this, either it will remain unused or be used as a temporary
|
||||
// gameplay balance
|
||||
@ -118,10 +120,18 @@ impl<'a> System<'a> for Sys {
|
||||
// healthchange = (healthchange / 1.5).min(-1.0);
|
||||
//}
|
||||
|
||||
// TODO: remove this when there is a better way to target healing
|
||||
// Don't heal npc's hp
|
||||
if agent_b_maybe.is_some() && healthchange > 0.0 {
|
||||
// TODO: remove this when there is a better way to deal with alignment
|
||||
// Don't heal NPCs
|
||||
if (healthchange > 0.0 && alignment_b_maybe
|
||||
.map(|a| !a.is_friendly_to_players())
|
||||
.unwrap_or(true))
|
||||
// Don't hurt pets
|
||||
|| (healthchange < 0.0 && alignment_b_maybe
|
||||
.map(|b| Alignment::Owned(*uid).passive_towards(*b))
|
||||
.unwrap_or(false))
|
||||
{
|
||||
healthchange = 0.0;
|
||||
knockback = 0.0;
|
||||
}
|
||||
|
||||
if rand::random() {
|
||||
@ -135,14 +145,16 @@ impl<'a> System<'a> for Sys {
|
||||
healthchange *= 1.0 - BLOCK_EFFICIENCY
|
||||
}
|
||||
|
||||
server_emitter.emit(ServerEvent::Damage {
|
||||
uid: *uid_b,
|
||||
change: HealthChange {
|
||||
amount: healthchange as i32,
|
||||
cause: HealthSource::Attack { by: *uid },
|
||||
},
|
||||
});
|
||||
if attack.knockback != 0.0 {
|
||||
if healthchange != 0.0 {
|
||||
server_emitter.emit(ServerEvent::Damage {
|
||||
uid: *uid_b,
|
||||
change: HealthChange {
|
||||
amount: healthchange as i32,
|
||||
cause: HealthSource::Attack { by: *uid },
|
||||
},
|
||||
});
|
||||
}
|
||||
if knockback != 0.0 {
|
||||
local_emitter.emit(LocalEvent::ApplyForce {
|
||||
entity: b,
|
||||
force: attack.knockback
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
projectile, Energy, EnergySource, HealthSource, Ori, PhysicsState, Pos, Projectile, Vel,
|
||||
projectile, Alignment, Energy, EnergySource, HealthSource, Ori, PhysicsState, Pos,
|
||||
Projectile, Vel,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
state::DeltaTime,
|
||||
@ -27,6 +28,7 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Ori>,
|
||||
WriteStorage<'a, Projectile>,
|
||||
WriteStorage<'a, Energy>,
|
||||
ReadStorage<'a, Alignment>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
@ -43,6 +45,7 @@ impl<'a> System<'a> for Sys {
|
||||
mut orientations,
|
||||
mut projectiles,
|
||||
mut energies,
|
||||
alignments,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut local_emitter = local_bus.emitter();
|
||||
@ -82,7 +85,17 @@ impl<'a> System<'a> for Sys {
|
||||
for effect in projectile.hit_entity.drain(..) {
|
||||
match effect {
|
||||
projectile::Effect::Damage(change) => {
|
||||
if other != projectile.owner.unwrap() {
|
||||
let owner_uid = projectile.owner.unwrap();
|
||||
// Hacky: remove this when groups get implemented
|
||||
let passive = uid_allocator
|
||||
.retrieve_entity_internal(other.into())
|
||||
.and_then(|other| {
|
||||
alignments
|
||||
.get(other)
|
||||
.map(|a| Alignment::Owned(owner_uid).passive_towards(*a))
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if other != projectile.owner.unwrap() && !passive {
|
||||
server_emitter.emit(ServerEvent::Damage { uid: other, change });
|
||||
}
|
||||
},
|
||||
|
@ -16,6 +16,7 @@ use common::{
|
||||
terrain::TerrainChunkSize,
|
||||
util::Dir,
|
||||
vol::RectVolSize,
|
||||
LoadoutBuilder,
|
||||
};
|
||||
use rand::Rng;
|
||||
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
||||
@ -507,7 +508,11 @@ fn handle_spawn(
|
||||
String
|
||||
) {
|
||||
(Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount, opt_ai) => {
|
||||
if let Some(alignment) = parse_alignment(target, &opt_align) {
|
||||
let uid = server
|
||||
.state
|
||||
.read_component_cloned(target)
|
||||
.expect("Expected player to have a UID");
|
||||
if let Some(alignment) = parse_alignment(uid, &opt_align) {
|
||||
let amount = opt_amount
|
||||
.and_then(|a| a.parse().ok())
|
||||
.filter(|x| *x > 0)
|
||||
@ -539,7 +544,7 @@ fn handle_spawn(
|
||||
.create_npc(
|
||||
pos,
|
||||
comp::Stats::new(get_npc_name(id).into(), body),
|
||||
comp::Loadout::default(),
|
||||
LoadoutBuilder::animal().build(),
|
||||
body,
|
||||
)
|
||||
.with(comp::Vel(vel))
|
||||
@ -715,7 +720,7 @@ fn handle_help(
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_alignment(owner: EcsEntity, alignment: &str) -> Option<comp::Alignment> {
|
||||
fn parse_alignment(owner: Uid, alignment: &str) -> Option<comp::Alignment> {
|
||||
match alignment {
|
||||
"wild" => Some(comp::Alignment::Wild),
|
||||
"enemy" => Some(comp::Alignment::Enemy),
|
||||
|
@ -188,13 +188,16 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
let reinsert = if let Some(pos) =
|
||||
state.read_storage::<comp::Pos>().get(entity)
|
||||
{
|
||||
let uid = state
|
||||
.read_component_cloned(entity)
|
||||
.expect("Expected player to have a UID");
|
||||
if (
|
||||
&state.read_storage::<comp::Alignment>(),
|
||||
&state.read_storage::<comp::Agent>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(alignment, _)| {
|
||||
alignment == &&comp::Alignment::Owned(entity)
|
||||
alignment == &&comp::Alignment::Owned(uid)
|
||||
})
|
||||
.count()
|
||||
>= 3
|
||||
@ -220,10 +223,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
.map(|(entity, _, _)| entity);
|
||||
nearest_tameable
|
||||
} {
|
||||
let _ = state.ecs().write_storage().insert(
|
||||
tameable_entity,
|
||||
comp::Alignment::Owned(entity),
|
||||
);
|
||||
let _ = state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(tameable_entity, comp::Alignment::Owned(uid));
|
||||
let _ = state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
|
@ -171,7 +171,10 @@ impl StateExt for State {
|
||||
});
|
||||
self.write_component(entity, comp::Gravity(1.0));
|
||||
self.write_component(entity, comp::CharacterState::default());
|
||||
self.write_component(entity, comp::Alignment::Owned(entity));
|
||||
self.write_component(
|
||||
entity,
|
||||
comp::Alignment::Owned(self.read_component_cloned(entity).unwrap()),
|
||||
);
|
||||
|
||||
// Set the character id for the player
|
||||
// TODO this results in a warning in the console: "Error modifying synced
|
||||
|
@ -1,8 +1,8 @@
|
||||
use super::SysTimer;
|
||||
use common::{
|
||||
comp::{
|
||||
Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter, Loadout,
|
||||
Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel,
|
||||
Alignment, Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter,
|
||||
Loadout, Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel,
|
||||
},
|
||||
msg::EcsCompPacket,
|
||||
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt},
|
||||
@ -48,6 +48,7 @@ pub struct TrackedComps<'a> {
|
||||
pub scale: ReadStorage<'a, Scale>,
|
||||
pub mounting: ReadStorage<'a, Mounting>,
|
||||
pub mount_state: ReadStorage<'a, MountState>,
|
||||
pub alignment: ReadStorage<'a, Alignment>,
|
||||
pub mass: ReadStorage<'a, Mass>,
|
||||
pub collider: ReadStorage<'a, Collider>,
|
||||
pub sticky: ReadStorage<'a, Sticky>,
|
||||
@ -104,6 +105,10 @@ impl<'a> TrackedComps<'a> {
|
||||
.get(entity)
|
||||
.cloned()
|
||||
.map(|c| comps.push(c.into()));
|
||||
self.alignment
|
||||
.get(entity)
|
||||
.cloned()
|
||||
.map(|c| comps.push(c.into()));
|
||||
self.mass.get(entity).copied().map(|c| comps.push(c.into()));
|
||||
self.collider
|
||||
.get(entity)
|
||||
@ -146,6 +151,7 @@ pub struct ReadTrackers<'a> {
|
||||
pub scale: ReadExpect<'a, UpdateTracker<Scale>>,
|
||||
pub mounting: ReadExpect<'a, UpdateTracker<Mounting>>,
|
||||
pub mount_state: ReadExpect<'a, UpdateTracker<MountState>>,
|
||||
pub alignment: ReadExpect<'a, UpdateTracker<Alignment>>,
|
||||
pub mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
||||
pub collider: ReadExpect<'a, UpdateTracker<Collider>>,
|
||||
pub sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
|
||||
@ -178,6 +184,7 @@ impl<'a> ReadTrackers<'a> {
|
||||
.with_component(&comps.uid, &*self.scale, &comps.scale, filter)
|
||||
.with_component(&comps.uid, &*self.mounting, &comps.mounting, filter)
|
||||
.with_component(&comps.uid, &*self.mount_state, &comps.mount_state, filter)
|
||||
.with_component(&comps.uid, &*self.alignment, &comps.alignment, filter)
|
||||
.with_component(&comps.uid, &*self.mass, &comps.mass, filter)
|
||||
.with_component(&comps.uid, &*self.collider, &comps.collider, filter)
|
||||
.with_component(&comps.uid, &*self.sticky, &comps.sticky, filter)
|
||||
@ -207,6 +214,7 @@ pub struct WriteTrackers<'a> {
|
||||
scale: WriteExpect<'a, UpdateTracker<Scale>>,
|
||||
mounting: WriteExpect<'a, UpdateTracker<Mounting>>,
|
||||
mount_state: WriteExpect<'a, UpdateTracker<MountState>>,
|
||||
alignment: WriteExpect<'a, UpdateTracker<Alignment>>,
|
||||
mass: WriteExpect<'a, UpdateTracker<Mass>>,
|
||||
collider: WriteExpect<'a, UpdateTracker<Collider>>,
|
||||
sticky: WriteExpect<'a, UpdateTracker<Sticky>>,
|
||||
@ -228,6 +236,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
||||
trackers.scale.record_changes(&comps.scale);
|
||||
trackers.mounting.record_changes(&comps.mounting);
|
||||
trackers.mount_state.record_changes(&comps.mount_state);
|
||||
trackers.alignment.record_changes(&comps.alignment);
|
||||
trackers.mass.record_changes(&comps.mass);
|
||||
trackers.collider.record_changes(&comps.collider);
|
||||
trackers.sticky.record_changes(&comps.sticky);
|
||||
@ -282,6 +291,7 @@ pub fn register_trackers(world: &mut World) {
|
||||
world.register_tracker::<Scale>();
|
||||
world.register_tracker::<Mounting>();
|
||||
world.register_tracker::<MountState>();
|
||||
world.register_tracker::<Alignment>();
|
||||
world.register_tracker::<Mass>();
|
||||
world.register_tracker::<Collider>();
|
||||
world.register_tracker::<Sticky>();
|
||||
|
@ -9,6 +9,7 @@ use common::{
|
||||
npc::NPC_NAMES,
|
||||
state::TerrainChanges,
|
||||
terrain::TerrainGrid,
|
||||
LoadoutBuilder,
|
||||
};
|
||||
use rand::Rng;
|
||||
use specs::{Join, Read, ReadStorage, System, Write, WriteExpect, WriteStorage};
|
||||
@ -136,9 +137,9 @@ impl<'a> System<'a> for Sys {
|
||||
energy_cost: 0,
|
||||
buildup_duration: Duration::from_millis(0),
|
||||
recover_duration: Duration::from_millis(400),
|
||||
base_healthchange: -2,
|
||||
range: 3.5,
|
||||
max_angle: 60.0,
|
||||
base_healthchange: -6,
|
||||
range: 5.0,
|
||||
max_angle: 80.0,
|
||||
}),
|
||||
ability2: None,
|
||||
ability3: None,
|
||||
@ -214,36 +215,7 @@ impl<'a> System<'a> for Sys {
|
||||
head: None,
|
||||
tabard: None,
|
||||
},
|
||||
_ => comp::Loadout {
|
||||
active_item: Some(comp::ItemConfig {
|
||||
item: assets::load_expect_cloned("common.items.weapons.empty"),
|
||||
ability1: Some(CharacterAbility::BasicMelee {
|
||||
energy_cost: 10,
|
||||
buildup_duration: Duration::from_millis(800),
|
||||
recover_duration: Duration::from_millis(200),
|
||||
base_healthchange: -2,
|
||||
range: 3.5,
|
||||
max_angle: 60.0,
|
||||
}),
|
||||
ability2: None,
|
||||
ability3: None,
|
||||
block_ability: None,
|
||||
dodge_ability: None,
|
||||
}),
|
||||
second_item: None,
|
||||
shoulder: None,
|
||||
chest: None,
|
||||
belt: None,
|
||||
hand: None,
|
||||
pants: None,
|
||||
foot: None,
|
||||
back: None,
|
||||
ring: None,
|
||||
neck: None,
|
||||
lantern: None,
|
||||
head: None,
|
||||
tabard: None,
|
||||
},
|
||||
_ => LoadoutBuilder::animal().build(),
|
||||
};
|
||||
|
||||
let mut scale = entity.scale;
|
||||
|
@ -57,12 +57,9 @@ impl SessionState {
|
||||
.set_fov_deg(global_state.settings.graphics.fov);
|
||||
let hud = Hud::new(global_state, &client.borrow());
|
||||
{
|
||||
let my_entity = client.borrow().entity();
|
||||
client
|
||||
.borrow_mut()
|
||||
.state_mut()
|
||||
.ecs_mut()
|
||||
.insert(MyEntity(my_entity));
|
||||
let mut client = client.borrow_mut();
|
||||
let my_entity = client.entity();
|
||||
client.state_mut().ecs_mut().insert(MyEntity(my_entity));
|
||||
}
|
||||
let voxygen_i18n = load_expect::<VoxygenLocalization>(&i18n_asset_key(
|
||||
&global_state.settings.language.selected_language,
|
||||
@ -249,13 +246,21 @@ impl PlayState for SessionState {
|
||||
{
|
||||
(
|
||||
Some((cam_pos + cam_dir * (cam_dist - 0.01)).map(|e| e.floor() as i32)),
|
||||
Some((cam_pos + cam_dir * cam_dist).map(|e| e.floor() as i32)),
|
||||
Some((cam_pos + cam_dir * (cam_dist + 0.01)).map(|e| e.floor() as i32)),
|
||||
)
|
||||
},
|
||||
_ => (None, None),
|
||||
}
|
||||
};
|
||||
|
||||
let can_build = self
|
||||
.client
|
||||
.borrow()
|
||||
.state()
|
||||
.read_storage::<comp::CanBuild>()
|
||||
.get(self.client.borrow().entity())
|
||||
.is_some();
|
||||
|
||||
// Only highlight collectables
|
||||
self.scene.set_select_pos(select_pos.filter(|sp| {
|
||||
self.client
|
||||
@ -263,7 +268,7 @@ impl PlayState for SessionState {
|
||||
.state()
|
||||
.terrain()
|
||||
.get(*sp)
|
||||
.map(|b| b.is_collectible())
|
||||
.map(|b| b.is_collectible() || can_build)
|
||||
.unwrap_or(false)
|
||||
}));
|
||||
|
||||
@ -279,16 +284,9 @@ impl PlayState for SessionState {
|
||||
return PlayStateResult::Shutdown;
|
||||
},
|
||||
Event::InputUpdate(GameInput::Primary, state) => {
|
||||
// Check the existence of CanBuild component. If it's here, use LMB to
|
||||
// break blocks, if not, use it to attack
|
||||
// If we can build, use LMB to break blocks, if not, use it to attack
|
||||
let mut client = self.client.borrow_mut();
|
||||
if state
|
||||
&& client
|
||||
.state()
|
||||
.read_storage::<comp::CanBuild>()
|
||||
.get(client.entity())
|
||||
.is_some()
|
||||
{
|
||||
if state && can_build {
|
||||
if let Some(select_pos) = select_pos {
|
||||
client.remove_block(select_pos);
|
||||
}
|
||||
@ -302,13 +300,7 @@ impl PlayState for SessionState {
|
||||
|
||||
let mut client = self.client.borrow_mut();
|
||||
|
||||
if state
|
||||
&& client
|
||||
.state()
|
||||
.read_storage::<comp::CanBuild>()
|
||||
.get(client.entity())
|
||||
.is_some()
|
||||
{
|
||||
if state && can_build {
|
||||
if let Some(build_pos) = build_pos {
|
||||
client.place_block(build_pos, self.selected_block);
|
||||
}
|
||||
@ -319,12 +311,7 @@ impl PlayState for SessionState {
|
||||
|
||||
Event::InputUpdate(GameInput::Roll, state) => {
|
||||
let client = self.client.borrow();
|
||||
if client
|
||||
.state()
|
||||
.read_storage::<comp::CanBuild>()
|
||||
.get(client.entity())
|
||||
.is_some()
|
||||
{
|
||||
if can_build {
|
||||
if state {
|
||||
if let Some(block) = select_pos
|
||||
.and_then(|sp| client.state().terrain().get(sp).ok().copied())
|
||||
|
Loading…
Reference in New Issue
Block a user