- Duke: made gamevars type safe and capable of managing actor pointers.

Also a few unrelated changes needed to make it compile again.
This commit is contained in:
Christoph Oelckers 2021-12-05 17:36:57 +01:00
parent d088ab05a9
commit 8606045689
13 changed files with 365 additions and 223 deletions

View file

@ -1003,7 +1003,7 @@ static void movetripbomb(DDukeActor *actor)
{
auto s = actor->s;
int j, x;
int lTripBombControl = GetGameVar("TRIPBOMB_CONTROL", TRIPBOMB_TRIPWIRE, nullptr, -1);
int lTripBombControl = GetGameVar("TRIPBOMB_CONTROL", TRIPBOMB_TRIPWIRE, nullptr, -1).safeValue();
if (lTripBombControl & TRIPBOMB_TIMER)
{
// we're on a timer....

View file

@ -248,9 +248,9 @@ static bool cheatInventory(int player)
{
SetGameVarID(g_iReturnVarID, defvalue, nullptr, player);
OnEvent(evtype, player, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, player) >= 0)
if (GetGameVarID(g_iReturnVarID, nullptr, player).safeValue() >= 0)
{
dest = GetGameVarID(g_iReturnVarID, nullptr, player);
dest = GetGameVarID(g_iReturnVarID, nullptr, player).value();
}
};

View file

@ -19,19 +19,6 @@ inline int player_struct::GetPlayerNum()
return actor->s->yvel;
}
inline int ActorToScriptIndex(DDukeActor* a)
{
if (!a) return -1;
return a->GetSpriteIndex();
}
inline DDukeActor* ScriptIndexToActor(int index)
{
// only allow valid actors to get through here. Everything else gets null'ed.
if (index < 0 || index >= MAXSPRITES || hittype[index].s->statnum == MAXSTATUS) return nullptr;
return &hittype[index];
}
DDukeActor* spawn(DDukeActor* spawner, int type);
inline int ldist(DDukeActor* s1, DDukeActor* s2)

View file

