Add A_ClientsideACSExecute
Syntax is identical to ACS_NamedExecuteWithResult. Allows non-clientside actors to execute clientside scripts locally, without any bandwidth usage. Useful for per-client effects.
Motivation: The ACS_NamedX functions can't execute clientside at all, if the actor isn't clientside-only.
This new function allows a client to execute a script of its own accord, if an actor's state calls for it, even if it's not a clientside-only actor.
Allows me to do this: video
Without having the server tell the clients to execute a clientside script every tic (or every so many), for potentially thousands of actors at the same time.
The name was chosen to indicate that its meant to be used only as a state codepointer, and not confuse people into thinking this is a special or something they can use as an ACS command like ACS_Execute*.
Some example code:
actor AmmoGiver_Base : SH_FakeInventory
{
Radius 20
Height 16
+NOTIMEFREEZE
+INVENTORY.QUIET
States
{
Spawn:
TNT1 A 0 NoDelay A_SetSpecial(84, 13236, 0, false)
TNT1 A 0 Thing_ChangeTID(0, 69069)
TNT1 A 0 A_SetUserVar("user_scale_Base", 100)
TNT1 A 0 A_CheckFlag("DROPPED", "Dropped")
TNT1 A 35 A_Jump(256, "Init")
wait
Dropped:
TNT1 A 0 A_SetArg(2, true) // Dropped
TNT1 A 35 A_Jump(256, "Init")
wait
Init:
TNT1 A 0 // A_SetArg(1, AMMO_CLIP) // Ammo Type
goto CheckSprite
Base:
UNKN A -1
loop
Idle:
"####" "#" -1
stop
RecheckSprite:
"####" "#" 1
goto CheckSprite
CheckSprite:
"####" "#" -1 A_ClientsideACSExecute("SET_PICKUP_SPRITE")
loop
}
}
actor ChaingunGiver : WeaponGiver_Base replaces Chaingun
{
//$Sprite FCGNA0
States
{
Base:
FCGN A -1
loop
Kunai:
KPDW Z -1
loop
Gosimer:
DEGT A -1
loop
MaximusPrime:
BRIF A -1
loop
MaximusPrimeDropped:
AGUN A -1
loop
Init:
TNT1 A 0 A_SetArg(1, WEAPON_CHAINGUN) // Weapon Type
goto CheckSprite
SetScale:
"####" "#" 0 A_SetUserVar(user_scale_Kunai, 100)
"####" "#" 0 A_SetUserVar(user_scale_Gosimer, 60)
"####" "#" 0 A_SetUserVar(user_scale_MaximusPrime, 80)
goto Idle
}
}
// --------------------------------
// Clientside Pickup Transformation
// --------------------------------
script "SET_PICKUP_SPRITE" (void) CLIENTSIDE
{
if(ConsolePlayerNumber() == -1)
terminate;
int oldTID = ActivatorTID();
int newTID = UniqueTID();
Thing_ChangeTID(0, newTID);
SetActivatorToPlayer(ConsolePlayerNumber());
SetActivator(0, AAPTR_PLAYER_GETCAMERA);
str playerClassName = GetActorClass(0);
str scaleVar = StrParam(s:"user_scale_", s:playerClassName);
SetActivator(newTID);
SetActorState(0, "SetScale", false);
bool stateSet = false;
if(CheckFlag(0, "DROPPED"))
{
str droppedState = StrParam(s:playerClassName, s:"Dropped");
stateSet = SetActorState(0, droppedState, false);
}
if(!stateSet)
stateSet = SetActorState(0, playerClassName, false);
if(!stateSet)
SetActorState(0, "Base", false);
// Hack to bypass the fact that Zan can't use A_SetScale from the client side on non-clientsided actors.
if(GetUserVariable(0, scaleVar) > 0)
{
SetActorProperty(0, APROP_ScaleX, GetUserVariable(0, scaleVar)*0.01);
SetActorProperty(0, APROP_ScaleY, GetUserVariable(0, scaleVar)*0.01);
}
else if(GetUserVariable(0, "user_scale_Base") > 0)
{
SetActorProperty(0, APROP_ScaleX, GetUserVariable(0, "user_scale_Base")*0.01);
SetActorProperty(0, APROP_ScaleY, GetUserVariable(0, "user_scale_Base")*0.01);
}
Thing_ChangeTID(0, oldTID);
}
// in another file...
if(p == consolePlayer)
{
if(SetActivator(0, AAPTR_PLAYER_GETCAMERA))
{
int pCam = PlayerNumber();
if(pCam != -1)
{
if(PlayerClass(pCam) != lastCameraPlayerClass)
{
SetActorState(PICKUP_SPRITE_TAG, "RecheckSprite", 0);
lastCameraPlayerClass = PlayerClass(pCam);
}
}
}
}