Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Discussions about Coding and Scripting
1337GameDev
Skilled
Posts: 196
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by 1337GameDev » Sun Feb 06, 2022 6:12 am

I have a mutator that will replace certain actors for others (it's not a direct class->class map, so basic replacement fucntions won't work), and I want to guarantee that the actor I spawn in for a particular actor will be ignored by other mutators.

I tried using Mutator.AlwaysKeep and specify this actor, but given how mutator code is triggered, spawning an actor will IMMEDIATELY call the mutator chain of "CheckReplacement" (or the gameinfo's function to do so) before any code after the Spawn code will execute (so I can't store the newly spawned actor in an array/tag it somehow).

How can I skip these kinds of calls?

I can't use a "tag" set at spawn time, as the tag doesn't seem to be set even if I provide the tag in the spawn function call. Also, tags could be set by other classes and I don't want to interfere. Owner seems iffy, as that could change how other code works too.

How can I run code after the normal mutator chain of "CheckReplacement" on map actors, and then have newly spawned actors be ignored by subsequent immediate calls for Mutator.CheckReplacement. AlwaysKeep seems to not work reliably, as I can't "tag" these spawned actors or store them in an array and check them in Mutator.AlwaysKeep.....

Ideas? I would prefer my code to execute before players spawn, and spectators can be present.

Ideas? I'm kind of stumped on this..... :noidea

User avatar
sektor2111
Godlike
Posts: 5871
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by sektor2111 » Sun Feb 06, 2022 6:38 am

If you look at "KeepIt" mutator it uses "AlwaysKeep" for holding certain item and to not be replaced by "CheckReplacement" - this is based on original rules. If these rules are changed, or something does a replacement in other way then... bad effects can be seen.
In other hand if you want to have certain type of Weapon/Actor perhaps claiming it True in top of replacements, might be a good solution for preventing next executions. I'm only assuming these as long as I don't see what your code does. A secondary thing might be "bGameRelevant" for said actor - only if original rules are respected. Also you can take in account that if an actor is really evil and coder wants it removed it might be destroyed later or deactivated in different formats (Disabling touch, moving it into void, disabling triggering and removing events, tags, then hiding it, etc).
Another problem when more mutators are chained is their rules in taking control, if "evil" wants to destroy something it will do that. In my "trip" through UT codes I recall a mutator (but not its name) capable to go itself in top as BaseMutator taking full control over entire stage and usually a BaseMutator is very powerful. To not forget order of Mutators - it's important to see first one what does it do and tracking if all of them are going into the same direction or else...

1337GameDev
Skilled
Posts: 196
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by 1337GameDev » Sun Feb 06, 2022 7:50 am

Well what cani do to stop this specific actor from being replaced by another mutator?

Eg: if want to spawn an unreal 1 dispersion pistol as a basic example. There's a mutator / code that replaces it before / after my code, but I only want the one I spawned to not be replaced.

How can that be done, as an example that exists?

Also, is there a way to run code before players spawn, but after all "check replacement" code of mutators have ran?

Not really sure....

Maybe I could force my mutator to be last in the chain? But idk how that's done

Buggie
Godlike
Posts: 1483
Joined: Sat Mar 21, 2020 5:32 am

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by Buggie » Sun Feb 06, 2022 9:21 am

Just after spawn set bGameRelevant to true.

1337GameDev
Skilled
Posts: 196
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by 1337GameDev » Sun Feb 06, 2022 9:43 am

Buggie wrote:
Sun Feb 06, 2022 9:21 am
Just after spawn set bGameRelevant to true.
It gets replaced IMMEDIATELY before any code after the spawn call executes.

I have a simple if statement after the spawn call to check if the result of spawn is None, and it is.

🤷‍♂️

Buggie
Godlike
Posts: 1483
Joined: Sat Mar 21, 2020 5:32 am

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by Buggie » Sun Feb 06, 2022 10:07 am

Then use v469b and UTrace for log what actually happens.
AFAIK mutator replace not so fast. Maybe I am wrong.
But it more look like SpawnNotify work.

Also you can subclass item and change defaults.
Or change defaults before spawn, and after restore back, so this get you set bGameReleavant on early stage.

User avatar
Barbie
Godlike
Posts: 2393
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by Barbie » Sun Feb 06, 2022 12:57 pm