@ -95,11 +95,30 @@ void addspritetodelete(int spnum)
killthesprite = true;
}
sectortype* toSect(int index)
{
return validSectorIndex(index) ? &sector[index] : nullptr;
}
int fromSect(sectortype* sect)
{
return sect ? sectnum(sect) : -1;
}
walltype* toWall(int index)
{
return validWallIndex(index) ? &wall[index] : nullptr;
}
int fromWall(walltype* sect)
{
return sect ? wallnum(sect) : -1;
}
static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, int sPlayer, int lParm2)
{
int lValue;
lValue = GetGameVarID(lVar2, sActor, sPlayer);
auto vValue = GetGameVarID(lVar2, sActor, sPlayer);
auto lValue = vValue.safeValue();
// most settings have been removed because they are either pointless, no longer existent or simply too dangerous to access.
// Others have been made read-only.
@ -131,8 +150,8 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
break;
case USERDEFS_CAMERASPRITE:
if (bSet) ud.cameraactor = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ud.cameraactor), sActor, sPlayer);
if (bSet) ud.cameraactor = vValue.safeActor();
else SetGameVarID(lVar2, ud.cameraactor, sActor, sPlayer);
break;
case USERDEFS_LAST_CAMSPRITE:
@ -251,11 +270,11 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
///////////////////////////////////////////
void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, int sPlayer, int lParm2)
{
int iPlayer;
int lValue;
int lTemp;
auto vValue = GetGameVarID(lVar2, sActor, sPlayer);
auto lValue = vValue.safeValue();
lValue = GetGameVarID(lVar2, sActor, sPlayer);
int iPlayer;
int lTemp;
if (lVar1 == g_iThisActorID)
{
@ -264,7 +283,7 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
}
else
{
iPlayer = GetGameVarID(lVar1, sActor, sPlayer);
iPlayer = GetGameVarID(lVar1, sActor, sPlayer).safeValue();
}
if (iPlayer < 0 || iPlayer >= MAXPLAYERS)
@ -465,6 +484,11 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
if (!bSet) SetGameVarID(lVar2, 0, sActor, sPlayer);
break;
case PLAYER_CURSECTNUM:
if (bSet) ps[iPlayer].cursector = toSect(lValue);
else SetGameVarID(lVar2, fromSect(ps[iPlayer].cursector), sActor, sPlayer);
break;
case PLAYER_LOOK_ANG:
if (bSet) ps[iPlayer].angle.look_ang = buildang(lValue);
else SetGameVarID(lVar2, ps[iPlayer].angle.look_ang.asbuild(), sActor, sPlayer);
@ -481,14 +505,14 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
break;
case PLAYER_AMMO_AMOUNT:
lTemp = GetGameVarID(lParm2, sActor, sPlayer);
lTemp = GetGameVarID(lParm2, sActor, sPlayer).safeValue();
if (bSet) ps[iPlayer].ammo_amount[lTemp] = lValue;
else SetGameVarID(lVar2, ps[iPlayer].ammo_amount[lTemp], sActor, sPlayer);
break;
case PLAYER_WACKEDBYACTOR:
if (bSet) ps[iPlayer].wackedbyactor = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ps[iPlayer].wackedbyactor), sActor, sPlayer);
if (bSet) ps[iPlayer].wackedbyactor = vValue.safeActor();
else SetGameVarID(lVar2, ps[iPlayer].wackedbyactor, sActor, sPlayer);
break;
case PLAYER_FRAG:
@ -527,8 +551,8 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
break;
case PLAYER_NEWOWNER:
if (bSet) ps[iPlayer].newOwner = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ps[iPlayer].newOwner), sActor, sPlayer);
if (bSet) ps[iPlayer].newOwner = vValue.safeActor();
else SetGameVarID(lVar2, ps[iPlayer].newOwner, sActor, sPlayer);
break;
case PLAYER_HURT_DELAY:
@ -561,6 +585,16 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
else SetGameVarID(lVar2, ps[iPlayer].access_incs, sActor, sPlayer);
break;
case PLAYER_ACCESS_WALLNUM:
if (bSet) ps[iPlayer].access_wall = toWall(lValue);
else SetGameVarID(lVar2, fromWall(ps[iPlayer].access_wall), sActor, sPlayer);
break;
case PLAYER_ACCESS_SPRITENUM:
if (bSet) ps[iPlayer].access_spritenum = vValue.safeActor();
else SetGameVarID(lVar2, ps[iPlayer].access_spritenum, sActor, sPlayer);
break;
case PLAYER_KICKBACK_PIC:
if (bSet) ps[iPlayer].okickback_pic = ps[iPlayer].kickback_pic = lValue;
else SetGameVarID(lVar2, ps[iPlayer].kickback_pic, sActor, sPlayer);
@ -582,13 +616,22 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
break;
case PLAYER_SOMETHINGONPLAYER:
if (bSet) ps[iPlayer].somethingonplayer = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ps[iPlayer].somethingonplayer), sActor, sPlayer);
if (bSet) ps[iPlayer].somethingonplayer = vValue.safeActor();
else SetGameVarID(lVar2, (ps[iPlayer].somethingonplayer), sActor, sPlayer);
break;
case PLAYER_ON_CRANE:
if (bSet) ps[iPlayer].on_crane = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ps[iPlayer].on_crane), sActor, sPlayer);
if (bSet) ps[iPlayer].on_crane = vValue.safeActor();
else SetGameVarID(lVar2, (ps[iPlayer].on_crane), sActor, sPlayer);
break;
case PLAYER_I: // Read only, because this is very dangerous.
if (!bSet) SetGameVarID(lVar2, ps[iPlayer].actor, sActor, sPlayer);
break;
case PLAYER_ONE_PARALLAX_SECTNUM:
if (bSet) ps[iPlayer].one_parallax_sectnum = toSect(lValue);
else SetGameVarID(lVar2, fromSect(ps[iPlayer].one_parallax_sectnum), sActor, sPlayer);
break;
case PLAYER_OVER_SHOULDER_ON:
@ -617,8 +660,8 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
break;
case PLAYER_DUMMYPLAYERSPRITE:
if (bSet) ps[iPlayer].dummyplayersprite = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ps[iPlayer].dummyplayersprite), sActor, sPlayer);
if (bSet) ps[iPlayer].dummyplayersprite = vValue.safeActor();
else SetGameVarID(lVar2, (ps[iPlayer].dummyplayersprite), sActor, sPlayer);
break;
case PLAYER_EXTRA_EXTRA8:
@ -637,8 +680,8 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
break;
case PLAYER_ACTORSQU:
if (bSet) ps[iPlayer].actorsqu = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ps[iPlayer].actorsqu), sActor, sPlayer);
if (bSet) ps[iPlayer].actorsqu = vValue.safeActor();
else SetGameVarID(lVar2, (ps[iPlayer].actorsqu), sActor, sPlayer);
break;
case PLAYER_TIMEBEFOREEXIT:
@ -706,8 +749,8 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
break;
case PLAYER_HOLODUKE_ON:
if (bSet) ps[iPlayer].holoduke_on = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ps[iPlayer].holoduke_on), sActor, sPlayer);
if (bSet) ps[iPlayer].holoduke_on = vValue.safeActor();
else SetGameVarID(lVar2, (ps[iPlayer].holoduke_on), sActor, sPlayer);
break;
case PLAYER_PYCOUNT:
@ -906,14 +949,11 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
////////////////////
void DoWall(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, int sPlayer, int lParm2)
{
int iWall;
int lValue;
auto lValue = GetGameVarID(lVar2, sActor, sPlayer).safeValue();
auto vWall = GetGameVarID(lVar1, sActor, sPlayer);
auto iWall = vWall.safeValue();
lValue = GetGameVarID(lVar2, sActor, sPlayer);
iWall = GetGameVarID(lVar1, sActor, sPlayer);
if (iWall < 0 || iWall >= numwalls)
if (iWall < 0 || iWall >= numwalls || vWall.isActor())
{
if (!bSet) SetGameVarID(lVar2, 0, sActor, sPlayer);
return;
@ -996,6 +1036,7 @@ void DoSector(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
{
int iSector;
int lValue;
bool no = false;
if (lVar1 == g_iThisActorID)
{
@ -1004,16 +1045,18 @@ void DoSector(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
}
else
{
iSector = GetGameVarID(lVar1, sActor, sPlayer);
auto vv = GetGameVarID(lVar1, sActor, sPlayer);
no = vv.isActor();
iSector = vv.safeValue();
}
if (iSector < 0 || iSector >= numsectors)
if (iSector < 0 || iSector >= numsectors || no)
{
if (!bSet) SetGameVarID(lVar2, 0, sActor, sPlayer);
return;
}
lValue = GetGameVarID(lVar2, sActor, sPlayer);
lValue = GetGameVarID(lVar2, sActor, sPlayer).safeValue();
auto sectp = &sector[iSector];
// All fields affecting map geometry have been made read-only!
@ -1111,28 +1154,25 @@ void DoSector(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
}
return;
}
void DoActor(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor, int sPlayer, int lParm2)
{
int iActor;
int lValue;
lValue = GetGameVarID(lVar2, sActor, sPlayer);
auto vValue = GetGameVarID(lVar2, sActor, sPlayer);
auto lValue = vValue.safeValue();
DDukeActor* act;
if (lVar1 == g_iThisActorID)
{
// if they've asked for 'this', then use 'this'...
act = sActor;
iActor = ActorToScriptIndex(sActor);
}
else
{
iActor = GetGameVarID(lVar1, sActor, sPlayer);
act = ScriptIndexToActor(iActor);
act = GetGameVarID(lVar1, sActor, sPlayer).safeActor();
}
auto spr = act->s;
if (iActor < 0 || iActor >= MAXSPRITES || spr->statnum == MAXSTATUS)
if (!act)
{
if (!bSet) SetGameVarID(lVar2, 0, sActor, sPlayer);
return;
@ -1192,11 +1232,20 @@ void DoActor(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
if (bSet) spr->yoffset = lValue;
else SetGameVarID(lVar2, spr->yoffset, sActor, sPlayer);
break;
case ACTOR_SECTNUM: // made read only because this is not safe.
if (!bSet) /*changespritesect(iActor, lValue);
else*/ SetGameVarID(lVar2, spr->sectnum, sActor, sPlayer);
break;
case ACTOR_STATNUM:
if (!bSet) /*changespritestat(iActor, lValue);
else*/ SetGameVarID(lVar2, spr->statnum, sActor, sPlayer);
break;
case ACTOR_ANG:
if (bSet) spr->ang = lValue;
else SetGameVarID(lVar2, spr->ang, sActor, sPlayer);
break;
case ACTOR_OWNER:
// there is no way to handle this well because we do not know whether this is an actor or not. Pity.
if (bSet) spr->owner = lValue;
else SetGameVarID(lVar2, spr->owner, sActor, sPlayer);
break;
@ -1241,6 +1290,10 @@ void DoActor(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
if (bSet) act->extra = lValue;
else SetGameVarID(lVar2, act->extra, sActor, sPlayer);
break;
case ACTOR_HTOWNER:
if (bSet) act->hitOwnerActor = vValue.safeActor();
else SetGameVarID(lVar2, act->hitOwnerActor, sActor, sPlayer);
break;
case ACTOR_HTMOVFLAG:
if (bSet) act->movflag = lValue;
else SetGameVarID(lVar2, act->movflag, sActor, sPlayer);
@ -1249,6 +1302,10 @@ void DoActor(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* sActor,
if (bSet) act->tempang = lValue;
else SetGameVarID(lVar2, act->tempang, sActor, sPlayer);
break;
case ACTOR_HTACTORSTAYPUT:
if (bSet) act->actorstayput = toSect(lValue);
else SetGameVarID(lVar2, fromSect(act->actorstayput), sActor, sPlayer);
break;
case ACTOR_HTDISPICNUM:
if (bSet) act->dispicnum = lValue;
else SetGameVarID(lVar2, act->dispicnum, sActor, sPlayer);
@ -2590,7 +2647,7 @@ int ParseState::parse(void)
{ int i;
insptr++;
i=*(insptr++); // ID of def
SetGameVarID(i, GetGameVarID(i, g_ac, g_p) + *insptr, g_ac, g_p );
SetGameVarID(i, GetGameVarID(i, g_ac, g_p).safeValue() + *insptr, g_ac, g_p );
insptr++;
break;
}
@ -2599,7 +2656,7 @@ int ParseState::parse(void)
{ int i;
insptr++;
i=*(insptr++); // ID of def
SetGameVarID(i, GetGameVarID(i, g_ac, g_p) + GetGameVarID(*insptr, g_ac, g_p), g_ac, g_p );
SetGameVarID(i, GetGameVarID(i, g_ac, g_p).safeValue() + GetGameVarID(*insptr, g_ac, g_p).safeValue(), g_ac, g_p );
insptr++;
break;
}
@ -2609,7 +2666,7 @@ int ParseState::parse(void)
insptr++;
i=*(insptr++); // ID of def
j=0;
if(GetGameVarID(i, g_ac, g_p) == GetGameVarID(*(insptr), g_ac, g_p) )
if(GetGameVarID(i, g_ac, g_p).safeValue() == GetGameVarID(*(insptr), g_ac, g_p).safeValue())
{
j=1;
}
@ -2622,7 +2679,7 @@ int ParseState::parse(void)
insptr++;
i=*(insptr++); // ID of def
j=0;
if(GetGameVarID(i, g_ac, g_p) > GetGameVarID(*(insptr), g_ac, g_p) )
if(GetGameVarID(i, g_ac, g_p).safeValue() > GetGameVarID(*(insptr), g_ac, g_p).safeValue())
{
j=1;
}
@ -2635,7 +2692,7 @@ int ParseState::parse(void)
insptr++;
i=*(insptr++); // ID of def
j=0;
if(GetGameVarID(i, g_ac, g_p) < GetGameVarID(*(insptr), g_ac, g_p) )
if(GetGameVarID(i, g_ac, g_p).safeValue() < GetGameVarID(*(insptr), g_ac, g_p).safeValue())
{
j=1;
}
@ -2648,7 +2705,7 @@ int ParseState::parse(void)
insptr++;
i=*(insptr++); // ID of def
j=0;
if(GetGameVarID(i, g_ac, g_p) == *insptr)
if(GetGameVarID(i, g_ac, g_p).safeValue() == *insptr)
{
j=1;
}
@ -2661,7 +2718,7 @@ int ParseState::parse(void)
insptr++;
i=*(insptr++); // ID of def
j=0;
if(GetGameVarID(i, g_ac, g_p) > *insptr)
if(GetGameVarID(i, g_ac, g_p).safeValue() > *insptr)
{
j=1;
}
@ -2674,7 +2731,7 @@ int ParseState::parse(void)
insptr++;
i=*(insptr++); // ID of def
j=0;
if(GetGameVarID(i, g_ac, g_p) < *insptr)
if(GetGameVarID(i, g_ac, g_p).safeValue() < *insptr)
{
j=1;
}
@ -2835,11 +2892,11 @@ int ParseState::parse(void)
insptr++;
lIn = *insptr++;
lIn = GetGameVarID(lIn, g_ac, g_p);
lIn = GetGameVarID(lIn, g_ac, g_p).safeValue();
if(g_ac->insector())
lReturn = spawn(g_ac, lIn);
SetGameVarID(g_iReturnVarID, ActorToScriptIndex(lReturn), g_ac, g_p);
SetGameVarID(g_iReturnVarID, (lReturn), g_ac, g_p);
break;
}
case concmd_espawn:
@ -2849,7 +2906,7 @@ int ParseState::parse(void)
if(g_ac->insector())
lReturn = spawn(g_ac, *insptr);
insptr++;
SetGameVarID(g_iReturnVarID, ActorToScriptIndex(lReturn), g_ac, g_p);
SetGameVarID(g_iReturnVarID, (lReturn), g_ac, g_p);
break;
}
case concmd_setsector:
@ -2881,7 +2938,7 @@ int ParseState::parse(void)
insptr++;
lInVarID = *(insptr++);
lOutVarID = *(insptr++);
lIn = GetGameVarID(lInVarID, g_ac, g_p);
lIn = GetGameVarID(lInVarID, g_ac, g_p).safeValue();
SetGameVarID(lOutVarID, ksqrt(lIn), g_ac, g_p);
break;
}
@ -2923,7 +2980,7 @@ int ParseState::parse(void)
}
}
SetGameVarID(lVarID, ActorToScriptIndex(lFound), g_ac, g_p);
SetGameVarID(lVarID, (lFound), g_ac, g_p);
break;
}
@ -2946,7 +3003,7 @@ int ParseState::parse(void)
lType = *(insptr++);
lMaxDistVar = *(insptr++);
lVarID = *(insptr++);
lMaxDist = GetGameVarID(lMaxDistVar, g_ac, g_p);
lMaxDist = GetGameVarID(lMaxDistVar, g_ac, g_p).safeValue();
DDukeActor* lFound = nullptr;
lDist = 32767; // big number
@ -2966,7 +3023,7 @@ int ParseState::parse(void)
}
}
SetGameVarID(lVarID, ActorToScriptIndex(lFound), g_ac, g_p);
SetGameVarID(lVarID, (lFound), g_ac, g_p);
break;
}
@ -3033,7 +3090,6 @@ int ParseState::parse(void)
// gets the value of the per-actor variable varx into VAR
// <var> <varx> <VAR>
int lVar1, lVar2, lVar3;
int lTemp, lSprite;
insptr++;
@ -3041,11 +3097,11 @@ int ParseState::parse(void)
lVar2 = *(insptr++);
lVar3 = *(insptr++);
lSprite = GetGameVarID(lVar1, g_ac, g_p);
if (lSprite >= 0)
auto lSprite = GetGameVarID(lVar1, g_ac, g_p);
if (lSprite.isActor())
{
lTemp = GetGameVarID(lVar3, g_ac, g_p);
SetGameVarID(lVar2, lTemp, ScriptIndexToActor(lSprite), g_p);
auto lTemp = GetGameVarID(lVar3, g_ac, g_p);
SetGameVarID(lVar2, lTemp, lSprite.actor(), g_p);
}
break;
@ -3056,7 +3112,6 @@ int ParseState::parse(void)
// gets the value of the per-actor variable varx into VAR
// <var> <varx> <VAR>
int lVar1, lVar2, lVar3;
int lTemp, lSprite;
insptr++;
@ -3064,10 +3119,10 @@ int ParseState::parse(void)
lVar2 = *(insptr++);
lVar3 = *(insptr++);
lSprite = GetGameVarID(lVar1, g_ac, g_p);
if (lSprite >= 0)
auto lSprite = GetGameVarID(lVar1, g_ac, g_p);
if (lSprite.isActor())
{
lTemp = GetGameVarID(lVar2, ScriptIndexToActor(lSprite), g_p);
auto lTemp = GetGameVarID(lVar2, lSprite.actor(), g_p);
SetGameVarID(lVar3, lTemp, g_ac, g_p);
}
@ -3110,7 +3165,7 @@ int ParseState::parse(void)
int i;
insptr++;
i = *(insptr++); // ID of def
ps[g_p].transporter_hold = GetGameVarID(i, g_ac, g_p);
ps[g_p].transporter_hold = GetGameVarID(i, g_ac, g_p).safeValue();
break;
}
case concmd_getplayerangle:
@ -3126,7 +3181,7 @@ int ParseState::parse(void)
int i;
insptr++;
i = *(insptr++); // ID of def
ps[g_p].angle.ang = buildang(GetGameVarID(i, g_ac, g_p) & 2047);
ps[g_p].angle.ang = buildang(GetGameVarID(i, g_ac, g_p).safeValue() & 2047);
break;
}
case concmd_getactorangle:
@ -3142,7 +3197,7 @@ int ParseState::parse(void)
int i;
insptr++;
i = *(insptr++); // ID of def
g_sp->ang = GetGameVarID(i, g_ac, g_p);
g_sp->ang = GetGameVarID(i, g_ac, g_p).safeValue();
g_sp->ang &= 2047;
break;
}
@ -3160,7 +3215,7 @@ int ParseState::parse(void)
int i;
insptr++;
i = *(insptr++); // ID of def
SetGameVarID(i, GetGameVarID(i, g_ac, g_p) * (*insptr), g_ac, g_p);
SetGameVarID(i, GetGameVarID(i, g_ac, g_p).safeValue()* (*insptr), g_ac, g_p);
insptr++;
break;
}
@ -3173,7 +3228,7 @@ int ParseState::parse(void)
{
I_Error("Divide by Zero in CON.");
}
SetGameVarID(i, GetGameVarID(i, g_ac, g_p) / (*insptr), g_ac, g_p);
SetGameVarID(i, GetGameVarID(i, g_ac, g_p).safeValue() / (*insptr), g_ac, g_p);
insptr++;
break;
}
@ -3189,7 +3244,7 @@ int ParseState::parse(void)
{
I_Error("Divide by Zero in CON");
}
lResult = GetGameVarID(i, g_ac, g_p) % l;
lResult = GetGameVarID(i, g_ac, g_p).safeValue() % l;
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
break;
@ -3202,7 +3257,7 @@ int ParseState::parse(void)
insptr++;
i = *(insptr++); // ID of def
l = (*insptr);
lResult = GetGameVarID(i, g_ac, g_p) & l;
lResult = GetGameVarID(i, g_ac, g_p).safeValue() & l;
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
break;
@ -3215,7 +3270,7 @@ int ParseState::parse(void)
insptr++;
i = *(insptr++); // ID of def
l = (*insptr);
lResult = GetGameVarID(i, g_ac, g_p) ^ l;
lResult = GetGameVarID(i, g_ac, g_p).safeValue() ^ l;
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
break;
@ -3228,7 +3283,7 @@ int ParseState::parse(void)
insptr++;
i = *(insptr++); // ID of def
l = (*insptr);
lResult = GetGameVarID(i, g_ac, g_p) | l;
lResult = GetGameVarID(i, g_ac, g_p).safeValue() | l;
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
break;
@ -3240,8 +3295,8 @@ int ParseState::parse(void)
int lResult;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p); // not used for this command
l2 = GetGameVarID(*insptr, g_ac, g_p);
l1 = GetGameVarID(i, g_ac, g_p).safeValue(); // not used for this command
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue();
lResult = MulScale(rand(), l2, 15);
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
@ -3254,8 +3309,8 @@ int ParseState::parse(void)
int lResult;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p);
l2 = GetGameVarID(*insptr, g_ac, g_p); // l2 not used in this one
l1 = GetGameVarID(i, g_ac, g_p).safeValue();
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue(); // l2 not used in this one
lResult = gs.max_ammo_amount[l1];
SetGameVarID(*insptr, lResult, g_ac, g_p);
insptr++;
@ -3267,8 +3322,8 @@ int ParseState::parse(void)
int l1, l2;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p);
l2 = GetGameVarID(*insptr, g_ac, g_p);
l1 = GetGameVarID(i, g_ac, g_p).safeValue();
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue();
gs.max_ammo_amount[l1] = l2;
insptr++;
@ -3281,8 +3336,8 @@ int ParseState::parse(void)
int lResult;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p);
l2 = GetGameVarID(*insptr, g_ac, g_p);
l1 = GetGameVarID(i, g_ac, g_p).safeValue();
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue();
lResult = l1 * l2;
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
@ -3295,8 +3350,8 @@ int ParseState::parse(void)
int lResult;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p);
l2 = GetGameVarID(*insptr, g_ac, g_p);
l1 = GetGameVarID(i, g_ac, g_p).safeValue();
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue();
if (l2 == 0)
{
I_Error("Divide by Zero in CON");
@ -3313,8 +3368,8 @@ int ParseState::parse(void)
int lResult;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p);
l2 = GetGameVarID(*insptr, g_ac, g_p);
l1 = GetGameVarID(i, g_ac, g_p).safeValue();
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue();
if (l2 == 0)
{
I_Error("Mod by Zero in CON");
@ -3331,8 +3386,8 @@ int ParseState::parse(void)
int lResult;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p);
l2 = GetGameVarID(*insptr, g_ac, g_p);
l1 = GetGameVarID(i, g_ac, g_p).safeValue();
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue();
lResult = l1 & l2;
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
@ -3345,8 +3400,8 @@ int ParseState::parse(void)
int lResult;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p);
l2 = GetGameVarID(*insptr, g_ac, g_p);
l1 = GetGameVarID(i, g_ac, g_p).safeValue();
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue();
lResult = l1 ^ l2;
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
@ -3359,8 +3414,8 @@ int ParseState::parse(void)
int lResult;
insptr++;
i = *(insptr++); // ID of def
l1 = GetGameVarID(i, g_ac, g_p);
l2 = GetGameVarID(*insptr, g_ac, g_p);
l1 = GetGameVarID(i, g_ac, g_p).safeValue();
l2 = GetGameVarID(*insptr, g_ac, g_p).safeValue();
lResult = l1 | l2;
SetGameVarID(i, lResult, g_ac, g_p);
insptr++;
@ -3371,7 +3426,7 @@ int ParseState::parse(void)
int i;
insptr++;
i = *(insptr++); // ID of def
SetGameVarID(i, GetGameVarID(i, g_ac, g_p) - *insptr, g_ac, g_p);
SetGameVarID(i, GetGameVarID(i, g_ac, g_p).safeValue() - *insptr, g_ac, g_p);
insptr++;
break;
}
@ -3380,7 +3435,7 @@ int ParseState::parse(void)
int i;
insptr++;
i = *(insptr++); // ID of def
SetGameVarID(i, GetGameVarID(i, g_ac, g_p) - GetGameVarID(*insptr, g_ac, g_p), g_ac, g_p);
SetGameVarID(i, GetGameVarID(i, g_ac, g_p).safeValue() - GetGameVarID(*insptr, g_ac, g_p).safeValue(), g_ac, g_p);
insptr++;
break;
}
@ -3390,7 +3445,7 @@ int ParseState::parse(void)
int lValue;
insptr++;
i = *(insptr++); // ID of def
lValue = bsin(GetGameVarID(*insptr, g_ac, g_p));
lValue = bsin(GetGameVarID(*insptr, g_ac, g_p).safeValue());
SetGameVarID(i, lValue, g_ac, g_p);
insptr++;
break;
@ -3434,8 +3489,8 @@ int ParseState::parse(void)
int levnume;
insptr++; // skip command
volnume = GetGameVarID(*insptr++, g_ac, g_p);
levnume = GetGameVarID(*insptr++, g_ac, g_p);
volnume = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
levnume = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
auto level = FindMapByIndex(volnume, levnume);
if (level != nullptr)
ChangeLevel(level, g_nextskill);
@ -3453,14 +3508,14 @@ int ParseState::parse(void)
int orientation;
int pal;
int tw = *insptr++;
x = GetGameVarID(*insptr++, g_ac, g_p);
y = GetGameVarID(*insptr++, g_ac, g_p);
tilenum = GetGameVarID(*insptr++, g_ac, g_p);
shade = GetGameVarID(*insptr++, g_ac, g_p);
orientation = GetGameVarID(*insptr++, g_ac, g_p);
x = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
y = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
tilenum = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
shade = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
orientation = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
if (tw == concmd_myospal)
{
pal = GetGameVarID(*insptr++, g_ac, g_p);
pal = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
//myospal(x, y, tilenum, shade, orientation, pal);
}
else if (tw == concmd_myos)
@ -3473,7 +3528,7 @@ int ParseState::parse(void)
}
else if (tw == concmd_myospalx)
{
pal = GetGameVarID(*insptr++, g_ac, g_p);
pal = GetGameVarID(*insptr++, g_ac, g_p).safeValue();
//myospal640(x, y, tilenum, shade, orientation, pal);
}
break;
@ -3508,7 +3563,7 @@ int ParseState::parse(void)
// For each case: value, ptr to code
insptr++; // p-code
lVarID = *insptr++;
lValue = GetGameVarID(lVarID, g_ac, g_p);
lValue = GetGameVarID(lVarID, g_ac, g_p).safeValue();
lEnd = *insptr++;
lCases = *insptr++;
lpDefault = insptr++;
@ -3569,7 +3624,7 @@ int ParseState::parse(void)
insptr++;
i = *(insptr++); // ID of def
j = 0;
if (GetGameVarID(i, g_ac, g_p) & GetGameVarID(*(insptr), g_ac, g_p))
if (GetGameVarID(i, g_ac, g_p).safeValue() & GetGameVarID(*(insptr), g_ac, g_p).safeValue())
{
j = 1;
}
@ -3582,7 +3637,7 @@ int ParseState::parse(void)
insptr++;
i = *(insptr++); // ID of def
j = 0;
if (GetGameVarID(i, g_ac, g_p) != GetGameVarID(*(insptr), g_ac, g_p))
if (GetGameVarID(i, g_ac, g_p).safeValue() != GetGameVarID(*(insptr), g_ac, g_p).safeValue())
{
j = 1;
}
@ -3595,7 +3650,7 @@ int ParseState::parse(void)
insptr++;
i = *(insptr++); // ID of def
j = 0;
if (GetGameVarID(i, g_ac, g_p) != *insptr)
if (GetGameVarID(i, g_ac, g_p).safeValue() != *insptr)
{
j = 1;
}
@ -3608,7 +3663,7 @@ int ParseState::parse(void)
insptr++;
i = *(insptr++); // ID of def
j = 0;
if (GetGameVarID(i, g_ac, g_p) & *insptr)
if (GetGameVarID(i, g_ac, g_p).safeValue() & *insptr)
{
j = 1;
}

