Change behavior of Spawn to match ZDoom

Add SpawnForced and SpawnProjectile
This commit is contained in:
Lactozilla 2024-04-29 23:37:52 -03:00
parent 6b4d193fdf
commit 985a0be20a
6 changed files with 206 additions and 28 deletions

View file

@ -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),

View file

@ -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 *>(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)

View file

@ -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);

View file

@ -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));

View file

@ -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);

View file

@ -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!