Jump to content
  • 0

How to make randomizer that replaces all monsters rather than individual ones?


LukeGaming

Question

So far I have been using randomizer like this:

eee.png.483d230e3fe35b431bf4621cd5b101ca.png

And it works fine but once you add too many monster types to single replacer levels become a bit too messy. I'm thinking about something that would replace given enemy type with one other randomly chosen one throught entire level. What method would you use for that? Do you know any randomizer that does something like this without very complicated method? 

 

Share this post


Link to post

10 answers to this question

Recommended Posts

  • 0

Definitely go the event handler route, not just because it's way more sane than anything actor-related and designed precisely for replacing stuff, but also because it is an officially supported way of having a global state, which is what you need if you want a "choose one of (arch-vile, chaingunner, rev) -> every imp becomes that one enemy" type of thing. Any actor-code method will run into this wall of "need to define who gets replaced by what once globally and read it in each individual actor", which you'd need an event handler for, which means you might as well go all the way.

 

Here's a rough example where a replacee->replacer mapping is populated first (NewGame event happens before CheckReplacement) based on curated lists of what we want, and is then applied by CheckReplacement using real in-level thing classes to find appropriate replacement in the mapping.

version "4.11.0"

class RandomReplacer : EventHandler {
	Map<String, String> replaceMap;
	
	static const String[] replacees = {
		"ChaingunGuy",
		"DoomImp",
		"ShotgunGuy",
		"Zombieman"
	};
	
	static const String[] replacers = {
		"SuperImp",
		"Wimp",
		"MyCaco"
	};
	
	override void NewGame(){
		replaceMap.Clear();
		for(int v = 0; v < replacees.Size(); v++){
			replaceMap.Insert(replacees[v], replacers[Random(0, replacers.Size()-1)]);
		}
	}
	
	override void CheckReplacement (ReplaceEvent e){
		let cls = e.Replacee.GetClassName();
		if (replaceMap.CheckKey(cls)){
			e.Replacement = replaceMap.Get(cls);
		}
	}
}

class Wimp : DoomImp {
    Default { Health 1; }
}

class SuperImp : DoomImp {
	Default { Health 1000; }
}

class MyCaco : Cacodemon {
	Default { Health 2000; }
}

Obviously you can add other logic - chance to not be replaced at all, multiple lists based on replacee classes (so e.g. imps only get replaced by imp-like types your monster pack has) etc.

Share this post


Link to post
  • 0
On 12/14/2023 at 1:40 PM, LukeGaming said:

So far I have been using randomizer like this:

eee.png.483d230e3fe35b431bf4621cd5b101ca.png

And it works fine but once you add too many monster types to single replacer levels become a bit too messy. I'm thinking about something that would replace given enemy type with one other randomly chosen one throught entire level. What method would you use for that? Do you know any randomizer that does something like this without very complicated method? 

 

Why not build a master replacer object?

class MonstaReplacer : RandomSpawner

{

default

   {

      DropItem "monsta1";

      DropItem "monsta2";

      DropItem "monsta3";

      DropItem "monsta4";

   }

}

 

class ImpReplacer : MonstaReplacer replaces DoomImp

{

}

 

class HellknightReplacer : MonstaReplacer replaces HellKnight

{

}

 

class ZombieReplacer : MonstaReplacer replaces Zombieman

{

}

 

...etc, etc, etc.

Using this MonstaReplacer class, we've written all our randomness we wanted in it. We then

are making new derivative classes which inherit everything in that MonstaReplacer class and

make no changes due to the emptiness between those curly braces.

 

Hope this helps!

 

 

Share this post


Link to post
  • 0
On 12/31/2023 at 10:48 PM, prfunky said:

 

Why not build a master replacer object?

class MonstaReplacer : RandomSpawner

{

default

   {

      DropItem "monsta1";

      DropItem "monsta2";

      DropItem "monsta3";

      DropItem "monsta4";

   }

}

 

class ImpReplacer : MonstaReplacer replaces DoomImp

{

}

 

class HellknightReplacer : MonstaReplacer replaces HellKnight