View file

@ -55,6 +55,45 @@ extern int errorcount, warningcount, line_count;
//intptr_t *actorLoadEventScrptr[MAXTILES];
intptr_t apScriptGameEvent[MAXGAMEEVENTS];
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
FSerializer& Serialize(FSerializer& arc, const char* keyname, GameVarValue& w, GameVarValue* def)
{
if (arc.BeginObject(keyname))
{
int type = w.isActor() ? 0 : 1;
arc("type", type);
switch (type)
{
case 0:
{
// pointless to handle now, fix when saving works again.
#pragma message(" fix me!");
#if 0
DDukeActor* a = arc.isWriting() ? w.actor() : nullptr;
arc("actor", a);
if (arc.isReading()) w = GameVarValue(a);
break;
#endif
}
case 1:
{
auto s = arc.isWriting()? w.value() : 0;
arc("value", s);
if (arc.isReading()) w = GameVarValue(s);
break;
}
}
arc.EndObject();
}
return arc;
}
//---------------------------------------------------------------------------
//
//
@ -72,8 +111,12 @@ void SerializeGameVars(FSerializer &arc)
{
if (arc.BeginObject(gv.szLabel))
{
arc("value", gv.lValue)
("initvalue", gv.initValue)
if (gv.dwFlags & (GAMEVAR_FLAG_PERPLAYER | GAMEVAR_FLAG_PERACTOR))
arc("index", gv.indexValue);
else
arc("value", gv.lValue);
arc("initvalue", gv.initValue)
.EndObject();
}
}
@ -115,10 +158,10 @@ int AddGameVar(const char* pszLabel, intptr_t lValue, unsigned dwFlags)
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_SYSTEM && !(dwFlags & (GAMEVAR_FLAG_PLONG | GAMEVAR_FLAG_PFUNC)))
{
// if existing is system, they only get to change default value....
aGameVars[i].lValue = (int)lValue;
aGameVars[i].lValue = GameVarValue(lValue);
if (!(dwFlags & GAMEVAR_FLAG_NODEFAULT))
{
aGameVars[i].defaultValue = (int)lValue;
aGameVars[i].defaultValue = GameVarValue(lValue);
}
}
else
@ -131,14 +174,14 @@ int AddGameVar(const char* pszLabel, intptr_t lValue, unsigned dwFlags)
}
else
{
aGameVars[i].lValue = (int)lValue;
aGameVars[i].lValue = GameVarValue(lValue);
}
if (!(dwFlags & GAMEVAR_FLAG_NODEFAULT))
{
aGameVars[i].defaultValue = (int)lValue;
aGameVars[i].defaultValue = GameVarValue(lValue);
}
}
aGameVars[i].initValue = (int)lValue;
aGameVars[i].initValue = GameVarValue(lValue);
if (i == iGameVarCount)
{
// we're adding a new one.
@ -232,7 +275,7 @@ void ResetGameVars(void)
{
for (auto &pl : ps)
{
pl.uservars[aGameVars[i].lValue] = aGameVars[i].defaultValue;
pl.uservars[aGameVars[i].indexValue] = aGameVars[i].defaultValue;
}
}
else if (!(aGameVars[i].dwFlags & GAMEVAR_FLAG_PERACTOR))
@ -254,22 +297,22 @@ void ResetGameVars(void)
//
//---------------------------------------------------------------------------
int GetGameVarID(int id, DDukeActor* sActor, int sPlayer)
GameVarValue GetGameVarID(int id, DDukeActor* sActor, int sPlayer)
{
if(id<0 || id >= iGameVarCount)
{
Printf("GetGameVarID: Invalid Game ID %d\n", id);
return -1;
return GameVarValue(-1);
}
if (id == g_iThisActorID)
{
return ActorToScriptIndex(sActor);
return GameVarValue(sActor);
}
if( aGameVars[id].dwFlags & GAMEVAR_FLAG_PERPLAYER )
{
// for the current player
if (sPlayer >= 0 && sPlayer < MAXPLAYERS)
return ps[sPlayer].uservars[aGameVars[id].lValue];
return ps[sPlayer].uservars[aGameVars[id].indexValue];
return aGameVars[id].initValue;
}
@ -278,7 +321,7 @@ int GetGameVarID(int id, DDukeActor* sActor, int sPlayer)
// for the current actor
if(sActor != nullptr)
{
return sActor->uservars[aGameVars[id].lValue];
return sActor->uservars[aGameVars[id].indexValue];
}
else
{
@ -292,7 +335,7 @@ int GetGameVarID(int id, DDukeActor* sActor, int sPlayer)
Printf("GetGameVarID NULL PlValues for PLONG Var=%s\n",aGameVars[id].szLabel);
}
return *aGameVars[id].plValue;
return GameVarValue(*aGameVars[id].plValue);
}
else if (aGameVars[id].dwFlags & GAMEVAR_FLAG_PFUNC)
{
@ -301,7 +344,7 @@ int GetGameVarID(int id, DDukeActor* sActor, int sPlayer)
Printf("GetGameVarID NULL PlValues for PFUNC Var=%s\n", aGameVars[id].szLabel);
}
return aGameVars[id].getter();
return GameVarValue(aGameVars[id].getter());
}
else
{
@ -316,7 +359,7 @@ int GetGameVarID(int id, DDukeActor* sActor, int sPlayer)
//
//---------------------------------------------------------------------------
void SetGameVarID(int id, int lValue, DDukeActor* sActor, int sPlayer)
void SetGameVarID(int id, GameVarValue lValue, DDukeActor* sActor, int sPlayer)
{
if(id<0 || id >= iGameVarCount)
{
@ -329,24 +372,24 @@ void SetGameVarID(int id, int lValue, DDukeActor* sActor, int sPlayer)
if (sPlayer >= 0)
{
if (sPlayer < MAXPLAYERS)
ps[sPlayer].uservars[aGameVars[id].lValue] = lValue;
ps[sPlayer].uservars[aGameVars[id].indexValue] = lValue;
}
else
{
for (int i = connecthead; i >= 0; i = connectpoint2[i])
ps[i].uservars[aGameVars[id].lValue] = lValue; // set for all players
ps[i].uservars[aGameVars[id].indexValue] = lValue; // set for all players
aGameVars[id].initValue = lValue;
}
}
else if( aGameVars[id].dwFlags & GAMEVAR_FLAG_PERACTOR )
{
// for the current actor
if (sActor != nullptr) sActor->uservars[aGameVars[id].lValue] = lValue;
if (sActor != nullptr) sActor->uservars[aGameVars[id].indexValue] = lValue;
else
{
DukeSpriteIterator it;
while (auto actor = it.Next())
actor->uservars[aGameVars[id].lValue] = lValue;
actor->uservars[aGameVars[id].indexValue] = lValue;
aGameVars[id].initValue = lValue;
}
@ -354,7 +397,7 @@ void SetGameVarID(int id, int lValue, DDukeActor* sActor, int sPlayer)
else if( aGameVars[id].dwFlags & GAMEVAR_FLAG_PLONG )
{
// set the value at pointer
*aGameVars[id].plValue=lValue;
*aGameVars[id].plValue = lValue.safeValue();
}
else if( !(aGameVars[id].dwFlags & GAMEVAR_FLAG_PFUNC) )
{
@ -363,13 +406,23 @@ void SetGameVarID(int id, int lValue, DDukeActor* sActor, int sPlayer)
}
void SetGameVarID(int id, int lValue, DDukeActor* sActor, int sPlayer)
{
SetGameVarID(id, GameVarValue(lValue), sActor, sPlayer);
}
void SetGameVarID(int id, DDukeActor* lValue, DDukeActor* sActor, int sPlayer)
{
SetGameVarID(id, GameVarValue(lValue), sActor, sPlayer);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int GetGameVar(const char *szGameLabel, int lDefault, DDukeActor* sActor, int sPlayer)
GameVarValue GetGameVar(const char *szGameLabel, GameVarValue lDefault, DDukeActor* sActor, int sPlayer)
{
for (int i = 0; i < iGameVarCount; i++)
{
@ -381,6 +434,11 @@ int GetGameVar(const char *szGameLabel, int lDefault, DDukeActor* sActor, int sP
return lDefault;
}
GameVarValue GetGameVar(const char* szGameLabel, int lDefault, DDukeActor* sActor, int sPlayer)
{
return GetGameVar(szGameLabel, GameVarValue(lDefault), sActor, sPlayer);
}
//---------------------------------------------------------------------------
//
// only used for the aplWeapon stuff
@ -397,7 +455,7 @@ int GetGameValuePtr(char *szGameLabel)
{
I_FatalError("%s was overridden to something other than a player gamevar.", szGameLabel);
}
return aGameVars[i].lValue;
return aGameVars[i].indexValue;
}
}
I_FatalError("%s was overridden to something other than a player gamevar.", szGameLabel);
@ -468,21 +526,21 @@ static int i_aplWeaponFireSound[MAX_WEAPONS]; // Sound made when firing (each ti
static int i_aplWeaponSound2Time[MAX_WEAPONS]; // Alternate sound time
static int i_aplWeaponSound2Sound[MAX_WEAPONS]; // Alternate sound sound ID
int aplWeaponClip(int weapon, int player) { return ps[player].uservars[i_aplWeaponClip[weapon]]; }
int aplWeaponReload(int weapon, int player) { return ps[player].uservars[i_aplWeaponReload[weapon]]; }
int aplWeaponFireDelay(int weapon, int player) { return ps[player].uservars[i_aplWeaponFireDelay[weapon]]; }
int aplWeaponHoldDelay(int weapon, int player) { return ps[player].uservars[i_aplWeaponHoldDelay[weapon]]; }
int aplWeaponTotalTime(int weapon, int player) { return ps[player].uservars[i_aplWeaponTotalTime[weapon]]; }
int aplWeaponFlags(int weapon, int player) { return ps[player].uservars[i_aplWeaponFlags[weapon]]; }
int aplWeaponShoots(int weapon, int player) { return ps[player].uservars[i_aplWeaponShoots[weapon]]; }
int aplWeaponSpawnTime(int weapon, int player) { return ps[player].uservars[i_aplWeaponSpawnTime[weapon]]; }
int aplWeaponSpawn(int weapon, int player) { return ps[player].uservars[i_aplWeaponSpawn[weapon]]; }
int aplWeaponShotsPerBurst(int weapon, int player) { return ps[player].uservars[i_aplWeaponShotsPerBurst[weapon]]; }
int aplWeaponWorksLike(int weapon, int player) { return ps[player].uservars[i_aplWeaponWorksLike[weapon]]; }
int aplWeaponInitialSound(int weapon, int player) { return ps[player].uservars[i_aplWeaponInitialSound[weapon]]; }
int aplWeaponFireSound(int weapon, int player) { return ps[player].uservars[i_aplWeaponFireSound[weapon]]; }
int aplWeaponSound2Time(int weapon, int player) { return ps[player].uservars[i_aplWeaponSound2Time[weapon]]; }
int aplWeaponSound2Sound(int weapon, int player) { return ps[player].uservars[i_aplWeaponSound2Sound[weapon]]; }
int aplWeaponClip(int weapon, int player) { return ps[player].uservars[i_aplWeaponClip[weapon]].safeValue(); }
int aplWeaponReload(int weapon, int player) { return ps[player].uservars[i_aplWeaponReload[weapon]].safeValue(); }
int aplWeaponFireDelay(int weapon, int player) { return ps[player].uservars[i_aplWeaponFireDelay[weapon]].safeValue(); }
int aplWeaponHoldDelay(int weapon, int player) { return ps[player].uservars[i_aplWeaponHoldDelay[weapon]].safeValue(); }
int aplWeaponTotalTime(int weapon, int player) { return ps[player].uservars[i_aplWeaponTotalTime[weapon]].safeValue(); }
int aplWeaponFlags(int weapon, int player) { return ps[player].uservars[i_aplWeaponFlags[weapon]].safeValue(); }
int aplWeaponShoots(int weapon, int player) { return ps[player].uservars[i_aplWeaponShoots[weapon]].safeValue(); }
int aplWeaponSpawnTime(int weapon, int player) { return ps[player].uservars[i_aplWeaponSpawnTime[weapon]].safeValue(); }
int aplWeaponSpawn(int weapon, int player) { return ps[player].uservars[i_aplWeaponSpawn[weapon]].safeValue(); }
int aplWeaponShotsPerBurst(int weapon, int player) { return ps[player].uservars[i_aplWeaponShotsPerBurst[weapon]].safeValue(); }
int aplWeaponWorksLike(int weapon, int player) { return ps[player].uservars[i_aplWeaponWorksLike[weapon]].safeValue(); }
int aplWeaponInitialSound(int weapon, int player) { return ps[player].uservars[i_aplWeaponInitialSound[weapon]].safeValue(); }
int aplWeaponFireSound(int weapon, int player) { return ps[player].uservars[i_aplWeaponFireSound[weapon]].safeValue(); }
int aplWeaponSound2Time(int weapon, int player) { return ps[player].uservars[i_aplWeaponSound2Time[weapon]].safeValue(); }
int aplWeaponSound2Sound(int weapon, int player) { return ps[player].uservars[i_aplWeaponSound2Sound[weapon]].safeValue(); }
//---------------------------------------------------------------------------
//
@ -1166,11 +1224,11 @@ void FinalizeGameVars(void)
{
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_PERPLAYER)
{
aGameVars[i].lValue = weapNdx++;
aGameVars[i].indexValue = weapNdx++;
}
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_PERACTOR)
{
aGameVars[i].lValue = actorNdx++;
aGameVars[i].indexValue = actorNdx++;
}
}
for (auto& pl : ps) pl.uservars.Resize(weapNdx);
@ -1200,7 +1258,7 @@ void SetupGameVarsForActor(DDukeActor* actor)
{
if (aGameVars[i].dwFlags & GAMEVAR_FLAG_PERACTOR)
{
actor->uservars[aGameVars[i].lValue] = aGameVars[i].initValue;
actor->uservars[aGameVars[i].indexValue] = aGameVars[i].initValue;
}
}
}

View file

@ -6,6 +6,39 @@ BEGIN_DUKE_NS
// gamedef.c
class DDukeActor;
// Game vars can reference actors, we need a type-safe way to handle that so that index values won't get misappropriated and actors can be GC'd.
class GameVarValue
{
enum EType
{
Actor = 0,
Value = 1
};
union
{
DDukeActor* ActorP;
uint64_t index;
};
public:
GameVarValue() = default;
explicit GameVarValue(DDukeActor* actor_) { ActorP = actor_; assert(isActor()); /* GC:WriteBarrier(actor);*/ }
explicit GameVarValue(int val) { index = (val << 8) | Value; }
bool isActor() const { return (index & 7) == Actor; }
bool isValue() const { return (index & 7) == Value; }
DDukeActor* actor() const { assert(isActor()); return /*GC::ReadBarrier*/(ActorP); }
int value() const { assert(isValue()); return index >> 8; }
int safeValue() const { return isValue() ? value() : actor() == nullptr ? 0 : -1; } // return -1 for valid actors and 0 for null. This allows most comparisons to work.
DDukeActor* safeActor() const { return isActor() ? actor() : nullptr; }
bool operator==(const GameVarValue& other) const { return index == other.index; }
bool operator!=(const GameVarValue& other) const { return index != other.index; }
};
enum
{
// store global game definitions
@ -95,12 +128,13 @@ typedef struct
{
union
{
int lValue;
GameVarValue lValue;
int indexValue;
int* plValue;
int (*getter)();
};
int defaultValue;
int initValue; // this is what gets copied to players/actors upon spawn. This is not the same as the default!
GameVarValue defaultValue;
GameVarValue initValue; // this is what gets copied to players/actors upon spawn. This is not the same as the default!
unsigned int dwFlags;
char szLabel[MAXVARLABEL];
} MATTGAMEVAR;
@ -126,10 +160,15 @@ int GetDefID(const char *szGameLabel);
void ClearGameVars(void);
void AddSystemVars();
void ResetGameVars(void);
struct DDukeActor;
int GetGameVarID(int id, DDukeActor* sActor, int sPlayer);
class DDukeActor;
class GameVarValue;
GameVarValue GetGameVarID(int id, DDukeActor* sActor, int sPlayer);
void SetGameVarID(int id, GameVarValue lValue, DDukeActor* sActor, int sPlayer);
GameVarValue GetGameVar(const char* szGameLabel, GameVarValue lDefault, DDukeActor* sActor, int sPlayer);
GameVarValue GetGameVar(const char* szGameLabel, int lDefault, DDukeActor* sActor, int sPlayer);
void SetGameVarID(int id, int lValue, DDukeActor* sActor, int sPlayer);
int GetGameVar(const char* szGameLabel, int lDefault, DDukeActor* sActor, int sPlayer);
void SetGameVarID(int id, DDukeActor* lValue, DDukeActor* sActor, int sPlayer);
void ClearGameEvents();
bool IsGameEvent(int i);

View file

@ -98,7 +98,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_QUICKKICK, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() == 0)
{
p->quick_kick = 14;
if (!p->quick_kick_msg && plnum == screenpeek) FTA(QUOTE_MIGHTY_FOOT, p);
@ -123,7 +123,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_INVENTORY, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() == 0)
{
if (p->inven_icon > ICON_NONE && p->inven_icon <= ICON_HEATS) PlayerSetItemUsed(plnum, p->inven_icon);
}
@ -133,7 +133,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USENIGHTVISION, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0 && p->heat_amount > 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() == 0 && p->heat_amount > 0)
{
p->heat_on = !p->heat_on;
p->inven_icon = 5;
@ -146,7 +146,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USESTEROIDS, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() == 0)
{
if (p->steroids_amount == 400)
{
@ -229,13 +229,13 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, dainv, nullptr, plnum);
OnEvent(EVENT_INVENTORYLEFT, plnum, nullptr, -1);
dainv = GetGameVarID(g_iReturnVarID, nullptr, plnum);
dainv = GetGameVarID(g_iReturnVarID, nullptr, plnum).safeValue();
}
if (PlayerInput(plnum, SB_INVNEXT))
{
SetGameVarID(g_iReturnVarID, dainv, nullptr, plnum);
OnEvent(EVENT_INVENTORYRIGHT, plnum, nullptr, -1);
dainv = GetGameVarID(g_iReturnVarID, nullptr, plnum);
dainv = GetGameVarID(g_iReturnVarID, nullptr, plnum).safeValue();
}
p->inven_icon = dainv;
// Someone must have really hated constant data, doing this with a switch/case (and of course also with literal numbers...)
@ -273,7 +273,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_HOLODUKEON, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() == 0)
{
if (!isRR())
{
@ -330,7 +330,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USENIGHTVISION, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() == 0)
{
if (p->yehaa_timer == 0)
{
@ -362,7 +362,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USEMEDKIT, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() == 0)
{
if (p->firstaid_amount > 0 && p->GetActor()->s->extra < gs.max_player_health)
{
@ -415,7 +415,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USEJETPACK, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() == 0)
{
if (!isRR())
{
@ -483,7 +483,7 @@ void hud_input(int plnum)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_TURNAROUND, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) != 0)
if (GetGameVarID(g_iReturnVarID, nullptr, plnum).value() != 0)
{
p->sync.actions &= ~SB_TURNAROUND;
}

View file

@ -691,7 +691,7 @@ void playerCrouch(int snum)
// crouching
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_CROUCH, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
p->pos.z += (2048 + 768);
p->crack_time = CRACK_TIME;
@ -707,7 +707,7 @@ void playerJump(int snum, int fz, int cz)
{
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_JUMP, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
p->jumping_counter = 1;
p->jumping_toggle = 1;
@ -835,7 +835,7 @@ void checklook(int snum, ESyncBits actions)
{
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_LOOKLEFT, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) != 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() != 0)
{
actions &= ~SB_LOOK_LEFT;
}
@ -845,7 +845,7 @@ void checklook(int snum, ESyncBits actions)
{
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_LOOKRIGHT, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) != 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() != 0)
{
actions &= ~SB_LOOK_RIGHT;
}
@ -864,7 +864,7 @@ void playerCenterView(int snum)
auto p = &ps[snum];
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_RETURNTOCENTER, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
p->sync.actions |= SB_CENTERVIEW;
}
@ -879,7 +879,7 @@ void playerLookUp(int snum, ESyncBits actions)
auto p = &ps[snum];
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_LOOKUP, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
p->sync.actions |= SB_CENTERVIEW;
}
@ -894,7 +894,7 @@ void playerLookDown(int snum, ESyncBits actions)
auto p = &ps[snum];
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_LOOKDOWN, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
p->sync.actions |= SB_CENTERVIEW;
}
@ -909,7 +909,7 @@ void playerAimUp(int snum, ESyncBits actions)
auto p = &ps[snum];
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_AIMUP, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) != 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() != 0)
{
p->sync.actions &= ~SB_AIM_UP;
}
@ -920,7 +920,7 @@ void playerAimDown(int snum, ESyncBits actions)
auto p = &ps[snum];
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_AIMDOWN, snum, p->GetActor(), -1); // due to a typo in WW2GI's CON files this is the same as EVENT_AIMUP.
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) != 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() != 0)
{
p->sync.actions &= ~SB_AIM_DOWN;
}
@ -1228,7 +1228,7 @@ DEFINE_ACTION_FUNCTION(_DukePlayer, GetGameVar)
PARAM_SELF_STRUCT_PROLOGUE(player_struct);
PARAM_STRING(name);
PARAM_INT(def);
ACTION_RETURN_INT(GetGameVar(name, def, self->GetActor(), self->GetPlayerNum()));
ACTION_RETURN_INT(GetGameVar(name, def, self->GetActor(), self->GetPlayerNum()).safeValue());
}
DEFINE_ACTION_FUNCTION(_Duke, GetViewPlayer)

