Tweaked particles. Added skill icons.

This commit is contained in:
Sam 2020-08-29 15:44:46 -05:00
parent 8b9202710f
commit 6b23af6e0b
15 changed files with 204 additions and 76 deletions

BIN
assets/voxygen/element/icons/heal_bomb.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -23,6 +23,7 @@ in vec3 inst_pos;
in float inst_time;
in float inst_lifespan;
in float inst_entropy;
in float inst_misc;
in int inst_mode;
out vec3 f_pos;
@ -49,6 +50,7 @@ const int FIREFLY = 10;
const int BEE = 11;
const int GROUND_SHOCKWAVE = 12;
const int HEALING_BEAM = 13;
const int ENERGY_NATURE = 14;
// meters per second squared (acceleration)
const float earth_gravity = 9.807;
@ -104,6 +106,28 @@ mat4 identity() {
);
}
vec3 beam_pos2() {
return vec3(inst_lifespan, inst_entropy, inst_misc);
}
vec3 perp_axis1(vec3 axis) {
return normalize(vec3(axis.y + axis.z, -axis.x + axis.z, -axis.x - axis.y));
}
vec3 perp_axis2(vec3 axis1, vec3 axis2) {
return normalize(vec3(axis1.y * axis2.z - axis1.z * axis2.y, axis1.z * axis2.x - axis1.x * axis2.z, axis1.x * axis2.y - axis1.y * axis2.x));
}
vec3 spiral_motion(vec3 line, float radius, float time_function) {
vec3 axis2 = perp_axis1(line);
vec3 axis3 = perp_axis2(line, axis2);
return line * time_function + vec3(
radius * cos(10 * time_function) * axis2.x + radius * sin(10 * time_function) * axis3.x,
radius * cos(10 * time_function) * axis2.y + radius * sin(10 * time_function) * axis3.y,
radius * cos(10 * time_function) * axis2.z + radius * sin(10 * time_function) * axis3.z + 1.0);
}
void main() {
float rand0 = hash(vec4(inst_entropy + 0));
float rand1 = hash(vec4(inst_entropy + 1));
@ -251,10 +275,20 @@ void main() {
);
} else if (inst_mode == HEALING_BEAM) {
attr = Attr(
vec3(rand0 * lifetime * 15, rand1 * lifetime * 15, rand2 * lifetime * 15 + 1),
1 + rand3,
spiral_motion(beam_pos2() - inst_pos, 0.3 * (floor(2 * hash(vec4(inst_time)) + 0.5) - 0.5), lifetime / 2),
(1.7 - 0.7 * abs(floor(2 * hash(vec4(inst_time)) - 0.5) + 0.5)) * (1.5 + 0.5 * sin(tick.x * 10 - lifetime * 4)),
vec4(vec3(0, 0.7 + 0.3 * sin(tick.x * 8 - lifetime * 3), 0), 1),
spin_in_axis(vec3(inst_entropy, inst_misc, inst_lifespan), tick.z)
);
} else if (inst_mode == ENERGY_NATURE) {
attr = Attr(
linear_motion(
vec3(rand0 * 1, rand1 * 1, rand2 * 1),
vec3(rand3 * 2, rand4 * 2, rand5 * 2)
),
0.8,
vec4(vec3(0, 1, 0), 1),
spin_in_axis(vec3(1, 0, 0), 0)
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3)
);
} else {
attr = Attr(

View File

@ -67,6 +67,7 @@ make_case_elim!(
FireworkRed = 57,
FireworkYellow = 58,
MultiArrow = 59,
BoltNature = 60,
}
);
@ -77,7 +78,7 @@ impl Body {
}
}
pub const ALL_OBJECTS: [Body; 60] = [
pub const ALL_OBJECTS: [Body; 61] = [
Body::Arrow,
Body::Bomb,
Body::Scarecrow,
@ -138,6 +139,7 @@ pub const ALL_OBJECTS: [Body; 60] = [
Body::FireworkRed,
Body::FireworkYellow,
Body::MultiArrow,
Body::BoltNature,
];
impl From<Body> for super::Body {
@ -207,6 +209,7 @@ impl Body {
Body::FireworkRed => "firework_red",
Body::FireworkYellow => "firework_yellow",
Body::MultiArrow => "multi_arrow",
Body::BoltNature => "bolt_nature",
}
}
}

View File

@ -314,7 +314,7 @@ impl Tool {
energy_regen: 120,
},
BasicRanged {
energy_cost: 400,
energy_cost: 0,
holdable: true,
prepare_duration: Duration::from_millis(800),
recover_duration: Duration::from_millis(50),
@ -337,12 +337,12 @@ impl Tool {
owner: None,
ignore_group: true,
},
projectile_body: Body::Object(object::Body::BoltFireBig),
projectile_body: Body::Object(object::Body::BoltNature),
projectile_light: Some(LightEmitter {
col: (0.0, 1.0, 0.0).into(),
..Default::default()
}),
projectile_gravity: None,
projectile_gravity: Some(Gravity(1.0)),
projectile_speed: 25.0,
},
]

