Skip to content

Commit fe2d97b

Browse files
authored
Burst fire for Actor AIs (#589)
* Add burst fire behaviour to Actor Attack Func * Set burst properties via custom logics * Serialise the new data * Loop the attack animation during burst * Create a "dying" flag for actors, to stop burst fire when actor is dying
1 parent efcc51f commit fe2d97b

9 files changed

Lines changed: 115 additions & 4 deletions

File tree

TheForceEngine/TFE_DarkForces/Actor/actor.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,14 @@ namespace TFE_DarkForces
305305
attackMod->meleeDmg = 0;
306306
attackMod->meleeRate = FIXED(230);
307307
attackMod->attackFlags = ATTFLAG_RANGED | ATTFLAG_LIT_RNG;
308+
309+
// new to TFE - burstfire
310+
attackMod->hasBurstFire = JFALSE;
311+
attackMod->burstFire.burstNumber = 5;
312+
attackMod->burstFire.variation = 2;
313+
attackMod->burstFire.interval = 29;
314+
attackMod->burstFire.shotCount = 5;
315+
attackMod->burstFire.lastShot = 0;
308316

309317
// Why is this being returned? This function maybe should be a void?
310318
return attackMod->fireOffset.y;
@@ -318,6 +326,8 @@ namespace TFE_DarkForces
318326
moveMod->collisionFlags |= ACTORCOL_GRAVITY;
319327
// Added to disable auto-aim when dying.
320328
logic->logic.obj->flags &= ~OBJ_FLAG_AIM;
329+
330+
logic->flags |= ACTOR_DYING; // added to stop burst fire when actor is dying
321331
}
322332

323333
// Returns JTRUE if the object is on the floor, or JFALSE is not on the floor or moving too fast.
@@ -987,6 +997,11 @@ namespace TFE_DarkForces
987997
// Do ranged attack (primary)
988998
attackMod->anim.state = STATE_ATTACK1;
989999
attackMod->timing.delay = attackMod->timing.rangedDelay;
1000+
1001+
if (attackMod->hasBurstFire)
1002+
{
1003+
attackMod->anim.flags &= ~AFLAG_PLAYONCE; // if logic has burst fire, allow attack anim to loop
1004+
}
9901005
}
9911006

9921007
if (obj->type == OBJ_TYPE_SPRITE)
@@ -1020,7 +1035,7 @@ namespace TFE_DarkForces
10201035
} break;
10211036
case STATE_ATTACK1:
10221037
{
1023-
if (!(attackMod->anim.flags & AFLAG_READY))
1038+
if (!(attackMod->anim.flags & AFLAG_READY) && !attackMod->hasBurstFire)
10241039
{
10251040
break;
10261041
}
@@ -1049,7 +1064,42 @@ namespace TFE_DarkForces
10491064
obj->flags |= OBJ_FLAG_FULLBRIGHT;
10501065
}
10511066

1052-
attackMod->anim.state = STATE_ANIMATE1;
1067+
// Burst fire option - set via custom logics
1068+
if (attackMod->hasBurstFire)
1069+
{
1070+
if (s_curTick < attackMod->burstFire.lastShot + attackMod->burstFire.interval)
1071+
{
1072+
break;
1073+
}
1074+
1075+
if (logic->flags & ACTOR_DYING) { break; }
1076+
1077+
if (attackMod->burstFire.shotCount <= 1)
1078+
{
1079+
// Burst is finished, end the looping & reset the shot count
1080+
attackMod->anim.state = STATE_ANIMATE1;
1081+
attackMod->anim.flags |= AFLAG_PLAYONCE;
1082+
1083+
s32 var = random(attackMod->burstFire.variation * 2);
1084+
s32 nextBurstNumber = attackMod->burstFire.burstNumber - attackMod->burstFire.variation + var;
1085+
attackMod->burstFire.shotCount = max(nextBurstNumber, 2);
1086+
}
1087+
else
1088+
{
1089+
// Reorient towards the player
1090+
attackMod->target.yaw = vec2ToAngle(s_playerObject->posWS.x - obj->posWS.x, s_playerObject->posWS.z - obj->posWS.z);
1091+
1092+
// Fire the next shot in the burst
1093+
attackMod->burstFire.lastShot = s_curTick;
1094+
attackMod->burstFire.shotCount--;
1095+
}
1096+
}
1097+
else
1098+
{
1099+
// No burst fire -- vanilla logic
1100+
attackMod->anim.state = STATE_ANIMATE1;
1101+
}
1102+
10531103
vec3_fixed fireOffset = {};
10541104

10551105
// Calculate the X,Z fire offsets based on where the enemy is facing. It doesn't matter for Y.

TheForceEngine/TFE_DarkForces/Actor/actor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ enum ActorDispatchFlags
9292
ACTOR_PLAYER_VISIBLE = FLAG_BIT(3),
9393
ACTOR_OFFIC_ALERT = FLAG_BIT(4), // use officer alert sounds
9494
ACTOR_TROOP_ALERT = FLAG_BIT(5), // use stormtrooper alert sounds
95+
96+
// Added for TFE
97+
ACTOR_DYING = FLAG_BIT(6),
9598
};
9699

97100
// Forward Declarations.

TheForceEngine/TFE_DarkForces/Actor/actorModule.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,15 @@ struct ActorTarget
142142
u32 flags;
143143
};
144144

