Skip to content

Commit

Permalink
Added goblin death animation + improved level design
Browse files Browse the repository at this point in the history
  • Loading branch information
Louis-Tarvin committed Sep 14, 2022
1 parent 0abafe6 commit c662f1c
Show file tree
Hide file tree
Showing 12 changed files with 1,569 additions and 442 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "elemental-sorcerer"
version = "0.1.0"
version = "0.2.0"
edition = "2021"

[features]
Expand All @@ -11,7 +11,6 @@ dev = [
[dependencies]
bevy = { version = "0.8", default-features = false, features = ["bevy_asset", "bevy_winit", "render", "png", "x11"] }
bevy_kira_audio = { version = "0.12", default-features = false, features = ["wav"] }
# bevy = { version = "0.8.0", features = ["dynamic"] }
bevy-inspector-egui = "0.12.1"
bevy_prototype_debug_lines = "0.8"
bevy_ecs_ldtk = { version = "0.4.0", features = ["atlas"] }
Expand Down
Binary file modified assets/audio/bgm.wav
100644 → 100755
Binary file not shown.
1,715 changes: 1,399 additions & 316 deletions assets/levels/level.ldtk

Large diffs are not rendered by default.

13 changes: 0 additions & 13 deletions build/style.css
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
body, html {
height: 100%;
}

body {
background: linear-gradient(
135deg,
white 0%,
white 49%,
black 49%,
black 51%,
white 51%,
white 100%
) repeat;
background-size: 20px 20px;
margin: 0;
}

Expand Down
194 changes: 114 additions & 80 deletions src/abilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::Display;
use bevy::{
prelude::{
Assets, Commands, Component, DespawnRecursiveExt, Entity, EventReader, GlobalTransform,
Mut, Query, Res, ResMut, Transform, Vec2, Vec3, With, Without,
Query, Res, ResMut, Transform, Vec2, Vec3, With, Without,
},
sprite::{Sprite, SpriteBundle, SpriteSheetBundle, TextureAtlas, TextureAtlasSprite},
time::{Time, Timer},
Expand All @@ -17,8 +17,15 @@ use heron::{
use crate::{
animation::Animated,
audio::AudioAssets,
damage::Hurtbox,
destruction::DestructionTimer,
entity::{block::Block, lava::Lava, player::Player, Flamable},
entity::{
block::Block,
goblin::{AnimationState, Patrol},
lava::Lava,
player::Player,
Flamable,
},
input::Controllable,
physics::{PhysicsLayers, PhysicsObjectBundle},
state::load_game::GameAssets,
Expand Down Expand Up @@ -57,11 +64,11 @@ impl Display for Element {
}

#[derive(Component)]
pub enum Projectile {
Fireball,
Wind,
Water,
}
pub struct FireProjectile;
#[derive(Component)]
pub struct WindProjectile;
#[derive(Component)]
pub struct WaterProjectile;

pub fn use_ability(
time: Res<Time>,
Expand Down Expand Up @@ -103,7 +110,7 @@ pub fn use_ability(
..Default::default()
})
.insert(Animated::new(0.05, 0, 4, false))
.insert(Projectile::Fireball)
.insert(FireProjectile)
.insert(DestructionTimer(Timer::from_seconds(0.6, false)))
.insert_bundle(PhysicsObjectBundle {
collider: CollisionShape::Cuboid {
Expand Down Expand Up @@ -149,7 +156,7 @@ pub fn use_ability(
..Default::default()
})
.insert(Animated::new(0.1, 0, 5, false))
.insert(Projectile::Wind)
.insert(WindProjectile)
.insert(DestructionTimer(Timer::from_seconds(0.6, false)))
.insert_bundle(PhysicsObjectBundle {
collider: CollisionShape::Cuboid {
Expand Down Expand Up @@ -191,7 +198,7 @@ pub fn use_ability(
sprite: projectile_sprite,
..Default::default()
})
.insert(Projectile::Water)
.insert(WaterProjectile)
.insert_bundle(PhysicsObjectBundle {
collider: CollisionShape::Sphere { radius: 4.0 },
rb: RigidBody::Dynamic,
Expand All @@ -214,95 +221,122 @@ pub fn use_ability(
}
}

pub fn projectile_collision(
pub fn fire_projectile_collision(
mut commands: Commands,
mut projectiles: Query<
(&Projectile, &Velocity, &mut CollisionLayers),
(Without<Block>, Without<Lava>),
>,
flamables: Query<(Entity, &Flamable)>,
mut blocks: Query<&mut Velocity, (With<Block>, Without<Projectile>)>,
mut lava: Query<
(Entity, &mut Animated, &mut RigidBody, &mut CollisionLayers),
(With<Lava>, Without<Projectile>),
>,
fireballs: Query<Entity, (With<FireProjectile>, Without<Block>, Without<Lava>)>,
flamables: Query<Entity, With<Flamable>>,
mut goblins: Query<(&mut AnimationState, &mut Velocity, &mut Patrol)>,
mut collisions: EventReader<CollisionEvent>,
audio: Res<Audio>,
audio_manager: Res<AudioAssets>,
audio_assets: Res<AudioAssets>,
) {
// Should probably split this into multiple systems

for event in collisions.iter().filter(|e| e.is_started()) {
let (e1, e2) = event.rigid_body_entities();
if let Ok((projectile, projectile_velocity, layers)) = projectiles.get_mut(e1) {
if fireballs.contains(e1) {
// entity 1 is projectile
resolve_projectile_collision(
&mut commands,
projectile,
projectile_velocity,
layers,
e1,
e2,
&flamables,
&mut blocks,
&mut lava,
&audio,
&audio_manager,
);
} else if let Ok((projectile, projectile_velocity, layers)) = projectiles.get_mut(e2) {
if let Ok((mut state, mut velocity, mut patrol)) = goblins.get_mut(e2) {
commands.entity(e1).despawn_recursive();
audio.play(audio_assets.hurt.clone());
// play goblin death animation
*state = AnimationState::Death;
patrol.movement_speed = 0.0;
velocity.linear.x = 0.0;
commands
.entity(e2)
.remove::<Hurtbox>()
.remove::<RigidBody>()
.insert(DestructionTimer(Timer::from_seconds(0.6, false)));
} else if flamables.contains(e2) {
// despawn
commands.entity(e2).despawn_recursive();
commands.entity(e1).despawn_recursive();
audio.play(audio_assets.hurt.clone());
}
} else if fireballs.contains(e2) {
// entity 2 is projectile
resolve_projectile_collision(
&mut commands,
projectile,
projectile_velocity,
layers,
e2,
e1,
&flamables,
&mut blocks,
&mut lava,
&audio,
&audio_manager,
);
if let Ok((mut state, mut velocity, mut patrol)) = goblins.get_mut(e1) {
commands.entity(e2).despawn_recursive();
audio.play(audio_assets.hurt.clone());
// play goblin death animation
*state = AnimationState::Death;
patrol.movement_speed = 0.0;
velocity.linear.x = 0.0;
commands
.entity(e1)
.remove::<Hurtbox>()
.remove::<RigidBody>()
.insert(DestructionTimer(Timer::from_seconds(0.6, false)));
} else if flamables.contains(e1) {
// despawn
commands.entity(e2).despawn_recursive();
commands.entity(e1).despawn_recursive();
audio.play(audio_assets.hurt.clone());
}
}
}
}

fn resolve_projectile_collision(
commands: &mut Commands,
projectile: &Projectile,
projectile_velocity: &Velocity,
mut layers: Mut<CollisionLayers>,
projectile_entity: Entity,
other: Entity,
flamables: &Query<(Entity, &Flamable)>,
blocks: &mut Query<&mut Velocity, (With<Block>, Without<Projectile>)>,
lava: &mut Query<
(Entity, &mut Animated, &mut RigidBody, &mut CollisionLayers),
(With<Lava>, Without<Projectile>),
pub fn wind_projectile_collision(
mut projectiles: Query<
(&Velocity, &mut CollisionLayers),
(With<WindProjectile>, Without<Block>),
>,
audio: &Res<Audio>,
audio_manager: &Res<AudioAssets>,
mut blocks: Query<&mut Velocity, (With<Block>, Without<FireProjectile>)>,
mut collisions: EventReader<CollisionEvent>,
) {
match projectile {
Projectile::Fireball => {
if flamables.get(other).is_ok() {
// despawn
commands.entity(other).despawn_recursive();
commands.entity(projectile_entity).despawn_recursive();
audio.play(audio_manager.hurt.clone());
for event in collisions.iter().filter(|e| e.is_started()) {
let (e1, e2) = event.rigid_body_entities();
if let Ok((projectile_velocity, mut layers)) = projectiles.get_mut(e1) {
// entity 1 is projectile
if let Ok(mut velocity) = blocks.get_mut(e2) {
// push
velocity.linear = projectile_velocity.linear;
*layers = layers.without_mask(PhysicsLayers::Movable);
}
}
Projectile::Wind => {
if let Ok(mut velocity) = blocks.get_mut(other) {
} else if let Ok((projectile_velocity, mut layers)) = projectiles.get_mut(e2) {
// entity 2 is projectile
if let Ok(mut velocity) = blocks.get_mut(e1) {
// push
velocity.linear = projectile_velocity.linear;
*layers = layers.without_mask(PhysicsLayers::Movable);
}
}
Projectile::Water => {
commands.entity(projectile_entity).despawn();
if let Ok((entity, mut animation, mut rb, mut layers)) = lava.get_mut(other) {
}
}

pub fn water_projectile_collision(
mut commands: Commands,
projectiles: Query<Entity, With<WaterProjectile>>,
mut lava: Query<
(Entity, &mut Animated, &mut RigidBody, &mut CollisionLayers),
(With<Lava>, Without<WaterProjectile>),
>,
mut collisions: EventReader<CollisionEvent>,
audio: Res<Audio>,
audio_assets: Res<AudioAssets>,
) {
for event in collisions.iter().filter(|e| e.is_started()) {
let (e1, e2) = event.rigid_body_entities();
if projectiles.contains(e1) {
// entity 1 is projectile
commands.entity(e1).despawn();
if let Ok((entity, mut animation, mut rb, mut layers)) = lava.get_mut(e2) {
// turn lava to stone
animation.start = 8;
animation.end = 9;
if *rb != RigidBody::Static {
*rb = RigidBody::Static;
}
commands.entity(entity).remove::<Lava>();
if !layers.contains_group(PhysicsLayers::Terrain) {
*layers = layers.with_group(PhysicsLayers::Terrain);
}
audio.play(audio_assets.steam.clone());
}
} else if projectiles.contains(e2) {
// entity 2 is projectile
commands.entity(e2).despawn();
if let Ok((entity, mut animation, mut rb, mut layers)) = lava.get_mut(e1) {
// turn lava to stone
animation.start = 8;
animation.end = 9;
Expand All @@ -313,7 +347,7 @@ fn resolve_projectile_collision(
if !layers.contains_group(PhysicsLayers::Terrain) {
*layers = layers.with_group(PhysicsLayers::Terrain);
}
audio.play(audio_manager.steam.clone());
audio.play(audio_assets.steam.clone());
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub struct Animated {
timer: Timer,
pub start: usize,
pub end: usize,
play_once: bool,
pub play_once: bool,
}
impl Animated {
pub fn new(seconds_per_frame: f32, start: usize, end: usize, play_once: bool) -> Self {
Expand Down
37 changes: 24 additions & 13 deletions src/entity/goblin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl LdtkEntity for Patrol {
pub enum AnimationState {
Idle,
Walking,
Death,
}
impl Default for AnimationState {
fn default() -> Self {
Expand All @@ -120,16 +121,18 @@ pub struct GoblinBundle {
pub patrol: Patrol,
}

pub fn patrol(
mut query: Query<(
&Patrol,
&mut Velocity,
&Transform,
&mut TextureAtlasSprite,
&mut AnimationState,
)>,
) {
for (patrol, mut velocity, transform, mut sprite, mut state) in query.iter_mut() {
pub fn init_animation_state(mut query: Query<(&Patrol, &mut AnimationState), Added<Patrol>>) {
for (patrol, mut state) in query.iter_mut() {
if patrol.points.is_some() {
*state = AnimationState::Walking;
} else {
*state = AnimationState::Idle;
}
}
}

pub fn patrol(mut query: Query<(&Patrol, &mut Velocity, &Transform, &mut TextureAtlasSprite)>) {
for (patrol, mut velocity, transform, mut sprite) in query.iter_mut() {
if let Some((start, end)) = patrol.points {
if transform.translation.x < start.x.min(end.x) || velocity.linear.x == 0.0 {
velocity.linear.x = patrol.movement_speed;
Expand All @@ -138,15 +141,17 @@ pub fn patrol(
velocity.linear.x = -patrol.movement_speed;
sprite.flip_x = true;
}
*state = AnimationState::Walking;
}
}
}

pub fn animation_state_update(
mut query: Query<(&mut Animated, &AnimationState), Changed<AnimationState>>,
mut query: Query<
(&mut Animated, &AnimationState, &mut TextureAtlasSprite),
Changed<AnimationState>,
>,
) {
for (mut animation, state) in query.iter_mut() {
for (mut animation, state, mut atlas) in query.iter_mut() {
match state {
AnimationState::Idle => {
animation.start = 18;
Expand All @@ -156,7 +161,13 @@ pub fn animation_state_update(
animation.start = 0;
animation.end = 6;
}
AnimationState::Death => {
animation.start = 6;
animation.end = 12;
animation.play_once = true;
}
}
atlas.index = animation.start;
}
}

Expand Down
Loading

0 comments on commit c662f1c

Please sign in to comment.