All Doom 3 AI is now native.

This commit is contained in:
Justin Marshall
2026-05-23 14:19:48 -07:00
parent 654e209e95
commit 027067a122
132 changed files with 61205 additions and 240 deletions
+171 -3
View File
@@ -138,6 +138,9 @@ idAI::idAI
=====================
*/
idAI::idAI() {
nativeAIObject = NULL;
nativeAIObjectName.Clear();
aas = NULL;
travelFlags = TFL_WALK | TFL_AIR;
@@ -244,6 +247,7 @@ idAI::~idAI
=====================
*/
idAI::~idAI() {
ShutdownNativeAIObject();
delete projectileClipModel;
DeconstructScriptObject();
scriptObject.Free();
@@ -548,6 +552,9 @@ void idAI::Restore(idRestoreGame* savefile) {
// Link the script variables back to the scriptobject
LinkScriptVariables();
// Recreate the native state-machine object from the entityDef/spawnArg.
InitNativeAIObject();
if (restorePhysics) {
RestorePhysics(&physicsObj);
}
@@ -793,6 +800,9 @@ void idAI::Spawn(void) {
// init the move variables
StopMove(MOVE_STATUS_DONE);
// create the optional native state-machine object after idAI native fields are ready
InitNativeAIObject();
}
/*
@@ -1074,12 +1084,165 @@ void idAI::LinkScriptVariables(void) {
AI_PUSHED.LinkTo(scriptObject, "AI_PUSHED");
}
/*
=====================
NativeAIObjectNameMatches
=====================
*/
static bool NativeAIObjectNameMatches( const char* requestedName, const char* scriptObjectName, const char* cppObjectName ) {
if ( !requestedName || !requestedName[0] ) {
return false;
}
return ( !idStr::Icmp( requestedName, scriptObjectName ) || !idStr::Icmp( requestedName, cppObjectName ) );
}
#define CREATE_NATIVE_AI_OBJECT( scriptObjectName, cppObjectType ) \
if ( NativeAIObjectNameMatches( nativeObjectName, scriptObjectName, #cppObjectType ) ) { return new cppObjectType(); }
/*
=====================
idAI::CreateNativeAIObject
Creates the native replacement object named by the entityDef/spawnArg "nativeobject" key.
The key may use either the original DoomScript object name, such as "monster_demon_imp",
or the generated native C++ type name, such as "rvmMonsterDemonImp".
=====================
*/
rvmAIObject* idAI::CreateNativeAIObject( const char* nativeObjectName ) {
if ( !nativeObjectName || !nativeObjectName[0] ) {
return NULL;
}
CREATE_NATIVE_AI_OBJECT( "ai", rvmAIObject );
CREATE_NATIVE_AI_OBJECT( "character", rvmCharacter );
CREATE_NATIVE_AI_OBJECT( "ai_character_prone", rvmAICharacterProne );
CREATE_NATIVE_AI_OBJECT( "char_sentry", rvmCharSentry );
CREATE_NATIVE_AI_OBJECT( "follower", rvmFollower );
CREATE_NATIVE_AI_OBJECT( "ai_alphalabs2_scientist1", rvmAIAlphaLabs2Scientist1 );
CREATE_NATIVE_AI_OBJECT( "monster_base", rvmMonsterBase );
CREATE_NATIVE_AI_OBJECT( "monster_zombie_base", rvmMonsterZombieBase );
CREATE_NATIVE_AI_OBJECT( "monster_zombie", rvmMonsterZombie );
CREATE_NATIVE_AI_OBJECT( "monster_zombie_bernie", rvmMonsterZombieBernie );
CREATE_NATIVE_AI_OBJECT( "monster_zombie_morgue", rvmMonsterZombieMorgue );
CREATE_NATIVE_AI_OBJECT( "monster_zombie_sawyer", rvmMonsterZombieSawyer );
CREATE_NATIVE_AI_OBJECT( "monster_zombie_commando_tentacle", rvmMonsterZombieCommandoTentacle );
CREATE_NATIVE_AI_OBJECT( "monster_zombie_commando_cgun", rvmMonsterZombieCommandoCGun );
CREATE_NATIVE_AI_OBJECT( "monster_zombie_security_pistol", rvmMonsterZombieSecurityPistol );
CREATE_NATIVE_AI_OBJECT( "monster_demon_pinky", rvmMonsterDemonPinky );
CREATE_NATIVE_AI_OBJECT( "monster_demon_imp", rvmMonsterDemonImp );
CREATE_NATIVE_AI_OBJECT( "monster_demon_vulgar", rvmMonsterDemonVulgar );
CREATE_NATIVE_AI_OBJECT( "monster_demon_revenant", rvmMonsterDemonRevenant );
CREATE_NATIVE_AI_OBJECT( "monster_demon_hellknight", rvmMonsterDemonHellknight );
CREATE_NATIVE_AI_OBJECT( "monster_demon_maggot", rvmMonsterDemonMaggot );
CREATE_NATIVE_AI_OBJECT( "monster_demon_cherub", rvmMonsterDemonCherub );
CREATE_NATIVE_AI_OBJECT( "monster_demon_trite", rvmMonsterDemonTrite );
CREATE_NATIVE_AI_OBJECT( "monster_demon_wraith", rvmMonsterDemonWraith );
CREATE_NATIVE_AI_OBJECT( "monster_demon_mancubus", rvmMonsterDemonMancubus );
CREATE_NATIVE_AI_OBJECT( "monster_demon_archvile", rvmMonsterDemonArchvile );
CREATE_NATIVE_AI_OBJECT( "monster_flying_cacodemon", rvmMonsterFlyingCacodemon );
CREATE_NATIVE_AI_OBJECT( "monster_flying_lostsoul", rvmMonsterFlyingLostsoul );
CREATE_NATIVE_AI_OBJECT( "monster_turret", rvmMonsterTurret );
CREATE_NATIVE_AI_OBJECT( "monster_boss_cyberdemon", rvmMonsterBossCyberdemon );
CREATE_NATIVE_AI_OBJECT( "monster_boss_vagary", rvmMonsterBossVagary );
CREATE_NATIVE_AI_OBJECT( "monster_boss_sabaoth", rvmMonsterBossSabaoth );
CREATE_NATIVE_AI_OBJECT( "seeker_base", rvmSeekerBase );
CREATE_NATIVE_AI_OBJECT( "monster_boss_guardian_spawner", rvmMonsterBossGuardianSpawner );
CREATE_NATIVE_AI_OBJECT( "monster_boss_guardian", rvmMonsterBossGuardian );
CREATE_NATIVE_AI_OBJECT( "monster_boss_guardian_seeker", rvmMonsterBossGuardianSeeker );
return NULL;
}
#undef CREATE_NATIVE_AI_OBJECT
/*
=====================
idAI::ShutdownNativeAIObject
=====================
*/
void idAI::ShutdownNativeAIObject( void ) {
if ( nativeAIObject ) {
nativeAIObject->Destroy();
delete nativeAIObject;
nativeAIObject = NULL;
}
nativeAIObjectName.Clear();
}
/*
=====================
idAI::InitNativeAIObject
The entityDef owns selection through "nativeobject". Empty, "0", or "false" means
keep the old DoomScript VM path. "1" or "true" means use the existing "scriptobject"
name as the native object selector, which is convenient while converting decls.
=====================
*/
void idAI::InitNativeAIObject( void ) {
ShutdownNativeAIObject();
idStr requestedNativeObject;
spawnArgs.GetString( "nativeobject", "", requestedNativeObject );
if ( !requestedNativeObject.Length() || !idStr::Icmp( requestedNativeObject.c_str(), "0" ) || !idStr::Icmp( requestedNativeObject.c_str(), "false" ) ) {
return;
}
if ( !idStr::Icmp( requestedNativeObject.c_str(), "1" ) || !idStr::Icmp( requestedNativeObject.c_str(), "true" ) ) {
spawnArgs.GetString( "scriptobject", "", requestedNativeObject );
}
if ( !requestedNativeObject.Length() ) {
gameLocal.Warning( "%s has nativeobject set, but no native object or scriptobject name was provided", name.c_str() );
return;
}
nativeAIObject = CreateNativeAIObject( requestedNativeObject.c_str() );
if ( !nativeAIObject ) {
gameLocal.Warning( "%s has unknown nativeobject '%s'; falling back to DoomScript object '%s'", name.c_str(), requestedNativeObject.c_str(), spawnArgs.GetString( "scriptobject", "" ) );
return;
}
nativeAIObjectName = requestedNativeObject;
nativeAIObject->Init( this );
}
/*
=====================
idAI::GetNativeAIObject
=====================
*/
rvmAIObject* idAI::GetNativeAIObject( void ) const {
return nativeAIObject;
}
/*
=====================
idAI::GetNativeAIObjectName
=====================
*/
const char* idAI::GetNativeAIObjectName( void ) const {
return nativeAIObjectName.c_str();
}
/*
=====================
idAI::UpdateAIScript
=====================
*/
void idAI::UpdateAIScript(void) {
if (nativeAIObject) {
nativeAIObject->Update();
// clear the hit enemy flag so we catch the next time we hit someone
AI_HIT_ENEMY = false;
return;
}
UpdateScript();
// clear the hit enemy flag so we catch the next time we hit someone
@@ -3338,9 +3501,14 @@ void idAI::Killed(idEntity* inflictor, idEntity* attacker, int damage, const idV
restartParticles = false;
state = GetScriptFunction("state_Killed");
SetState(state);
SetWaitState("");
if (nativeAIObject) {
nativeAIObject->SetState("state_Killed");
}
else {
state = GetScriptFunction("state_Killed");
SetState(state);
SetWaitState("");
}
const idKeyValue* kv = spawnArgs.MatchPrefix("def_drops", NULL);
while (kv) {