Files
DoomRTX/neo/prey/Prey/game_eggspawner.cpp
T
2026-05-05 15:01:01 -07:00

282 lines
8.1 KiB
C++

#include "precompiled.h"
#pragma hdrstop
#include "prey_local.h"
//==========================================================================
//
// hhEggSpawner
//
// When activated, shoots out an egg along it's X axis
//==========================================================================
#define MAX_HATCH_TIME (SEC2MS(10))
CLASS_DECLARATION(hhAnimatedEntity, hhEggSpawner)
EVENT( EV_PlayIdle, hhEggSpawner::Event_PlayIdle)
EVENT( EV_Activate, hhEggSpawner::Event_Activate)
END_CLASS
void hhEggSpawner::Spawn(void) {
GetPhysics()->SetContents( CONTENTS_SOLID );
fl.takedamage = true;
idleAnim = GetAnimator()->GetAnim("idle");
hatchAnim = GetAnimator()->GetAnim("launch");
painAnim = GetAnimator()->GetAnim("pain");
PostEventMS(&EV_PlayIdle, 0);
}
void hhEggSpawner::Save(idSaveGame *savefile) const {
savefile->WriteInt( idleAnim );
savefile->WriteInt( hatchAnim );
savefile->WriteInt( painAnim );
}
void hhEggSpawner::Restore( idRestoreGame *savefile ) {
savefile->ReadInt( idleAnim );
savefile->ReadInt( hatchAnim );
savefile->ReadInt( painAnim );
}
void hhEggSpawner::SpawnEgg(idEntity *activator) {
idVec3 dir = GetPhysics()->GetAxis()[0];
idVec3 offset = spawnArgs.GetVector("offset_spawn");
float power = spawnArgs.GetFloat("power");
// Play anim
if (hatchAnim) {
GetAnimator()->PlayAnim(ANIMCHANNEL_ALL, hatchAnim, gameLocal.time, 0);
int ms = GetAnimator()->GetAnim( hatchAnim )->Length();
PostEventMS(&EV_PlayIdle, ms);
}
idDict args;
args.Clear();
args.SetVector( "origin", GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis() );
hhEgg *egg = static_cast<hhEgg*>(gameLocal.SpawnObject(spawnArgs.GetString("def_egg"), &args));
if (egg) {
egg->GetPhysics()->SetLinearVelocity(dir * power);
egg->SetActivator(activator);
// Copy our targets
for ( int i = 0; i < targets.Num(); i++ ) {
egg->targets.AddUnique(targets[i]);
}
}
StartSound( "snd_spawn", SND_CHANNEL_ANY, 0, true, NULL );
}
void hhEggSpawner::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
// Don't actually take damage, but give feedback
if (painAnim) {
GetAnimator()->PlayAnim(ANIMCHANNEL_ALL, painAnim, gameLocal.time, 0);
int ms = GetAnimator()->GetAnim( painAnim )->Length();
PostEventMS(&EV_PlayIdle, ms);
}
}
void hhEggSpawner::Event_PlayIdle() {
if (idleAnim) {
GetAnimator()->ClearAllAnims(gameLocal.time, 200);
GetAnimator()->CycleAnim(ANIMCHANNEL_ALL, idleAnim, gameLocal.time, 200);
}
}
void hhEggSpawner::Event_Activate(idEntity *activator) {
SpawnEgg(activator);
}
//==========================================================================
//
// hhEgg
//
// Moveable that spawns a creature on impact
//==========================================================================
const idEventDef EV_Hatch("<hatch>");
CLASS_DECLARATION(hhMoveable, hhEgg)
EVENT( EV_Activate, hhEgg::Event_Activate)
EVENT( EV_Hatch, hhEgg::Event_Hatch)
END_CLASS
void hhEgg::Spawn(void) {
enemy = NULL;
bHatched = false;
bHatching = false;
const char *tableName = spawnArgs.GetString("table_hatch");
table = static_cast<const idDeclTable *>(declManager->FindType( DECL_TABLE, tableName, true ));
hatchTime = -1.0f;
PostEventSec( &EV_Activate, spawnArgs.GetFloat("secondsBeforeHatch"), this );
}
void hhEgg::Save(idSaveGame *savefile) const {
savefile->WriteBool( bHatching );
savefile->WriteBool( bHatched );
savefile->WriteFloat( deformAlpha.GetStartTime() ); // idInterpolate<float>
savefile->WriteFloat( deformAlpha.GetDuration() );
savefile->WriteFloat( deformAlpha.GetStartValue() );
savefile->WriteFloat( deformAlpha.GetEndValue() );
savefile->WriteFloat( hatchTime );
enemy.Save(savefile);
}
void hhEgg::Restore( idRestoreGame *savefile ) {
float set;
savefile->ReadBool( bHatching );
savefile->ReadBool( bHatched );
savefile->ReadFloat( set ); // idInterpolate<float>
deformAlpha.SetStartTime( set );
savefile->ReadFloat( set );
deformAlpha.SetDuration( set );
savefile->ReadFloat( set );
deformAlpha.SetStartValue(set);
savefile->ReadFloat( set );
deformAlpha.SetEndValue( set );
savefile->ReadFloat( hatchTime );
const char *tableName = spawnArgs.GetString("table_hatch");
table = static_cast<const idDeclTable *>(declManager->FindType( DECL_TABLE, tableName, true ));
enemy.Restore(savefile);
}
void hhEgg::SetActivator(idEntity *activator) {
if (activator && activator->IsType(idActor::Type)) {
enemy = static_cast<idActor*>(activator);
}
}
void hhEgg::Ticker() {
if (bHatching) {
// over time, send different parms into deformation
if (deformAlpha.IsDone(gameLocal.time)) {
Hatch();
}
else {
float alpha = deformAlpha.GetCurrentValue(gameLocal.time);
float value = table->TableLookup(alpha);
SetShaderParm(SHADERPARM_ANY_DEFORM, DEFORMTYPE_VIBRATE);
SetShaderParm(SHADERPARM_ANY_DEFORM_PARM1, value);
}
}
}
bool hhEgg::Collide( const trace_t &collision, const idVec3 &velocity ) {
AttemptToPlayBounceSound( collision, velocity );
if (spawnArgs.GetBool("spawnOnImpact")) { //obs
Hatch();
return true;
}
return false;
}
void hhEgg::Killed(idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location) {
Hatch();
}
bool hhEgg::SpawnHatchling(const char *monsterName, const idVec3 &spawnLocation) {
idDict args;
args.Clear();
args.SetVector( "origin", spawnLocation );
args.SetFloat( "angle", GetAxis().ToAngles().yaw );
args.Set( "trigger_anim", "birth" );
hhMonsterAI *monster = static_cast<hhMonsterAI*>(gameLocal.SpawnObject(monsterName, &args));
if (monster) {
// Set enemy
if (enemy.IsValid()) {
monster->SetEnemy(enemy.GetEntity());
}
// Copy our targets
for ( int i = 0; i < targets.Num(); i++ ) {
monster->targets.AddUnique(targets[i]);
}
monster->PostEventSec( &EV_Activate, 0, this );
}
return monster != NULL;
}
void hhEgg::Hatch() {
if (!bHatched) {
if (hatchTime == -1.0f) {
hatchTime = gameLocal.time;
}
// Spawn hatchling
const char *monsterName = spawnArgs.GetString("def_hatchling", NULL);
if (monsterName) {
idVec3 spawnLocation = GetPhysics()->GetOrigin();
const float hatchOffsetDistance = 50.0f;
bool spawned = false;
idVec3 offset;
idVec3 offsetLocation;
trace_t trace;
memset(&trace, 0, sizeof(trace_t));
if (hhUtils::EntityDefWillFit(monsterName, spawnLocation, mat3_identity, MASK_MONSTERSOLID, this)) {
//gameRenderWorld->DebugArrow(colorGreen, spawnLocation, offsetLocation, 10, 10000);
spawned = SpawnHatchling(monsterName, spawnLocation);
}
if (!spawned) { // Creature wouldn't fit, try again later
CancelEvents(&EV_Hatch);
if (gameLocal.time - hatchTime > MAX_HATCH_TIME) {
// Give up after 15 seconds of struggling
ActivateTargets(this);
// Spawn gibs from monster
const idDict *dict = gameLocal.FindEntityDefDict(monsterName, false);
if (dict && dict->FindKey("def_gibdebrisspawner")) {
GetPhysics()->SetLinearVelocity( vec3_zero ); //HUMANHEAD jsh PCF zero out velocity to prevent debris huge translation
hhUtils::SpawnDebrisMass(dict->GetString("def_gibdebrisspawner"), this);
}
} else {
//idVec3 dir = hhUtils::RandomVector();
//gameRenderWorld->DebugArrow(colorGreen, spawnLocation, spawnLocation + dir * 50.0f, 10, 250);
GetPhysics()->AddForce(0, GetPhysics()->GetOrigin(), hhUtils::RandomVector() * idMath::ClampFloat(0x1000000, 0x100000000, (0x1000000 * ((gameLocal.time - hatchTime) / 25.0f))));
PostEventMS(&EV_Hatch, 250);
return;
}
}
}
hatchTime = -1.0f;
bHatched = true;
GetPhysics()->SetContents(0);
PostEventMS(&EV_Remove, 0);
StartSound( "snd_hatch", SND_CHANNEL_ANY, 0, true, NULL );
GetPhysics()->SetLinearVelocity( vec3_zero ); //HUMANHEAD jsh PCF zero out velocity to prevent debris huge translation
hhUtils::SpawnDebrisMass(spawnArgs.GetString("def_afterbirth"), this);
}
}
void hhEgg::Event_Activate(idEntity *activator) {
if (!bHatching) {
bHatching = true;
int msForTable = SEC2MS(spawnArgs.GetFloat("secondsToHatch"));
deformAlpha.Init(gameLocal.time, msForTable, 0.0f, 1.0f);
BecomeActive(TH_TICKER);
}
}
void hhEgg::Event_Hatch(void) {
Hatch();
}