Using Action Special tricks

From ZDoom Wiki
Jump to navigation Jump to search

Warning: This guide assumes you are already familiar with creating DECORATE objects and placing them in maps.

Overview

Most people prefer to use ACS when it comes to altering levels. This includes, but is not limited to:

  • Performing simple level alterations
  • Dealing with TIDs
  • Creating complex systems for handling events
  • Handling script-only functions

If you are looking to minimize the amount of scripts running on your maps and perhaps use an alternative route for making specials happen, then this guide is for you. Bear in mind, this is going to set up a general example of what you can do, and assume you can take it from here to experiment with by yourself or with others.

Recommended ACS Usage

Keep in mind, this guide is only meant to help minimize the need for using scripts, not eliminate it entirely. There are still some things which can only be done by scripts such as ACS functions. Some things are better off being done by ACS because, in fact, it can be used to simplify effect making. I recommend the following example for placing hand-made map controllers so you don't need to add DoomEd numbers to them in DECORATE.

You will need to place a MapSpot thing inside your map and set its tag to 2 for this example.

Script 1 OPEN
{
	SpawnSpotForced("HTC2SectorLightControllerSlow",2,1001,0);
}

DECORATE controllers

Light controller example

These are what can be called "controller" actors. If you look at the following example, it is made solely for the purpose of causing all sectors stored within the user_sectors array to have their sector colors changed.

Actor HTC2SectorLightControllerSlow
{
 	var int user_size;
	var int user_max;
	var int user_sectors[2];
	var int user_colorprog;
	+NOINTERACTION
	RenderStyle None
	States
	{
	Spawn:
		TNT1 A 0
		TNT1 A 0 A_SetUserArray("user_sectors",0,1)
		TNT1 A 0 A_SetUserArray("user_sectors",1,33)
		TNT1 A 0 A_SetUserVar("user_max",2)
		TNT1 A 0 A_SetUserVar("user_size",0)
		TNT1 A 0 A_SetUserVar("user_colorprog",1)
	Lighting1:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],255,0,0)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting2:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],192,64,0)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting3:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],128,128,0)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting4:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],64,192,0)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting5:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],0,255,0)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting6:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],0,192,64)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting7:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],0,128,128)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting8:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],0,64,192)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting9:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],0,0,255)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting10:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],64,0,192)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting11:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],128,0,128)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	Lighting12:
		TNT1 A 0 Sector_SetColor(user_sectors[user_size],192,0,64)
		TNT1 A 0 A_SetUserVar("user_size",user_size+1)
		TNT1 A 0 A_JumpIf(user_size>=user_max,"NextCycle")
		Loop
	NextCycle:
		TNT1 A 5 A_SetUserVar("user_size",0)
		TNT1 A 0 A_SetUserVar("user_colorprog",user_colorprog+1)
		TNT1 A 0 A_JumpIf(user_colorprog>=13,"NC2")
	NextCycleCont:
		TNT1 A 0 A_JumpIf(user_colorprog==1,"Lighting1")
		TNT1 A 0 A_JumpIf(user_colorprog==2,"Lighting2")
		TNT1 A 0 A_JumpIf(user_colorprog==3,"Lighting3")
		TNT1 A 0 A_JumpIf(user_colorprog==4,"Lighting4")
		TNT1 A 0 A_JumpIf(user_colorprog==5,"Lighting5")
		TNT1 A 0 A_JumpIf(user_colorprog==6,"Lighting6")
		TNT1 A 0 A_JumpIf(user_colorprog==7,"Lighting7")
		TNT1 A 0 A_JumpIf(user_colorprog==8,"Lighting8")
		TNT1 A 0 A_JumpIf(user_colorprog==9,"Lighting9")
		TNT1 A 0 A_JumpIf(user_colorprog==10,"Lighting10")
		TNT1 A 0 A_JumpIf(user_colorprog==11,"Lighting11")
		TNT1 A 0 A_JumpIf(user_colorprog==12,"Lighting12")
	NC2:
		TNT1 A 0 A_SetUserVar("user_colorprog",1)
		Goto NextCycleCont
	}
}

But say, for example, you wanted to increase the amount of sectors it will cycle through. Pay close attention to the comments to see where the changes have occurred.

Actor HTC2SectorLightControllerSlow
{
	var int user_size;
	var int user_max;
	var int user_sectors[3]; //Increased from 2 to 3
	var int user_colorprog;
	+NOINTERACTION
	RenderStyle None
	States
	{
	Spawn:
		TNT1 A 0
		TNT1 A 0 A_SetUserArray("user_sectors",0,1)
		TNT1 A 0 A_SetUserArray("user_sectors",1,33)
		TNT1 A 0 A_SetUserArray("user_sectors",2,34) 
		//Now affects sectors tagged 34 in addition to 1 and 33.

		TNT1 A 0 A_SetUserVar("user_max",3) 
		//Increased the maximum to 3. This is required, or else the controller won't be able to change the third sector.

		TNT1 A 0 A_SetUserVar("user_size",0)
		TNT1 A 0 A_SetUserVar("user_colorprog",1)
        //...

Controller Advantages

  • Can be modified so a certain inventory check will cause it to pause or outright stop.
  • No reliance upon ACS_ExecuteAlways or ACS_ExecuteWithResult (those two functions cannot have the executed scripts terminated by any means).
  • Robust, easy to change, and saves a lot of copy/pasting time.
  • No tangling with ACS loops
  • Less script usage, meaning less scripting conflicts with additional mods (i.e. an advanced map coupled with a weapon/monster replacer mod)

Disadvantages

  • Very advanced. You need to know how to set up A_JumpIf statements with user variables/arrays, and how to change them as well.
  • Requires extreme care to set up. Use state labels at all times, and avoid offset jumping with integers at all costs to prevent confusion.
  • Cannot protect from infinite loops by default. You must always make one to prevent ZDoom from crashing. However, the full example above the changed one already has one built in for its specific conditions.
  • This applies for both ACS and DECORATE: Cannot run non-script and line-only specials.