{

}

 

class ZombieReplacer : MonstaReplacer replaces Zombieman

{

}

 

...etc, etc, etc.

Using this MonstaReplacer class, we've written all our randomness we wanted in it. We then

are making new derivative classes which inherit everything in that MonstaReplacer class and

make no changes due to the emptiness between those curly braces.

 

Hope this helps!

 

 

It sounds like it could work, I'll it. Thanks in advance!

Share this post


Link to post
  • 0
On 12/31/2023 at 10:48 PM, prfunky said:

 

Why not build a master replacer object?

class MonstaReplacer : RandomSpawner

{

default

   {

      DropItem "monsta1";

      DropItem "monsta2";

      DropItem "monsta3";

      DropItem "monsta4";

   }

}

 

class ImpReplacer : MonstaReplacer replaces DoomImp

{

}

 

class HellknightReplacer : MonstaReplacer replaces HellKnight

{

}

 

class ZombieReplacer : MonstaReplacer replaces Zombieman

{

}

 

...etc, etc, etc.

Using this MonstaReplacer class, we've written all our randomness we wanted in it. We then

are making new derivative classes which inherit everything in that MonstaReplacer class and

make no changes due to the emptiness between those curly braces.

 

Hope this helps!

 

 

Put it together like this today:

157185984_2024-01-1110_16_43-Window.png.20c6ae95705b386b77dac0d1706d2fb8.png

And checked it in practice, sadly it still replaces monsters on individual basis.

Share this post


Link to post
  • 0

Try this :)

 

Full disclaimer I'm not a ZScript wizard so there MAY be issues in the script itself. Please check before attempting (As I have NOT tested it either.)

class ImpReplacer : DoomImp replaces DoomImp
{
	Default
	{
	}
		States
	{
		Spawn:
		TNT1 A 0;
		TNT1 A 1 NoDelay A_Jump(256, Spawn1, Spawn2, Spawn3);
		TNT1 A 0 A_Remove(AAPTR_DEFAULT);

		Spawn1:
		TNT1 A 0 SpawnItemEx("CacoImp", 0, 0, 0, 0, 0, 0, 0, SFX_TRANSFERSPECIAL|SFX_TRANSFERAMBUSHFLAG|SFX_NOPOINTERS, 0, 0);
		goto Death;
		Spawn2:
		TNT1 A 0 SpawnItemEx("HellImp", 0, 0, 0, 0, 0, 0, 0, SFX_TRANSFERSPECIAL|SFX_TRANSFERAMBUSHFLAG|SFX_NOPOINTERS, 0, 0);
		goto Death;
		Spawn3:
		TNT1 A 0 SpawnItemEx("BaronImp", 0, 0, 0, 0, 0, 0, 0, SFX_TRANSFERSPECIAL|SFX_TRANSFERAMBUSHFLAG|SFX_NOPOINTERS, 0, 0);
		goto Death;

		Death:
		TNT1 A 0 A_Remove(AAPTR_DEFAULT);
	}
}

 

Edited by Wo0p

Share this post


Link to post
  • 0
1 hour ago, Wo0p said:

Try this :)

 

Full disclaimer I'm not a ZScript wizard so there MAY be issues in the script itself. Please check before attempting (As I have NOT tested it either.)

 

I considered using actor states before, but this will also replace each individual monster with random one rather than replacing each with ONE randomly chosen type.

Share this post


Link to post
  • 0
1 hour ago, LukeGaming said:

I considered using actor states before, but this will also replace each individual monster with random one rather than replacing each with ONE randomly chosen type.

 

:o But

TNT1 A 1 A_Jump(256, Spawn1, Spawn2, Spawn3);

 

jumps every time (the 256) to ONE of the chosen Spawn1, Spawn2, Spawn3. Therefore, spawning a randomly chosen one.

 

Am I misunderstanding what you want to do?

Share this post


Link to post
  • 0
On 1/11/2024 at 12:23 PM, Wo0p said:

Try this :)

 

Full disclaimer I'm not a ZScript wizard so there MAY be issues in the script itself. Please check before attempting (As I have NOT tested it either.)

 