Buggie wrote:
Sun Feb 06, 2022 10:07 am
Or change defaults before spawn, and after restore back, so this get you set bGameReleavant on early stage.
That's exactly the way I do this. Because I also want to modify some Actors properties after I have spawned it, I have to prevent a possible replacement by any other code. After I have finished changing properties, I pass that Actor to the relevance chain of Game Controller. Additionally I remove collision before spawning to have the Actor spawned in each and every case.

Example:

Code: Select all

var Actor SpawnedActors[64]; // temporary list for spawned Actors that have not been passed to IsRelevant() yet
var int   SpawnedActorsCount;
...
	TR = Trigger(SpawnActorToList(class'Trigger', LogLevel, , , TD.Location));
	if (TR !=None)
	{
		// doing st with this Actor
		...
	}
...
	while (SpawnedActorsCount > 0)
	{
		CheckActorRelevance(SpawnedActors[--SpawnedActorsCount]);
		SpawnedActors[SpawnedActorsCount] = None;
	}
additional needed code stuff - read from bottom to top
Show

Code: Select all

function bool CheckActorRelevance(Actor A) {
/******************************************************************************
Checks if *A* is relevant and destroys it, if not.
The result of Level.Game.IsRelevant() is returned.
Intended for use with hacked bGameRelevant=True-spawned Actors.
******************************************************************************/
local bool result;

	result = Level.Game.IsRelevant(A);
	if ( ! result && ! A.bDeleteMe)
		A.Destroy();
	return result;
}



function Actor SpawnActor(class<Actor> SpawnClass, ELogType LogLevel, optional Actor SpawnOwner, optional name SpawnTag, optional Vector SpawnLocation, optional Rotator SpawnRotation) {
/******************************************************************************
Tries to spawn an Actor of the given *SpawnClass* with the properties
bBlockActors=false, bCollideWorld=false to avoid collision while placing and
bGameRelevant = true to avoid passing the new created Actor to the relevance
chain. Always call *CheckActorRelevance()* later to have the Actor passed the
relevance chain.

Changed default properties are reset to their previous values after the actor
has spawned.
Either the new Actor is returned or None if spawn failed.
******************************************************************************/
local Actor result;
local bool bBlockActorsDefault;
local bool bGameRelevantDefault;
local bool bCollideWorldDefault;

	bBlockActorsDefault = SpawnClass.Default.bBlockActors;
	bGameRelevantDefault = SpawnClass.Default.bGameRelevant;
	bCollideWorldDefault = SpawnClass.Default.bCollideWorld;
	SpawnClass.Default.bBlockActors = false;
	SpawnClass.Default.bGameRelevant = true;
	SpawnClass.Default.bCollideWorld = false;
	result = spawn(SpawnClass, SpawnOwner, SpawnTag, SpawnLocation, SpawnRotation);
	SpawnClass.Default.bBlockActors = bBlockActorsDefault;
	SpawnClass.Default.bGameRelevant = bGameRelevantDefault;
	SpawnClass.Default.bCollideWorld = bCollideWorldDefault;

	if (result == None)
		Logger(LOG_Error, "SpawnActor", "could not spawn" @ SpawnClass @ "at location" @ SpawnLocation);
	else
	{
		if ( ! class'SharedCode'.static.SetCollisionEx(result, result.bCollideActors, SpawnClass.Default.bBlockActors, result.bBlockPlayers))
			Logger(LOG_Error, "SpawnActor", "SetCollisionEx faild for" @ result);
		//result.SetCollision(result.bCollideActors, SpawnClass.Default.bBlockActors, result.bBlockPlayers);
		result.bGameRelevant = bGameRelevantDefault;
		result.bCollideWorld = bCollideWorldDefault;
		Logger(LOG_Verbose, "SpawnActor", "spawned" @ result @ "at location" @ SpawnLocation);
	}
	return result;
}