145+
struct ActorBurstFire
146+
{
147+
u32 burstNumber;
148+
u32 variation;
149+
Tick interval;
150+
Tick lastShot;
151+
s32 shotCount;
152+
};
153+
145154
struct AttackModule
146155
{
147156
ActorModule header;
@@ -162,6 +171,10 @@ struct AttackModule
162171
fixed16_16 meleeDmg;
163172
fixed16_16 meleeRate;
164173
u32 attackFlags; // see AttackFlags above.
174+
175+
// New in TFE - Burst fire properties
176+
JBool hasBurstFire;
177+
ActorBurstFire burstFire;
165178
};
166179

167180
struct MovementModule

TheForceEngine/TFE_DarkForces/Actor/actorSerialization.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ namespace TFE_DarkForces
298298
SERIALIZE(SaveVersionInit, attackMod->meleeDmg, 0);
299299
SERIALIZE(SaveVersionInit, attackMod->meleeRate, 0);
300300
SERIALIZE(SaveVersionInit, attackMod->attackFlags, 0);
301+
302+
SERIALIZE(ObjState_BurstFire, attackMod->hasBurstFire, JFALSE);
303+
SERIALIZE(ObjState_BurstFire, attackMod->burstFire.burstNumber, 5);
304+
SERIALIZE(ObjState_BurstFire, attackMod->burstFire.variation, 2);
305+
SERIALIZE(ObjState_BurstFire, attackMod->burstFire.interval, 29);
306+
SERIALIZE(ObjState_BurstFire, attackMod->burstFire.shotCount, 5);
307+
SERIALIZE(ObjState_BurstFire, attackMod->burstFire.lastShot, 0);
301308
}
302309

303310
void actor_serializeAttackModule(Stream* stream, ActorModule*& mod, ActorDispatch* dispatch)

TheForceEngine/TFE_DarkForces/Actor/animTables.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace TFE_DarkForces
1515
{
16-
enum
16+
enum ActorAnimation
1717
{
1818
ANIM_MOVE = 0,
1919
ANIM_ATTACK1 = 1,

TheForceEngine/TFE_DarkForces/Actor/enemies.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,13 @@ namespace TFE_DarkForces
234234
attackMod->fireOffset.x = floatToFixed16(cust->fireOffset.x);
235235
attackMod->fireOffset.y = cust->fireOffset.y < -999 ? attackMod->fireOffset.y : floatToFixed16(cust->fireOffset.y); // if -1000 use the default value
236236
attackMod->fireOffset.z = floatToFixed16(cust->fireOffset.z);
237+
238+
attackMod->hasBurstFire = cust->hasBurstFire ? JTRUE : JFALSE;
239+
attackMod->burstFire.burstNumber = cust->burstNumber;
240+
attackMod->burstFire.shotCount = cust->burstNumber;
241+
attackMod->burstFire.variation = cust->burstVariation;
242+
attackMod->burstFire.interval = cust->burstInterval;
243+
237244
s_actorState.attackMod = attackMod;
238245
actor_addModule(dispatch, (ActorModule*)attackMod);
239246

TheForceEngine/TFE_ExternalData/dfLogics.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,30 @@ namespace TFE_ExternalData
400400
return true;
401401
}
402402

403+
if (cJSON_IsBool(data) && strcasecmp(data->string, "hasBurstFire") == 0)
404+
{
405+
customLogic.hasBurstFire = cJSON_IsTrue(data);
406+
return true;
407+
}
408+
409+
if (cJSON_IsNumber(data) && strcasecmp(data->string, "burstNumber") == 0)
410+
{
411+
customLogic.burstNumber = data->valueint;
412+
return true;
413+
}
414+
415+
if (cJSON_IsNumber(data) && strcasecmp(data->string, "burstVariation") == 0)
416+
{
417+
customLogic.burstVariation = data->valueint;
418+
return true;
419+
}
420+
421+
if (cJSON_IsNumber(data) && strcasecmp(data->string, "burstInterval") == 0)
422+
{
423+
customLogic.burstInterval = (u32)(data->valuedouble * TICKS_PER_SECOND);
424+
return true;
425+
}
426+
403427
// When it comes to offsets these are considered from the perspective of the actor.
404428
//
405429
// Projectile spawn details guide.

TheForceEngine/TFE_ExternalData/dfLogics.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ namespace TFE_ExternalData
4949
u32 fireSpread = 30;
5050
vec3_float fireOffset = { 0, -1000, 0 }; // (y = -1000) will be treated as default
5151

52+
// burst fire
53+
bool hasBurstFire = false;
54+
u32 burstNumber = 5;
55+
u32 burstVariation = 2;
56+
u32 burstInterval = 29;
57+
5258
// Thinker and movement
5359
u32 speed = 4;
5460
u32 verticalSpeed = 10;

TheForceEngine/TFE_Jedi/Level/robjData.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ enum ObjStateVersion : u32
6969
ObjState_RefList = 9,
7070
ObjState_ExternalCamera = 10,
7171
ObjState_LogicScriptCallV1 = 11, // adds ScriptCalls on pickup, death, alert
72-
ObjState_CurVersion = ObjState_LogicScriptCallV1,
72+
ObjState_BurstFire = 12,
73+
ObjState_CurVersion = ObjState_BurstFire,
7374
};
7475

7576
// TFE Scripting

0 commit comments

Comments
 (0)