There's a much more elegant way of doing this in ZScript using an event handler. That's basically how I handled the replacement stuff in Vandomizer. Small snippet of the code I used (modified for brevity):

class SpawningHandler : EventHandler
{
	override void CheckReplacement(ReplaceEvent e)
	{	
		if(e.Replacee == 'ZombieMan' ||
		e.Replacee == 'ShotgunGuy' ||
		e.Replacee == 'ChaingunGuy' ||
		e.Replacee == 'DoomImp')
		{
			switch(random[Replacement](0,3))
			{
			case 0:
				e.Replacement = 'FixedZombieMan';
				break;
			case 1:
				e.Replacement = 'FixedShotgunGuy';
				break;
			case 2:
				e.Replacement = 'FixedChaingunGuy';
				break;
			case 3:
				e.Replacement = 'DoomImp';
				break;
			}
		}
	}
}

The Vandomizer code in its entirety is a mess and a half, due in part to it basically being one giant blob of switch-case and if-else statements, and there are probably more elegant ways to code the replacement logic, but it works so eh.

Share this post


Link to post
  • 0
5 hours ago, Bryan T said:

I think what you're wanting is what this mod does. Look at the code here.

 

https://forum.zdoom.org/viewtopic.php?t=57310

I aped/copy-pasted some code from it and even got it to work somehow, but my bastardized version ended up still making individual replacements, lol

3 hours ago, MFG38 said:

 

There's a much more elegant way of doing this in ZScript using an event handler. That's basically how I handled the replacement stuff in Vandomizer. Small snippet of the code I used (modified for brevity):

The Vandomizer code in its entirety is a mess and a half, due in part to it basically being one giant blob of switch-case and if-else statements, and there are probably more elegant ways to code the replacement logic, but it works so eh.

 

2 hours ago, Doomy__Doom said:

Definitely go the event handler route, not just because it's way more sane than anything actor-related and designed precisely for replacing stuff, but also because it is an officially supported way of having a global state, which is what you need if you want a "choose one of (arch-vile, chaingunner, rev) -> every imp becomes that one enemy" type of thing. Any actor-code method will run into this wall of "need to define who gets replaced by what once globally and read it in each individual actor", which you'd need an event handler for, which means you might as well go all the way.

 

Here's a rough example where a replacee->replacer mapping is populated first (NewGame event happens before CheckReplacement) based on curated lists of what we want, and is then applied by CheckReplacement using real in-level thing classes to find appropriate replacement in the mapping.


version "4.11.0"

class RandomReplacer : EventHandler {
	Map<String, String> replaceMap;
	
	static const String[] replacees = {
		"ChaingunGuy",
		"DoomImp",
		"ShotgunGuy",
		"Zombieman"
	};
	
	static const String[] replacers = {
		"SuperImp",
		"Wimp",
		"MyCaco"
	};
	
	override void NewGame(){
		replaceMap.Clear();
		for(int v = 0; v < replacees.Size(); v++){
			replaceMap.Insert(replacees[v], replacers[Random(0, replacers.Size()-1)]);
		}
	}
	
	override void CheckReplacement (ReplaceEvent e){
		let cls = e.Replacee.GetClassName();
		if (replaceMap.CheckKey(cls)){
			e.Replacement = replaceMap.Get(cls);
		}
	}
}

class Wimp : DoomImp {
    Default { Health 1; }
}

class SuperImp : DoomImp {
	Default { Health 1000; }
}

class MyCaco : Cacodemon {
	Default { Health 2000; }
}

Obviously you can add other logic - chance to not be replaced at all, multiple lists based on replacee classes (so e.g. imps only get replaced by imp-like types your monster pack has) etc.

Yep, this is what I was looking for. I ended up going with @Doomy__Doom version, and it does exacly what I needed. Code is a bit over my understanding of scripts, but I think I'll work it out eventually.

Btw, If it wasn't for error that I got I still wouldn't know there are different versions of zscript and that you can set up version at beginning of the script.

Anyway, thanks for help guys!

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...