Ladder
From ZDoom Wiki
|
Assumed Knowledge
It is assumed that before you begin this tutorial, you are familiar with the following concepts:
- Thing executed specials
- ThrustThingZ
- SetPlayerProperty
- SetLineSpecial
- SetLineBlocking
- GetActorX
- GetActorY
- GetActorZ
Understanding these concepts is vital to understanding the following tutorial.
Past Examples
Aside from the "invisible stairs" approach, which can now be considered a messy and antiquated "hack", there are other examples of how to create a ladder. However, the following two examples both have their benefits and flaws.
Helms Deep's Ladder
The ladder in Helms Deep was simply a repeatable ThrustThingZ special placed on the line which was triggered when the player pushed on it.
ThrustThingZ(0, 16, 0, 0);
The problem arises when pressing back to climb down, resulting in falling off the ladder completely.
Little White Mouse's Ladder
This example uses what has been dubbed a "flying sector"; that is, using the "Actor Enters Sector" and "Actor Leaves Sector" Thing executed specials to set and remove the APROP_FLY property:
script 1 (int Ladder) {
if (Ladder == 1){SetPlayerProperty (0, 1, PROP_FLY);}
if (Ladder == 2){SetPlayerProperty (0, 0, PROP_FLY);}
}
This method is effective in that you can turn around & fire from any point on the ladder, so that you don't just drop when going down.
One limitation of this method is that the player is required to use their "jump / fly up" and "crouch / fly down" keys to move up and down the ladder, and if you remove the air-control entirely it won't work at all.
Also, in this example, if there is no ceiling, the player can continue to fly as high as they please.
Combining the Methods
Using a combination of these two examples and the inclusion of a few "fixes", you can achieve a nice ladder effect which combines the best of both methods.
"Jumping" to climb the ladder
Rather than having the fly property activated by the sector things, use the Helms Deep approach and have it activated when the player pushes up against the line.
Secondly, when the player pushes the line include the ThrustThingZ code so that they will not have to use the "jump" key to climb upwards; rather, simply pushing forward will allow them to climb the ladder.
This method combines the ability to climb by simply pressing forward and turn around & fire from any point on the ladder.
This creates the problem of not being able to "climb down" the ladder and the issue of how to turn the fly property off if the player simply walks away from the line. However, both these problems will be addressed later.
No ceiling
Using a quick IF statement to check the player's height, you can determine whether or not to maintain the fly property or turn it off:
if(GetActorZ(ActivatorTID()) >= 0 && GetActorZ(ActivatorTID()) < 512<<16)
{
SetPlayerProperty(0, 1, PROP_FLY);
ThrustThingZ(0, 16, 0, 0);
}
else
{
SetPlayerProperty(0, 0, PROP_FLY);
}
Where "0" is the bottom of the ladder and "512" is the top.
Note that the player's height is in his shoes. If the height of the top platform floor is 384, that's what your player's height will be when he stands on it.
Inability to climb down
This issue should be addressed with another line placed adjacent to the "ladder" line, which acts inversely to its counterpart. In other words, this invisible line would use a ThrustThingZ to force the player downward. Place this "down ladder" about 36 map units away from the ladder line for the best effect.
Attention needs to be paid to this line in particular regarding when it should and should not be "passable."
The way to achieve this is to use the height checking statement from the previous solution to also use SetLineBlocking on the "down ladder."
Obviously, the player is still going to want to walk away from the ladder at the lowest point. However, their Z-height will still be greater than 0, meeting the condition specified in the previous code, so an extra "Else If" statement is required.
if(GetActorZ(ActivatorTID()) > 64<<16 && GetActorZ(ActivatorTID()) < 512<<16)
{
SetPlayerProperty(0, 1, PROP_FLY);
ThrustThingZ(0, 16, bool up/down, 0);
SetLineBlocking(lineid, BLOCK_CREATURES);
}
else if(GetActorZ(ActivatorTID()) >= 0 && GetActorZ(ActivatorTID()) < 64<<16)
{
SetPlayerProperty(0, 1, PROP_FLY);
ThrustThingZ(0, 16, bool up/down, 0);
SetLineBlocking(lineid, BLOCK_NOTHING);
}
else
{
SetPlayerProperty(0, 0, PROP_FLY);
SetLineBlocking(lineid, BLOCK_NOTHING);
}
Note that the property BLOCK_CREATURES is used so that projectiles can still pass through it.
Note that the third argument of ThrustThingZ has been changed to a bool. It is recommended you declare an integer for this argument which is defined by an argument of whichever line the player is pushing; this elminates the need for multiple scripts.
Now, you will need this "down ladder" line to have a lineid, but also to execute the special when bumped. To do this, give the line an ID in you map editor, and then use an open script to assign the special:
Script 999 OPEN
{
SetLineSpecial(lineid,80,1,0,1);
}
Also, when climbing on to the ladder from the top to climb down, the "down ladder" will of course not be activated since you haven't bumped the ladder to do so. Here is where an "Actor Enters Sector" thing works well to activate the following script:
if(GetActorZ(ActivatorTID()) > 64<<16 && GetActorZ(ActivatorTID()) <= 512<<16)
{
SetLineBlocking(downladderline, BLOCK_CREATURES);
}
else
{
SetLineBlocking(downladderline, BLOCK_NOTHING);
}
This will make sure that the "down ladder" is set to blocking; otherwise, you'll just jump off the top of the platform.
Removing the "fly" property
Now the Thing executed specials of "Actor Leaves Sector" should be used, as the previous code, as well as the dummy "down ladder", have already ensured that the player can only leave the sector when they are at the top or bottom of the ladder, at which point the fly property is no longer necessary. The "Actor Leaves Sector" thing can remove this property itself, without calling another script. The special should be "191: SetPlayerProperty", and the arguments should be "0,0,3".
It is advisable to leave a "buffer area" around the ladder line, as seen in the example map. Otherwise it is possible to quickly strafe across the ladder line and retain the fly property (i.e. the "leaves sector" thing special doesn't activate).
Abandoning the ladder
Finally, it's clear to see that, trapped by the linedefs, it would be impossible for the player to simply "jump off" the ladder.
This can be resolved using a "Player uses sector" thing to execute the following:
SetPlayerProperty(0, 0, PROP_FLY);
SetLineBlocking(downladderline, BLOCK_NOTHING);
Finished Script
Your completed script should look something like this:
#include "zcommon.acs"
int downladderline=1; //<--- Make this integer equal the lineid of your "down ladder".
Script 1 (int updown)
{
if(GetActorZ(ActivatorTID()) > 64<<16 && GetActorZ(ActivatorTID()) < 512<<16)
{
SetPlayerProperty(0, 1, PROP_FLY);
ThrustThingZ(0, 16, updown, 0);
SetLineBlocking(downladderline, BLOCK_CREATURES);
}
else if(GetActorZ(ActivatorTID()) >= 0 && GetActorZ(ActivatorTID()) < 64<<16)
{
SetPlayerProperty(0, 1, PROP_FLY);
ThrustThingZ(0, 16, updown, 0);
SetLineBlocking(downladderline, BLOCK_NOTHING);
}
else
{
SetPlayerProperty(0, 0, PROP_FLY);
SetLineBlocking(lineid, BLOCK_NOTHING);
}
}
Script 2 (void)
{
SetPlayerProperty(0, 0, PROP_FLY)
SetLineBlocking(downladderline, BLOCK_NOTHING);
}
Script 3 (void)
{
if(GetActorZ(ActivatorTID()) > 64<<16 && GetActorZ(ActivatorTID()) <= 512<<16)
{
SetLineBlocking(downladderline, BLOCK_CREATURES);
}
else
{
SetLineBlocking(downladderline, BLOCK_NOTHING);
}
}
Script 999 OPEN
{
SetLineSpecial(downladderline,80,1,0,1);
}

