diff --git a/extras/acs/srb2special.acs b/extras/acs/srb2special.acs index 1fb3056c0..fb7f096e8 100644 --- a/extras/acs/srb2special.acs +++ b/extras/acs/srb2special.acs @@ -26,6 +26,7 @@ special -211:SetActorFlag(2,3), -212:SetActorClass(2), -213:SetActorDye(2), + -214:SpawnForced(4,6), -300:CountEnemies(2), -301:CountPushables(2), -302:HasUnlockable(1), diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 04b9fd015..8005bae51 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -577,6 +577,27 @@ static UINT32 ACS_SectorTagThingCounter(mtag_t sectorTag, sector_t *activator, m return count; } +/*-------------------------------------------------- + static bool ACS_IsLocationValid(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z) + + Helper function for CallFunc_SpawnObject and CallFunc_SetObjectPosition. + Checks if the given coordinates are valid for an actor to exist in. +--------------------------------------------------*/ +static bool ACS_IsLocationValid(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z) +{ + if (P_CheckPosition(mobj, x, y) == true) + { + fixed_t floorz = P_FloorzAtPos(x, y, z, mobj->height); + fixed_t ceilingz = P_CeilingzAtPos(x, y, z, mobj->height); + if (z >= floorz && (z + mobj->height) <= ceilingz) + { + return true; + } + } + + return false; +} + /*-------------------------------------------------- bool CallFunc_Random(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) @@ -1791,6 +1812,42 @@ bool CallFunc_SetViewpoint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM return false; } +/*-------------------------------------------------- + static bool ACS_SpawnObject(player_t *player, INT32 property, const ACSVM::Word *argV, ACSVM::Word argC) + + Helper function for CallFunc_SpawnObject and CallFunc_SpawnObjectForced. +--------------------------------------------------*/ +static bool ACS_SpawnObject(mobjtype_t mobjType, bool forceSpawn, const ACSVM::Word *argV, ACSVM::Word argC) +{ + fixed_t x = argV[1]; + fixed_t y = argV[2]; + fixed_t z = argV[3]; + + mobj_t *mobj = P_SpawnMobj(x, y, z, mobjType); + + if (P_MobjWasRemoved(mobj)) + { + return false; + } + else if (!forceSpawn && ACS_IsLocationValid(mobj, x, y, z) == false) + { + P_RemoveMobj(mobj); + + return false; + } + else + { + // Spawned successfully + if (argC >= 5) + P_SetThingTID(mobj, argV[4]); + + if (argC >= 6) + mobj->angle = ACS_FixedToAngle(argV[5]); + } + + return true; +} + /*-------------------------------------------------- bool CallFunc_SpawnObject(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) @@ -1803,7 +1860,7 @@ bool CallFunc_SpawnObject(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM: { CONS_Alert(CONS_WARNING, "Spawn actor class was not provided.\n"); - NO_RETURN(thread); + thread->dataStk.push(0); return false; } @@ -1812,6 +1869,8 @@ bool CallFunc_SpawnObject(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM: mobjtype_t mobjType = MT_NULL; + int numSpawned = 0; + if (ACS_GetMobjTypeFromString(className, &mobjType) == false) { CONS_Alert(CONS_WARNING, @@ -1821,29 +1880,52 @@ bool CallFunc_SpawnObject(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM: } else { - fixed_t x = argV[1]; - fixed_t y = argV[2]; - fixed_t z = argV[3]; - - mobj_t *mobj = P_SpawnMobj(x, y, z, mobjType); - if (!P_MobjWasRemoved(mobj)) - { - if (argC >= 5) - P_SetThingTID(mobj, argV[4]); - - if (argC >= 6) - mobj->angle = ACS_FixedToAngle(argV[5]); - } - else - { - CONS_Alert(CONS_WARNING, - "Spawn: Couldn't spawn actor class \"%s\".\n", - className - ); - } + if (ACS_SpawnObject(mobjType, false, argV, argC) == true) + numSpawned = 1; } - NO_RETURN(thread); + thread->dataStk.push(numSpawned); + + return false; +} + +/*-------------------------------------------------- + bool CallFunc_SpawnObjectForced(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Spawns an actor, even in locations where it would not normally be able to exist. +--------------------------------------------------*/ +bool CallFunc_SpawnObjectForced(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + ACSVM::String *str = thread->scopeMap->getString( argV[0] ); + if (!str->str || str->len == 0) + { + CONS_Alert(CONS_WARNING, "SpawnForced actor class was not provided.\n"); + + thread->dataStk.push(0); + + return false; + } + + const char *className = str->str; + + mobjtype_t mobjType = MT_NULL; + + int numSpawned = 0; + + if (ACS_GetMobjTypeFromString(className, &mobjType) == false) + { + CONS_Alert(CONS_WARNING, + "Couldn't find actor class \"%s\" for SpawnForced.\n", + className + ); + } + else + { + if (ACS_SpawnObject(mobjType, true, argV, argC) == true) + numSpawned = 1; + } + + thread->dataStk.push(numSpawned); return false; } @@ -2569,11 +2651,9 @@ bool CallFunc_SetObjectPosition(ACSVM::Thread *thread, const ACSVM::Word *argV, fixed_t y = argV[2]; fixed_t z = argV[3]; - if (mobj != NULL && P_MobjWasRemoved(mobj) == false && P_CheckPosition(mobj, x, y) == true) + if (mobj != NULL && P_MobjWasRemoved(mobj) == false) { - fixed_t floorz = P_FloorzAtPos(x, y, z, mobj->height); - fixed_t ceilingz = P_CeilingzAtPos(x, y, z, mobj->height); - if (z >= floorz && (z + mobj->height) <= ceilingz) + if (ACS_IsLocationValid(mobj, x, y, z)) { P_SetOrigin(mobj, x, y, z); success = true; @@ -4516,7 +4596,7 @@ bool CallFunc_CheckPowerUp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM /*-------------------------------------------------- static void ACS_SetPowerUp(player_t *player, INT32 property, const ACSVM::Word *argV, ACSVM::Word argC) - Helper for CallFunc_GivePowerUp. + Helper function for CallFunc_GivePowerUp. --------------------------------------------------*/ static void ACS_SetPowerUp(player_t *player, INT32 property, const ACSVM::Word *argV, ACSVM::Word argC) { @@ -4674,7 +4754,7 @@ bool CallFunc_CheckAmmo(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::W /*-------------------------------------------------- static void ACS_GiveAmmo(player_t *player, INT32 weapon, INT32 value) - Helper for CallFunc_GiveAmmo/CallFunc_TakeAmmo. + Helper function for CallFunc_GiveAmmo/CallFunc_TakeAmmo. --------------------------------------------------*/ static bool ACS_GiveAmmo(player_t *player, INT32 weapon, INT32 value) { @@ -5200,6 +5280,58 @@ bool CallFunc_PlayerFinished(ACSVM::Thread *thread, const ACSVM::Word *argV, ACS return false; } +/*-------------------------------------------------- + bool CallFunc_SpawnProjectile(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Spawns a projectile. Yeah... +--------------------------------------------------*/ +bool CallFunc_SpawnProjectile(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + (void)argC; + + auto info = &static_cast(thread)->info; + + mobj_t *mobj = P_FindMobjFromTID(argV[0], NULL, info->mo); + if (mobj != NULL && P_MobjWasRemoved(mobj) == false) + { + ACSVM::String *str = thread->scopeMap->getString( argV[1] ); + if (!str->str || str->len == 0) + { + CONS_Alert(CONS_WARNING, "SpawnProjectile projectile class was not provided.\n"); + + NO_RETURN(thread); + + return false; + } + + const char *className = str->str; + + mobjtype_t mobjType = MT_NULL; + + if (ACS_GetMobjTypeFromString(className, &mobjType) == false) + { + CONS_Alert(CONS_WARNING, + "Couldn't find actor class \"%s\" for SpawnProjectile.\n", + className + ); + + NO_RETURN(thread); + + return false; + } + + mobj_t *missile = P_SpawnMissileAtSpeeds(mobj, mobjType, ACS_FixedToAngle(argV[2]), argV[3], argV[4], argV[5] != 0); + if (missile && argV[6] != 0) + { + P_SetThingTID(mobj, argV[6]); + } + } + + NO_RETURN(thread); + + return false; +} + /*-------------------------------------------------- bool CallFunc_Sin(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index 2aa9af756..665b6e547 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -81,6 +81,7 @@ bool CallFunc_PlayerExiting(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSV bool CallFunc_Teleport(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_SetViewpoint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_SpawnObject(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_SpawnObjectForced(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_TrackObjectAngle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_StopTrackingObjectAngle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_PlayerEmeralds(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); @@ -165,6 +166,8 @@ bool CallFunc_PlayerHoldingFlag(ACSVM::Thread *thread, const ACSVM::Word *argV, bool CallFunc_PlayerIsIt(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_PlayerFinished(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_SpawnProjectile(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); + bool CallFunc_Sin(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_Cos(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); bool CallFunc_Tan(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 4cb29333f..fbec87cc2 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -134,6 +134,7 @@ Environment::Environment() addCodeDataACS0(270, {"", 0, addCallFunc(CallFunc_EndLog)}); // 273 to 275: Implemented by ACSVM addCodeDataACS0(276, {"", 2, addCallFunc(CallFunc_SetObjectAngle)}); + addCodeDataACS0(280, {"", 7, addCallFunc(CallFunc_SpawnProjectile)}); addCodeDataACS0(282, {"", 1, addCallFunc(CallFunc_GetObjectCeilingZ)}); // 291 to 325: Implemented by ACSVM @@ -189,6 +190,7 @@ Environment::Environment() addFuncDataACS0( 211, addCallFunc(CallFunc_SetObjectFlag)); addFuncDataACS0( 212, addCallFunc(CallFunc_SetObjectClass)); addFuncDataACS0( 213, addCallFunc(CallFunc_SetObjectDye)); + addFuncDataACS0( 214, addCallFunc(CallFunc_SpawnObjectForced)); addFuncDataACS0( 300, addCallFunc(CallFunc_CountEnemies)); addFuncDataACS0( 301, addCallFunc(CallFunc_CountPushables)); diff --git a/src/p_local.h b/src/p_local.h index 698867e33..a0c537de2 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -342,6 +342,7 @@ mobj_t *P_SpawnXYZMissile(mobj_t *source, mobj_t *dest, mobjtype_t type, fixed_t mobj_t *P_SpawnPointMissile(mobj_t *source, fixed_t xa, fixed_t ya, fixed_t za, mobjtype_t type, fixed_t x, fixed_t y, fixed_t z); mobj_t *P_SpawnAlteredDirectionMissile(mobj_t *source, mobjtype_t type, fixed_t x, fixed_t y, fixed_t z, INT32 shiftingAngle); mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 aimtype, UINT32 flags2); +mobj_t *P_SpawnMissileAtSpeeds(mobj_t *source, mobjtype_t type, angle_t angle, fixed_t hspeed, fixed_t vspeed, boolean useGravity); #define P_SpawnPlayerMissile(s,t,f) P_SPMAngle(s,t,s->angle,true,f) #define P_SpawnNameFinder(s,t) P_SPMAngle(s,t,s->angle,true,0) void P_ColorTeamMissile(mobj_t *missile, player_t *source); diff --git a/src/p_mobj.c b/src/p_mobj.c index df3ae2586..86dfeacdb 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -14369,6 +14369,45 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai return slope ? th : NULL; } +// +// P_SpawnMissileAtSpeeds +// Fires missile at angle and X/Y speeds +// +mobj_t *P_SpawnMissileAtSpeeds(mobj_t *source, mobjtype_t type, angle_t angle, fixed_t hspeed, fixed_t vspeed, boolean useGravity) +{ + fixed_t x = source->x; + fixed_t y = source->y; + fixed_t z; + + if (source->eflags & MFE_VERTICALFLIP) + z = source->z + 2*source->height/3 - FixedMul(mobjinfo[type].height, source->scale); + else + z = source->z + source->height/3; + + mobj_t *th = P_SpawnMobj(x, y, z, type); + if (P_MobjWasRemoved(th)) + return NULL; + + if (source->eflags & MFE_VERTICALFLIP) + th->flags2 |= MF2_OBJECTFLIP; + + P_SetScale(th, source->scale, true); + P_SetTarget(&th->target, source); + + th->angle = angle; + th->momx = FixedMul(FixedMul(hspeed, FINECOSINE(angle>>ANGLETOFINESHIFT)), th->scale); + th->momy = FixedMul(FixedMul(hspeed, FINESINE(angle>>ANGLETOFINESHIFT)), th->scale); + th->momz = FixedMul(vspeed, th->scale); + + if (useGravity) + th->flags &= ~MF_NOGRAVITY; + + if (P_CheckMissileSpawn(th)) + return th; + + return NULL; +} + // // P_FlashPal // Flashes a player's palette. ARMAGEDDON BLASTS!