View File

@ -14,6 +14,7 @@ pub enum Outcome {
pos: Vec3<f32>,
power: f32,
reagent: Option<Reagent>, // How can we better define this?
percent_damage: f32,
},
ProjectileShot {
pos: Vec3<f32>,

View File

@ -94,7 +94,8 @@ impl CharacterBehavior for Data {
lifesteal_eff: self.lifesteal_eff,
energy_regen: self.energy_regen,
});
} else if data.inputs.primary.is_pressed() && self.cooldown_duration != Duration::default() {
} else if data.inputs.primary.is_pressed() && self.cooldown_duration != Duration::default()
{
// Cooldown until next tick of damage
update.character = CharacterState::BasicBeam(Data {
exhausted: self.exhausted,
@ -158,8 +159,6 @@ impl CharacterBehavior for Data {
data.updater.remove::<Attacking>(data.entity);
}
update
}
}

View File

@ -133,16 +133,17 @@ impl<'a> System<'a> for Sys {
energy_mut.change_by(energy as i32, EnergySource::HitEnemy);
}
},
projectile::Effect::Explode { power, percent_damage } => {
server_emitter.emit(ServerEvent::Explosion {
pos: pos.0,
power,
owner: projectile.owner,
friendly_damage: false,
reagent: None,
percent_damage,
})
},
projectile::Effect::Explode {
power,
percent_damage,
} => server_emitter.emit(ServerEvent::Explosion {
pos: pos.0,
power,
owner: projectile.owner,
friendly_damage: false,
reagent: None,
percent_damage,
}),
projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy {
entity,
cause: HealthSource::World,
@ -163,16 +164,17 @@ impl<'a> System<'a> for Sys {
if physics.on_wall.is_some() || physics.on_ground || physics.on_ceiling {
for effect in projectile.hit_solid.drain(..) {
match effect {
projectile::Effect::Explode { power, percent_damage } => {
server_emitter.emit(ServerEvent::Explosion {
pos: pos.0,
power,
owner: projectile.owner,
friendly_damage: false,
reagent: None,
percent_damage,
})
},
projectile::Effect::Explode {
power,
percent_damage,
} => server_emitter.emit(ServerEvent::Explosion {
pos: pos.0,
power,
owner: projectile.owner,
friendly_damage: false,
reagent: None,
percent_damage,
}),
projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy {
entity,
cause: HealthSource::World,

View File

@ -462,6 +462,7 @@ pub fn handle_explosion(
pos,
power,
reagent,
percent_damage,
});
let owner_entity = owner.and_then(|uid| {
ecs.read_resource::<UidAllocator>()
@ -486,9 +487,14 @@ pub fn handle_explosion(
&& distance_squared < hit_range.powi(2)
{
// See if entities are in the same group
let same_group = owner_entity
.and_then(|e| groups.get(e))
.map_or(false, |group_a| Some(group_a) == groups.get(entity_b));
let mut same_group = owner_entity
.and_then(|e| groups.get(e))
.map_or(false, |group_a| Some(group_a) == groups.get(entity_b));
if let Some(entity) = owner_entity {
if entity == entity_b {
same_group = true;
}
}
// Don't heal if outside group
// Don't damage in the same group
let (mut is_heal, mut is_damage) = (false, false);

View File

@ -61,7 +61,15 @@ impl Server {
friendly_damage,
reagent,
percent_damage,
} => handle_explosion(&self, pos, power, owner, friendly_damage, reagent, percent_damage),
} => handle_explosion(
&self,
pos,
power,
owner,
friendly_damage,
reagent,
percent_damage,
),
ServerEvent::Shoot {
entity,
dir,

View File

@ -268,6 +268,7 @@ image_ids! {
snake_arrow_0: "voxygen.element.icons.snake",
heal_0: "voxygen.element.icons.heal_0",
sword_whirlwind: "voxygen.element.icons.sword_whirlwind",
heal_bomb: "voxygen.element.icons.heal_bomb",
// Buttons
button: "voxygen.element.buttons.button",

View File

@ -620,7 +620,11 @@ impl<'a> Widget for Skillbar<'a> {
ToolKind::Hammer(_) => self.imgs.twohhammer_m1,
ToolKind::Axe(_) => self.imgs.twohaxe_m1,
ToolKind::Bow(_) => self.imgs.bow_m1,
ToolKind::Staff(_) => self.imgs.staff_m1,
ToolKind::Staff(kind) => match kind.as_ref() {
"Sceptre" => self.imgs.heal_0,
"SceptreVelorite" => self.imgs.heal_0,
_ => self.imgs.staff_m1,
},
ToolKind::Debug(kind) => match kind.as_ref() {
"Boost" => self.imgs.flyingrod_m1,
_ => self.imgs.nothing,
@ -699,8 +703,8 @@ impl<'a> Widget for Skillbar<'a> {
Some(ToolKind::Axe(_)) => self.imgs.axespin,
Some(ToolKind::Bow(_)) => self.imgs.bow_m2,
Some(ToolKind::Staff(kind)) => match kind.as_ref() {
"Sceptre" => self.imgs.heal_0,
"SceptreVelorite" => self.imgs.heal_0,
"Sceptre" => self.imgs.heal_bomb,
"SceptreVelorite" => self.imgs.heal_bomb,
_ => self.imgs.staff_m2,
},
Some(ToolKind::Debug(kind)) => match kind.as_ref() {

View File

@ -35,6 +35,9 @@ gfx_defines! {
// can save 32 bits per instance, and have cleaner tailor made code.
inst_mode: i32 = "inst_mode",
// an extra value for particles that need it
inst_misc: f32 = "inst_misc",
// a triangle is: f32 x 3 x 3 x 1 = 288 bits
// a quad is: f32 x 3 x 3 x 2 = 576 bits
// a cube is: f32 x 3 x 3 x 12 = 3456 bits
@ -107,6 +110,7 @@ pub enum ParticleMode {
Bee = 11,
GroundShockwave = 12,
HealingBeam = 13,
EnergyNature = 14,
}
impl ParticleMode {
@ -127,6 +131,23 @@ impl Instance {
inst_entropy: rand::thread_rng().gen(),
inst_mode: inst_mode as i32,
inst_pos: inst_pos.into_array(),
inst_misc: 0.0,
}
}
pub fn new_beam(
inst_time: f64,
inst_mode: ParticleMode,
inst_pos: Vec3<f32>,
inst_pos2: Vec3<f32>,
) -> Self {
Self {
inst_time: inst_time as f32,
inst_mode: inst_mode as i32,
inst_pos: inst_pos.into_array(),
inst_lifespan: inst_pos2.x,
inst_entropy: inst_pos2.y,
inst_misc: inst_pos2.z,
}
}
}

View File

@ -3588,6 +3588,7 @@ fn mesh_object(obj: &object::Body) -> BoneMeshes {
Body::BoltFireBig => ("weapon.projectile.fire-bolt-1", Vec3::new(-6.0, -6.0, -6.0)),
Body::TrainingDummy => ("object.training_dummy", Vec3::new(-7.0, -5.0, 0.0)),
Body::MultiArrow => ("weapon.projectile.multi-arrow", Vec3::new(-4.0, -9.5, -5.0)),
Body::BoltNature => ("weapon.projectile.nature-bolt", Vec3::new(-6.0, -6.0, -6.0)),
};
load_mesh(name, offset)
}

View File

@ -390,6 +390,7 @@ impl Scene {
pos,
power,
reagent,
..
} => self.event_lights.push(EventLight {
light: Light::new(
*pos,

View File

@ -58,37 +58,50 @@ impl ParticleMgr {
pos,
power,
reagent,
percent_damage,
} => {
self.particles.resize_with(
self.particles.len() + if reagent.is_some() { 300 } else { 150 },
|| {
if *percent_damage < 0.5 {
self.particles.resize_with(self.particles.len() + 200, || {
Particle::new(
Duration::from_millis(if reagent.is_some() { 1000 } else { 250 }),
Duration::from_secs(1),
time,
match reagent {
Some(Reagent::Blue) => ParticleMode::FireworkBlue,
Some(Reagent::Green) => ParticleMode::FireworkGreen,
Some(Reagent::Purple) => ParticleMode::FireworkPurple,
Some(Reagent::Red) => ParticleMode::FireworkRed,
Some(Reagent::Yellow) => ParticleMode::FireworkYellow,
None => ParticleMode::Shrapnel,
},
*pos,
ParticleMode::EnergyNature,
*pos + Vec3::<f32>::zero().map(|_| rng.gen_range(-3.0, 3.0) * power),
)
},
);
});
} else {
self.particles.resize_with(
self.particles.len() + if reagent.is_some() { 300 } else { 150 },
|| {
Particle::new(
Duration::from_millis(if reagent.is_some() { 1000 } else { 250 }),
time,
match reagent {
Some(Reagent::Blue) => ParticleMode::FireworkBlue,
Some(Reagent::Green) => ParticleMode::FireworkGreen,
Some(Reagent::Purple) => ParticleMode::FireworkPurple,
Some(Reagent::Red) => ParticleMode::FireworkRed,
Some(Reagent::Yellow) => ParticleMode::FireworkYellow,
None => ParticleMode::Shrapnel,
},
*pos,
)
},
);
self.particles.resize_with(
self.particles.len() + if reagent.is_some() { 100 } else { 200 },
|| {
Particle::new(
Duration::from_secs(4),
time,
ParticleMode::CampfireSmoke,
*pos + Vec2::<f32>::zero().map(|_| rng.gen_range(-1.0, 1.0) * power),
)
},
);
self.particles.resize_with(
self.particles.len() + if reagent.is_some() { 100 } else { 200 },
|| {
Particle::new(
Duration::from_secs(4),
time,
ParticleMode::CampfireSmoke,
*pos + Vec2::<f32>::zero()
.map(|_| rng.gen_range(-1.0, 1.0) * power),
)
},
);
}
},
Outcome::ProjectileShot { .. } => {},
}
@ -144,6 +157,9 @@ impl ParticleMgr {
Body::Object(object::Body::BoltFireBig) => {
self.maintain_boltfirebig_particles(scene_data, pos)
},
Body::Object(object::Body::BoltNature) => {
self.maintain_boltnature_particles(scene_data, pos)
},
Body::Object(
object::Body::Bomb
| object::Body::FireworkBlue
@ -241,6 +257,21 @@ impl ParticleMgr {
);
}
fn maintain_boltnature_particles(&mut self, scene_data: &SceneData, pos: &Pos) {
let time = scene_data.state.get_time();
// nature
self.particles.resize(
self.particles.len() + usize::from(self.scheduler.heartbeats(Duration::from_millis(3))),
Particle::new(
Duration::from_millis(250),
time,
ParticleMode::EnergyNature,
pos.0,
),
);
}
fn maintain_bomb_particles(&mut self, scene_data: &SceneData, pos: &Pos) {
span!(
_guard,
@ -314,19 +345,19 @@ impl ParticleMgr {
.join()
{
if let CharacterState::BasicBeam(b) = character_state {
if b.buildup_duration == Duration::default() {
let particle_ori = b.particle_ori.unwrap_or(*ori.vec());
for _ in 0..self.scheduler.heartbeats(Duration::from_millis(10)) {
for d in 0..(b.range as i32) {
self.particles.push(
Particle::new(
Duration::from_millis(50),
time,
ParticleMode::HealingBeam,
pos.0 + particle_ori * (d as f32),
),
);
}
let particle_ori = b.particle_ori.unwrap_or(*ori.vec());
for _ in 0..self.scheduler.heartbeats(Duration::from_millis(5)) {
let buildup = b.buildup_duration.as_millis() as i32;
for t in 0..((buildup / 50) + 1) {
let frac = ((t * 50) as f32) / 250.0; // Default value of buildup duration hardcoded for now, as it currently decreases over time
let dur = (2000.0 * (1.0 - frac)).max(0.0) as u64;
self.particles.push(Particle::new_beam(
Duration::from_millis(dur),
time,
ParticleMode::HealingBeam,
pos.0 + particle_ori * b.range * frac,
pos.0 + particle_ori * b.range,
));
}
}
}
@ -679,4 +710,17 @@ impl Particle {
instance: ParticleInstance::new(time, lifespan.as_secs_f32(), mode, pos),
}
}
fn new_beam(
lifespan: Duration,
time: f64,
mode: ParticleMode,
pos1: Vec3<f32>,
pos2: Vec3<f32>,
) -> Self {
Particle {
alive_until: time + lifespan.as_secs_f64(),
instance: ParticleInstance::new_beam(time, mode, pos1, pos2),
}
}
}