Skip to content

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);
						}
					}
				}
			}
Edited by StrikerTheHedgefox

Merge request reports