function Actor SpawnActorToList(class<Actor> SpawnClass, ELogType LogLevel, optional Actor SpawnOwner, optional name SpawnTag, optional Vector SpawnLocation, optional Rotator SpawnRotation) {
/******************************************************************************
Calls *SpawnActor()* and adds the spawned Actor to the list *SpawnedActors*.
Always call *CheckActorRelevance()* later to insert the Actor into the
relevance chain again.
******************************************************************************/
local Actor result;

	result = SpawnActor(SpawnClass, LogLevel, SpawnOwner, SpawnTag, SpawnLocation, SpawnRotation);
	if (result != None)
		if (SpawnedActorsCount >= ArrayCount(SpawnedActors))
			warn("SpawnedActorsCount exceeds limit of" @ ArrayCount(SpawnedActors));
		else
			SpawnedActors[SpawnedActorsCount++] = result;

	return result;
}
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett

1337GameDev
Skilled
Posts: 196
Joined: Thu Apr 16, 2020 3:23 pm
Personal rank: GameDev

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by 1337GameDev » Mon Feb 07, 2022 6:22 am

I'm still not sure how to implement this to stop Level.Game.BaseMutator that has code to replace u1 weapons with ut variants. I thought of shimming my mutator in front of all others, but don't know when i can do that before the base mutator code.....

A clear example of this is Botpack.DMMutator.

Looking at the code, it doesn't check ANY flags, except for if it's an inventory and at 0,0,0. So maybe I could spawn the object, and then move it to the correct location later?

Also, what's the appropriate time to execute code for a mutator after the game is ready, but before players have spawned? My guess is in PostBeginPlay, but this is before the mutator chain is established.

User avatar
Barbie
Godlike
Posts: 2393
Joined: Fri Sep 25, 2015 9:01 pm
Location: moved without proper hashing

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by Barbie » Mon Feb 07, 2022 1:45 pm

1337GameDev wrote:
Mon Feb 07, 2022 6:22 am
I'm still not sure how to implement this to stop Level.Game.BaseMutator that has code to replace u1 weapons with ut variants. I thought of shimming my mutator in front of all others, but don't know when i can do that before the base mutator code.....
Have a look at nexgen: it fights for the position of being the first in the mutator chain and replaces the existing base mutator.

If your mutator don't want to be in this battle, I'd do the following: wait until all old weapons are replaced and then replace them back by setting "SpawnClass.default.bGameRelevant = true" before spawning to prevent re-replacing by CheckReplacement() functions.
1337GameDev wrote:
Mon Feb 07, 2022 6:22 am
Also, what's the appropriate time to execute code for a mutator after the game is ready, but before players have spawned? My guess is in PostBeginPlay, but this is before the mutator chain is established.
Maybe Chain Of Events At Level Startup helps? It is for UT2004, but most parts are same as in UT.
In my Actors I have several events where it can be activated. Here from my MapPatcher:

Code: Select all

enum EPatchStage {
	PS_PreBeginPlayStart,
	PS_PreBeginPlay,
	PS_PostBeginPlay,
	PS_State,
	PS_GameEnded,
};
From my experience game is ready without having player spawned is when the Actor enters the "Auto State" code.
"Multiple exclamation marks," he went on, shaking his head, "are a sure sign of a diseased mind." --Terry Pratchett

User avatar
sektor2111
Godlike
Posts: 5871
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Spawn actor and have it be ignored by mutator "CheckReplacement" reliably?

Post by sektor2111 » Mon Feb 07, 2022 3:46 pm

As I recall LoathSome (R.I.P...) did a mutator capable to keep old weapons on purpose to use them and capturing moment when player is firing such a weapon without net code. That mutator (called if I well recall U1UT or something like that) is usable with DMMutator. I did not look at it I have to admit... because I'm using another private DM game having its own BaseMutator which has a bit of add-ons but... leaving original weapons alone - any weapon except useless ones (quadshot more exactly). In other hand, the only way to react faster than PreBeginPlay for changing certain properties is "InitGame()" but that's part of GameInfo not "Mutator" and then... here only a custom game-type has full control over everything.
As I know so far, if something it's in "AlwaysKeep" it should stay doesn't matter what other mutators want to do - except very evil ones doing replacements delayed and discarding "rules", injecting their own rules. I'm saying this because I wrote such thing due to issues caused by certain stock items conflicting each-other, in the same map and doing a recursive crash. Why ? Because if a map has embedded a mutator attempting to "AlwaysKeep" stuff, I'll destroy plain rules establishing my rules.

The rest means LOG lines added for tracking what's happening and figuring chain of relevance checks. A null mutator having logs will print which function is first, then testing with whatever class in a Test-map.