View file

@ -331,7 +331,7 @@ static void shootweapon(DDukeActor *actor, int p, int sx, int sy, int sz, int sa
{
SetGameVarID(g_iAimAngleVarID, AUTO_AIM_ANGLE, actor, p);
OnEvent(EVENT_GETAUTOAIMANGLE, p, ps[p].GetActor(), -1);
int varval = GetGameVarID(g_iAimAngleVarID, actor, p);
int varval = GetGameVarID(g_iAimAngleVarID, actor, p).value();
DDukeActor* aimed = nullptr;
if (varval > 0)
{
@ -366,8 +366,8 @@ static void shootweapon(DDukeActor *actor, int p, int sx, int sy, int sz, int sa
SetGameVarID(g_iAngRangeVarID, 32, actor, p);
SetGameVarID(g_iZRangeVarID, 256, actor, p);
OnEvent(EVENT_GETSHOTRANGE, p, ps[p].GetActor(), -1);
angRange = GetGameVarID(g_iAngRangeVarID, actor, p);
zRange = GetGameVarID(g_iZRangeVarID, actor, p);
angRange = GetGameVarID(g_iAngRangeVarID, actor, p).value();
zRange = GetGameVarID(g_iZRangeVarID, actor, p).value();
sa += (angRange / 2) - (krand() & (angRange - 1));
if (aimed == nullptr)
@ -884,11 +884,11 @@ static void shootlaser(DDukeActor* actor, int p, int sx, int sy, int sz, int sa)
if (!bomb) return;
if (isWW2GI())
{
int lTripBombControl = GetGameVar("TRIPBOMB_CONTROL", TRIPBOMB_TRIPWIRE, nullptr, -1);
int lTripBombControl = GetGameVar("TRIPBOMB_CONTROL", TRIPBOMB_TRIPWIRE, nullptr, -1).value();
if (lTripBombControl & TRIPBOMB_TIMER)
{
int lLifetime = GetGameVar("STICKYBOMB_LIFETIME", NAM_GRENADE_LIFETIME, nullptr, p);
int lLifetimeVar = GetGameVar("STICKYBOMB_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, nullptr, p);
int lLifetime = GetGameVar("STICKYBOMB_LIFETIME", NAM_GRENADE_LIFETIME, nullptr, p).value();
int lLifetimeVar = GetGameVar("STICKYBOMB_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, nullptr, p).value();
// set timer. blows up when at zero....
bomb->s->extra = lLifetime
+ MulScale(krand(), lLifetimeVar, 14)
@ -1024,7 +1024,7 @@ void shoot_d(DDukeActor* actor, int atwith)
SetGameVarID(g_iAtWithVarID, atwith, actor, p);
SetGameVarID(g_iReturnVarID, 0, actor, p);
OnEvent(EVENT_SHOOT, p, ps[p].GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, actor, p) != 0)
if (GetGameVarID(g_iReturnVarID, actor, p).safeValue() != 0)
{
return;
}
@ -1716,7 +1716,7 @@ static void operateJetpack(int snum, ESyncBits actions, int psectlotag, int fz,
// jump
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_SOARUP, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
p->pos.z -= j;
p->crack_time = CRACK_TIME;
@ -1728,7 +1728,7 @@ static void operateJetpack(int snum, ESyncBits actions, int psectlotag, int fz,
// crouch
SetGameVarID(g_iReturnVarID, 0, p->GetActor(), snum);
OnEvent(EVENT_SOARDOWN, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
p->pos.z += j;
p->crack_time = CRACK_TIME;
@ -2657,7 +2657,7 @@ static void processweapon(int snum, ESyncBits actions)
SetGameVarID(g_iWeaponVarID, p->curr_weapon, p->GetActor(), snum);
SetGameVarID(g_iWorksLikeVarID, aplWeaponWorksLike(p->curr_weapon, snum), p->GetActor(), snum);
OnEvent(EVENT_HOLSTER, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
// now it uses the game definitions...
if (aplWeaponFlags(p->curr_weapon, snum) & WEAPON_FLAG_HOLSTER_CLEARS_CLIP)
@ -3072,7 +3072,7 @@ HORIZONLY:
psectp = s->sector();
if (ud.clipping == 0 && psectp->lotag == 31)
{
auto secact = ScriptIndexToActor(psectp->hitag);
auto secact = static_cast<DDukeActor*>(psectp->hitagactor);
if (secact && secact->s->xvel && secact->temp_data[0] == 0)
{
quickkill(p);

View file

@ -869,7 +869,7 @@ void shoot_r(DDukeActor* actor, int atwith)
SetGameVarID(g_iAtWithVarID, atwith, actor, p);
SetGameVarID(g_iReturnVarID, 0, actor, p);
OnEvent(EVENT_SHOOT, p, ps[p].GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, actor, p) != 0)
if (GetGameVarID(g_iReturnVarID, actor, p).safeValue() != 0)
{
return;
}
@ -3909,7 +3909,7 @@ HORIZONLY:
psectp = s->sector();
if (ud.clipping == 0 && psectp->lotag == ST_31_TWO_WAY_TRAIN)
{
auto act = ScriptIndexToActor(psectp->hitag);
auto act = static_cast<DDukeActor*>(psectp->hitagactor);
if (act && act->s->xvel && act->temp_data[0] == 0)
{
quickkill(p);

View file

@ -162,7 +162,7 @@ void fireweapon_ww(int snum)
SetGameVarID(g_iWeaponVarID, p->curr_weapon, p->GetActor(), snum);
SetGameVarID(g_iWorksLikeVarID, aplWeaponWorksLike(p->curr_weapon, snum), p->GetActor(), snum);
OnEvent(EVENT_FIRE, snum, p->GetActor(), -1);
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum) == 0)
if (GetGameVarID(g_iReturnVarID, p->GetActor(), snum).value() == 0)
{
switch (aplWeaponWorksLike(p->curr_weapon, snum))
{
@ -350,8 +350,8 @@ void operateweapon_ww(int snum, ESyncBits actions)
if (j)
{
{
int lGrenadeLifetime = GetGameVar("GRENADE_LIFETIME", NAM_GRENADE_LIFETIME, nullptr, snum);
int lGrenadeLifetimeVar = GetGameVar("GRENADE_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, nullptr, snum);
int lGrenadeLifetime = GetGameVar("GRENADE_LIFETIME", NAM_GRENADE_LIFETIME, nullptr, snum).value();
int lGrenadeLifetimeVar = GetGameVar("GRENADE_LIFETIME_VAR", NAM_GRENADE_LIFETIME_VAR, nullptr, snum).value();
// set timer. blows up when at zero....
j->s->extra = lGrenadeLifetime
+ MulScale(krand(), lGrenadeLifetimeVar, 14)

View file

@ -32,6 +32,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
#include "gamestate.h"
#include "dukeactor.h"
#include "savegamehelp.h"
#include "gamevar.h"
//==========================================================================
//
@ -39,17 +40,19 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
//
//==========================================================================
template<> FSerializer& Serialize(FSerializer& arc, const char* key, Duke3d::DDukeActor*& ht, Duke3d::DDukeActor** def)
{
int index = ht? ht->GetSpriteIndex() : -1;
assert(index >= -1 && index < MAXSPRITES);
Serialize(arc, key, index, nullptr);
ht = index < 0? nullptr : &Duke3d::hittype[index];
return arc;
}
BEGIN_DUKE_NS
/*template<>*/ FSerializer& Serialize(FSerializer& arc, const char* key, Duke3d::DDukeActor*& ht, Duke3d::DDukeActor** def)
{
int index = ht ? ht->GetSpriteIndex() : -1;
assert(index >= -1 && index < MAXSPRITES);
Serialize(arc, key, index, nullptr);
ht = index < 0 ? nullptr : &Duke3d::hittype[index];
return arc;
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, GameVarValue& w, GameVarValue* def);
void SerializeActorGlobals(FSerializer& arc);
void lava_serialize(FSerializer& arc);
void SerializeGameVars(FSerializer &arc);

View file

@ -21,10 +21,10 @@ struct STATUSBARTYPE
bool gotweapon[MAX_WEAPONS];
};
struct DDukeActor : public DCoreActor
class DDukeActor : public DCoreActor
{
using Super = DCoreActor;
public:
uint8_t cgg;
uint8_t spriteextra; // moved here for easier maintenance. This was originally a hacked in field in the sprite structure called 'filler'.
DDukeActor* ownerActor, * hitOwnerActor;
@ -45,7 +45,7 @@ struct DDukeActor : public DCoreActor
DDukeActor* temp_actor, *seek_actor;
spritetype* s; // direct reference to the corresponding sprite.
TArray<int64_t> uservars;
TArray<GameVarValue> uservars;
static DDukeActor* array(); // this is necessary to allow define inline functions referencing the global array inside the definition itself.
@ -304,7 +304,7 @@ struct player_struct
double vehForwardScale, vehReverseScale, MotoSpeed;
bool vehTurnLeft, vehTurnRight, vehBraking;
TArray<int64_t> uservars;
TArray<GameVarValue> uservars;
// input stuff.