Skip to content

Commit 18a41f2

Browse files
authored
Adding an animation action that can spawn projectiles (#155)
* Fixing cases where monsters with custom projectiles could shoot themselves * Adding an animation action that can spawn projectiles * Disconnecting the ranged and melee attack timers * Spell cast animations stop monster movement as well * Monsters tossing potions will set them off
1 parent b7ab6db commit 18a41f2

File tree

2 files changed

+281
-84
lines changed

2 files changed

+281
-84
lines changed

Dungeoneer/src/com/interrupt/dungeoneer/entities/Monster.java

+158-84
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.interrupt.dungeoneer.entities;
22

3+
import com.badlogic.gdx.Gdx;
34
import com.badlogic.gdx.graphics.Color;
45
import com.badlogic.gdx.math.Vector3;
56
import com.badlogic.gdx.utils.Array;
@@ -70,6 +71,10 @@ public class Monster extends Actor implements Directional {
7071
@EditorProperty
7172
private float attackTime = 60;
7273

74+
/** Time to wait before starting to move again after an attack. */
75+
@EditorProperty
76+
private float postAttackMoveWaitTime = 0.01f;
77+
7378
/** Time interval between monster projectile attacks. */
7479
@EditorProperty
7580
private float projectileAttackTime = 100;
@@ -87,9 +92,11 @@ public class Monster extends Actor implements Directional {
8792
public float projectileOffset = 0f;
8893

8994
private float attacktimer = 0;
95+
private float rangedAttackTimer = 0;
9096
private float regenManaTimer = 0;
9197

9298
private float stuntime = 0;
99+
private float postAttackMoveWaitTimer = 0;
93100

94101
/** Is monster alerted to player's presence? */
95102
public boolean alerted = false;
@@ -200,6 +207,9 @@ public class Monster extends Actor implements Directional {
200207
/** Monster attack animation. */
201208
private SpriteAnimation attackAnimation = null;
202209

210+
/** Monster ranged attack animation. */
211+
private SpriteAnimation rangedAttackAnimation = null;
212+
203213
/** Monster cast animation. */
204214
private SpriteAnimation castAnimation = null;
205215

@@ -279,6 +289,7 @@ public Monster() {
279289
stepHeight = 0.4f;
280290

281291
attacktimer = 30;
292+
rangedAttackTimer = 30;
282293

283294
mass = 2f;
284295

@@ -379,6 +390,7 @@ public int takeDamage(int damage, DamageType damageType, Entity instigator) {
379390
int tookDamage = super.takeDamage(damage, damageType, instigator);
380391
if(doPainRoll(tookDamage)) {
381392
if (attackAnimation != null) attackAnimation.playing = false;
393+
if (rangedAttackAnimation != null) rangedAttackAnimation.playing = false;
382394
if (hurtAnimation != null) hurtAnimation.play();
383395
}
384396
return tookDamage;
@@ -437,6 +449,7 @@ public void tick(Level level, float delta)
437449

438450
// tick some timers (TODO: MAKE A TIMER HELPER)
439451
if(attacktimer > 0) attacktimer -= delta;
452+
if(rangedAttackTimer > 0) rangedAttackTimer -= delta;
440453
regenManaTimer += delta;
441454

442455
if(regenManaTimer > 60) {
@@ -473,7 +486,9 @@ public void tick(Level level, float delta)
473486
if(hostile) {
474487
canSeePlayer = level.canSeeIncludingDoors(x, y, player.x, player.y, 17);
475488

489+
// Reset attack timers if we lose track of the player
476490
if(!canSeePlayer && attacktimer < 30) attacktimer = 30;
491+
if(!canSeePlayer && rangedAttackTimer < 30) rangedAttackTimer = 30;
477492

478493
pxdir = player.x - x;
479494
pydir = player.y - y;
@@ -506,6 +521,7 @@ else if(playerdist > 0.75f) {
506521
Audio.playPositionedSound(alertSound, new Vector3(x, y, z), soundVolume, 1f, 12f);
507522

508523
attacktimer = 40 + Game.rand.nextInt(20);
524+
rangedAttackTimer = 40 + Game.rand.nextInt(20);
509525
}
510526
}
511527

@@ -663,15 +679,34 @@ else if(tryAt == 4)
663679
txa = 0;
664680
tza = 0;
665681
}
682+
683+
// stop moving after an attack
684+
if(postAttackMoveWaitTimer > 0) {
685+
boolean isPlayingAttackAnimation = false;
686+
687+
if(attackAnimation != null)
688+
isPlayingAttackAnimation |= attackAnimation.playing;
689+
if(rangedAttackAnimation != null)
690+
isPlayingAttackAnimation |= rangedAttackAnimation.playing;
691+
if(castAnimation != null)
692+
isPlayingAttackAnimation |= castAnimation.playing;
693+
694+
if(!isPlayingAttackAnimation)
695+
postAttackMoveWaitTimer -= delta;
696+
697+
txa = 0;
698+
tza = 0;
699+
}
666700

667-
if(hostile && (playerdist < collision.x + reach || playerdist < collision.x + attackStartDistance))
668-
{
669-
float zDiff = Math.abs((player.z + 0.3f) - (z + 0.3f));
670-
if(zDiff < reach || zDiff < attackStartDistance) {
671-
attack(player);
672-
txa = 0;
673-
tza = 0;
674-
}
701+
if(hostile) {
702+
if((playerdist < collision.x + reach || playerdist < collision.x + attackStartDistance)) {
703+
float zDiff = Math.abs((player.z + 0.3f) - (z + 0.3f));
704+
if (zDiff < reach || zDiff < attackStartDistance) {
705+
attack(player);
706+
}
707+
} else if(playerdist > projectileAttackMinDistance && playerdist < projectileAttackMaxDistance && (projectile != null || rangedAttackAnimation != null)) {
708+
rangedAttack(player);
709+
}
675710
}
676711

677712
if(walkSound != null) {
@@ -762,62 +797,8 @@ else if(tryAt == 4)
762797
}
763798
}
764799
}
765-
}
766-
767-
if(projectile != null && attacktimer <= 0 && stuntime <= 0 && canSeePlayer && hostile && !isParalyzed()
768-
&& playerdist < projectileAttackMaxDistance && playerdist > projectileAttackMinDistance)
769-
{
770-
// face fire direction!
771-
setDirectionTowards(player);
772-
773-
attacktimer = projectileAttackTime;
774-
if(attackAnimation != null) {
775-
attackAnimation.play();
776-
}
777800

778-
try {
779-
Entity pCopy = null;
780-
if(projectile instanceof Prefab) {
781-
Prefab p = (Prefab)projectile;
782-
pCopy = EntityManager.instance.getEntity(p.category, p.name);
783-
}
784-
else {
785-
pCopy = (Entity) KryoSerializer.copyObject(projectile);
786-
}
787-
788-
if(pCopy != null) {
789-
pCopy.owner = this;
790-
pCopy.ignorePlayerCollision = false;
791-
792-
// spawns from this entity
793-
pCopy.x = x;
794-
pCopy.y = y;
795-
pCopy.z = projectileOffset + z + (collision.z * 0.6f);
796-
797-
Vector3 dirToPlayer = new Vector3(player.x, player.y, player.z + (player.collision.z * 0.5f));
798-
if (!pCopy.floating) {
799-
// Go ballistics
800-
dirToPlayer.z += (playerdist * playerdist) * projectileBallisticsMod;
801-
}
802-
803-
dirToPlayer.sub(pCopy.x, pCopy.y, pCopy.z).nor();
804-
805-
// offset out of collision
806-
pCopy.x += dirToPlayer.x * collision.x * 0.5f;
807-
pCopy.y += dirToPlayer.y * collision.x * 0.5f;
808-
pCopy.z += dirToPlayer.z * collision.x * 0.5f;
809-
810-
// initial speed
811-
dirToPlayer.scl(projectileSpeed);
812-
813-
pCopy.xa = dirToPlayer.x;
814-
pCopy.ya = dirToPlayer.y;
815-
pCopy.za = dirToPlayer.z;
816-
817-
level.SpawnEntity(pCopy);
818-
}
819-
}
820-
catch(Exception ex) { }
801+
postAttackMoveWaitTimer = postAttackMoveWaitTime;
821802
}
822803

823804
// idle sounds!
@@ -836,10 +817,11 @@ else if(tryAt == 4)
836817
if(hurtAnimation != null && hurtAnimation.playing) hurtAnimation.animate(delta, this);
837818
else if(castAnimation != null && castAnimation.playing) castAnimation.animate(delta, this);
838819
else if(attackAnimation != null && attackAnimation.playing) attackAnimation.animate(delta, this);
820+
else if(rangedAttackAnimation != null && rangedAttackAnimation.playing) rangedAttackAnimation.animate(delta, this);
839821
else if(dodgeAnimation != null && dodgeAnimation.playing) dodgeAnimation.animate(delta, this);
840822
else if(walkAnimation != null) walkAnimation.animate(delta, this);
841823
}
842-
824+
843825
private float getSpeed() {
844826
float baseSpeed = speed;
845827

@@ -1142,36 +1124,128 @@ public void attack(Entity target)
11421124
if(!hostile) return;
11431125
if(stuntime > 0) return;
11441126
if(isParalyzed()) return;
1145-
if(!alerted) alerted = true;
1127+
alerted = true;
11461128

1147-
if(attacktimer <= 0)
1148-
{
1149-
if(dodgeAnimation != null) {
1150-
dodgeAnimation.play();
1151-
attacktimer = attackTime * 2;
1152-
return;
1129+
if(attacktimer > 0)
1130+
return;
1131+
1132+
if(dodgeAnimation != null) {
1133+
dodgeAnimation.play();
1134+
attacktimer = attackTime * 2;
1135+
return;
1136+
}
1137+
1138+
attackTarget = target;
1139+
attacktimer = attackTime + Game.rand.nextInt(10);
1140+
Audio.playPositionedSound(attackSwingSound, new Vector3(x, y, z), 0.75f, 1f, 12f);
1141+
1142+
setDirectionTowards(attackTarget);
1143+
1144+
if(Game.rand.nextFloat() > 0.7f) Audio.playPositionedSound(attackSound, new Vector3(x, y, z), soundVolume, 1f, 12f);
1145+
1146+
if(attackAnimation != null) {
1147+
attackAnimation.play();
1148+
1149+
// if no actions are set, just do the damage hit now
1150+
// otherwise assume that there's a set damage frame
1151+
if(attackAnimation.actions == null) {
1152+
tryDamageHit(attackTarget, 0f, 0.05f);
11531153
}
1154+
}
1155+
else {
1156+
tryDamageHit(attackTarget, 0f, 0.05f);
1157+
}
11541158

1155-
attackTarget = target;
1156-
attacktimer = attackTime + Game.rand.nextInt(10);
1157-
Audio.playPositionedSound(attackSwingSound, new Vector3(x, y, z), 0.75f, 1f, 12f);
1159+
postAttackMoveWaitTimer = postAttackMoveWaitTime;
1160+
}
1161+
1162+
private void rangedAttack(Entity target) {
1163+
if(target == null || !target.isActive) return;
1164+
if(!hostile) return;
1165+
if(stuntime > 0) return;
1166+
if(isParalyzed()) return;
1167+
alerted = true;
11581168

1159-
setDirectionTowards(attackTarget);
1169+
if(rangedAttackTimer > 0)
1170+
return;
11601171

1161-
if(Game.rand.nextFloat() > 0.7f) Audio.playPositionedSound(attackSound, new Vector3(x, y, z), soundVolume, 1f, 12f);
1172+
attackTarget = target;
1173+
rangedAttackTimer = projectileAttackTime + Game.rand.nextInt(15);
1174+
setDirectionTowards(attackTarget);
11621175

1163-
if(attackAnimation != null) {
1164-
attackAnimation.play();
1176+
if(rangedAttackAnimation != null) {
1177+
rangedAttackAnimation.play();
11651178

1166-
// if no actions are set, just do the damage hit now
1167-
// otherwise assume that there's a set damage frame
1168-
if(attackAnimation.actions == null) {
1169-
tryDamageHit(attackTarget, 0f, 0.05f);
1170-
}
1179+
// If no actions are set, try to spawn the basic projectile.
1180+
// Otherwise, assume there is a ProjectileAttackAction in there.
1181+
if(rangedAttackAnimation.actions == null) {
1182+
spawnBasicProjectile(target);
1183+
}
1184+
}
1185+
else {
1186+
spawnBasicProjectile(target);
1187+
}
1188+
1189+
postAttackMoveWaitTimer = postAttackMoveWaitTime;
1190+
}
1191+
1192+
private void spawnBasicProjectile(Entity target) {
1193+
// face fire direction!
1194+
setDirectionTowards(target);
1195+
1196+
rangedAttackTimer = projectileAttackTime;
1197+
if(attackAnimation != null) {
1198+
attackAnimation.play();
1199+
}
1200+
1201+
try {
1202+
Entity pCopy = null;
1203+
if(projectile instanceof Prefab) {
1204+
Prefab p = (Prefab)projectile;
1205+
pCopy = EntityManager.instance.getEntity(p.category, p.name);
11711206
}
11721207
else {
1173-
tryDamageHit(attackTarget, 0f, 0.05f);
1208+
pCopy = (Entity) KryoSerializer.copyObject(projectile);
11741209
}
1210+
1211+
if(pCopy != null) {
1212+
pCopy.owner = this;
1213+
pCopy.ignorePlayerCollision = false;
1214+
1215+
// spawns from this entity
1216+
pCopy.x = x;
1217+
pCopy.y = y;
1218+
pCopy.z = projectileOffset + z + (collision.z * 0.6f);
1219+
1220+
Vector3 dirToTarget = new Vector3(target.x, target.y, target.z + (target.collision.z * 0.5f));
1221+
if (!pCopy.floating) {
1222+
// Go ballistics
1223+
dirToTarget.z += (playerdist * playerdist) * projectileBallisticsMod;
1224+
}
1225+
1226+
dirToTarget.sub(pCopy.x, pCopy.y, pCopy.z).nor();
1227+
1228+
// offset out of collision
1229+
pCopy.x += dirToTarget.x * collision.x * 0.5f;
1230+
pCopy.y += dirToTarget.y * collision.x * 0.5f;
1231+
pCopy.z += dirToTarget.z * collision.x * 0.5f;
1232+
1233+
// initial speed
1234+
dirToTarget.scl(projectileSpeed);
1235+
1236+
pCopy.xa = dirToTarget.x;
1237+
pCopy.ya = dirToTarget.y;
1238+
pCopy.za = dirToTarget.z;
1239+
1240+
// Some items trigger effects when thrown
1241+
if(pCopy instanceof Item)
1242+
((Item)pCopy).tossItem(Game.instance.level, 1.0f);
1243+
1244+
Game.instance.level.SpawnEntity(pCopy);
1245+
}
1246+
}
1247+
catch(Exception ex) {
1248+
Gdx.app.log("DelverGame", "Error spawning projectile: " + ex.getMessage());
11751249
}
11761250
}
11771251

0 commit comments

Comments
 (0)