A touch trigger: issue with Call Special and teleport

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)

A touch trigger: issue with Call Special and teleport

Postby Sir Robin » Fri May 06, 2022 10:03 am

I'm creating a touch trigger actor. That is, an actor that will execute it's special when it is touched. I thought I could do this easily with +BumpSpecial but that only seems to fire if the actor is solid. I want an actor that can be move through and trigger a special, like a trip wire or motion sensor. Why not just use a linedef trigger? Well we all know that line will fire no matter what Z, I wanted a trip that only occurs at a set Z, and could be jumped over or crouched under or whatever.

Here's the code:
Code: Select allExpand view
class MWR_TouchTrigger : actor
{
   Default
   {
      height 64;
      radius 16;
      +NOGRAVITY;
      +Special;
      MWR_TouchTrigger.TouchTimeout 35;
   }
   States
   {
      Spawn:
         UNKN A -1;//DEBUG
         Stop;
   }
   
   int TouchTimeout;
   int TouchCountdown;
   
   property TouchTimeout:TouchTimeout;
   
   override void Touch(Actor toucher)
   {
      if (TouchCountdown>0) {return;}
      console.printf(GetCharacterName().."@"..level.time..": Touched by "..toucher.GetCharacterName());//DEBUG
      toucher.A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]);
      TouchCountdown=TouchTimeout;
   }
   
   override void tick()
   {
      if (TouchCountdown>0){TouchCountdown--;}
      super.tick();
   }
}


If I set the special to open a door, it works.
If I set the special to teleport to a landing, it makes the source and destination fog but teleports the player to the trigger, not the teleport landing.

What am I doing wrong?

download
Last edited by Sir Robin on Fri May 06, 2022 3:29 pm, edited 1 time in total.
User avatar
Sir Robin
 
Joined: 22 Dec 2021
Operating System: Windows 10/8.1/8/201x 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: Intel (Modern GZDoom)

Re: A touch trigger: issue with Call Special

Postby Sir Robin » Fri May 06, 2022 3:28 pm

I try using Level.ExecuteSpecial instead of A_CallSpecial, but get the same result. I looked in the actor code and A_SetSpecial calls Level.ExecuteSpecial, so no functional difference.
User avatar
Sir Robin
 
Joined: 22 Dec 2021
Operating System: Windows 10/8.1/8/201x 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: Intel (Modern GZDoom)

Re: A touch trigger: issue with Call Special and teleport

Postby Player701 » Wed May 11, 2022 12:45 pm

This has something to do with the way the movement code is written. I've tried running it in a debugger, and it seems that the player's movement for the current tick is processed after the teleport special is executed, overriding the player's coordinates as a result. I'm not yet sure if these is a way to work around this. See further below for a workaround solution.

Sir Robin wrote:Well we all know that line will fire no matter what Z, I wanted a trip that only occurs at a set Z, and could be jumped over or crouched under or whatever.

Consider using an ACS script with GetActorZ and/or GetActorFloorZ for line triggers to achieve the desired effect.
Last edited by Player701 on Fri May 13, 2022 9:25 am, edited 1 time in total.
User avatar
Player701
 
 
 
Joined: 13 May 2009
Location: Russia
Discord: Player701#8214
Operating System: Windows 10/8.1/8/201x 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: nVidia with Vulkan support

Re: A touch trigger: issue with Call Special and teleport

Postby Player701 » Fri May 13, 2022 9:24 am

Workaround for the original setup: use an inventory item that will call the special as soon as it appears in the owner's inventory, and then destroy itself. This delays the call until the player's movement for the current tick has been fully processed.

Code: Select allExpand view
class SpecialInv : Inventory
{
    override void DoEffect()
    {
        Owner.A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]);
        Destroy();
    }
}

Add the following code to MWR_TouchTrigger:

Code: Select allExpand view
private void GiveSpecialInv(Actor who)
{
    let si = Inventory(Spawn('SpecialInv'));

    if (si != null)
    {
        si.special = special;
       
        for (int i = 0; i < 5; i++)
        {
            si.args[i] = args[i];
        }

        if (!si.CallTryPickup(who))
        {
            si.Destroy();
        }
    }
}

and replace toucher.A_CallSpecial(...) with

Code: Select allExpand view
GiveSpecialInv(toucher);
User avatar
Player701
 
 
 
Joined: 13 May 2009
Location: Russia
Discord: Player701#8214
Operating System: Windows 10/8.1/8/201x 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: nVidia with Vulkan support

Re: A touch trigger: issue with Call Special and teleport

Postby Sir Robin » Fri May 13, 2022 12:19 pm

Player701 wrote:This has something to do with the way the movement code is written. I've tried running it in a debugger, and it seems that the player's movement for the current tick is processed after the teleport special is executed, overriding the player's coordinates as a result. I'm not yet sure if these is a way to work around this. See further below for a workaround solution.

Thanks for looking into this. Is it worth reporting as a bug / feature request? I would think that a teleport function call should reset any movement logic in the actor(s) it is affecting.

Player701 wrote:Consider using an ACS script with GetActorZ and/or GetActorFloorZ for line triggers to achieve the desired effect.

I understand there are different ways to do this, I was experimenting to see pros/cons of different approaches. What I like about using an actor is the simplicity. An editor like UDB will see the teleport special, find it's target, and indicate the relationship with a line or whatever is configured to do, so it is easy to graphically understand the relationship. I could have a line special call a script and that script call a teleport and that teleport point to a destination, but now that's 3 things I have to look at manually to understand what is happening.

Player701 wrote:Workaround for the original setup: use an inventory item that will call the special as soon as it appears in the owner's inventory, and then destroy itself. This delays the call until the player's movement for the current tick has been fully processed.

Excellent! Thanks for taking the time to work this out!

I saw your first post about the timing and tried moving that special call. I didn't think to move it to an inventory item, but I did try moving it from the touch to the tick function of the trigger object and that works for the teleport issue:
Code: Select allExpand view
   bool DelayedSpecial;
   actor SpecialToucher;
   
   override void Touch(Actor toucher)
   {
      if (TouchCountdown>0) {return;}
      console.printf(GetCharacterName().."@"..level.time..": Touched by "..toucher.GetCharacterName());//DEBUG
      DelayedSpecial=true;
      SpecialToucher=toucher;
      TouchCountdown=TouchTimeout;
   }
   
   override void tick()
   {
      if (TouchCountdown>0){TouchCountdown--;}
      super.tick();
      
      if (DelayedSpecial && SpecialToucher)
      {
         SpecialToucher.A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]);
      }
      DelayedSpecial=false;
      SpecialToucher=null;
   }
User avatar
Sir Robin
 
Joined: 22 Dec 2021
Operating System: Windows 10/8.1/8/201x 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: Intel (Modern GZDoom)

Re: A touch trigger: issue with Call Special and teleport

Postby Player701 » Fri May 13, 2022 5:58 pm

Sir Robin wrote:Thanks for looking into this. Is it worth reporting as a bug / feature request? I would think that a teleport function call should reset any movement logic in the actor(s) it is affecting.

You could ask, but I think Graf has said multiple times that he won't touch the movement code again...

Sir Robin wrote:I saw your first post about the timing and tried moving that special call. I didn't think to move it to an inventory item, but I did try moving it from the touch to the tick function of the trigger object and that works for the teleport issue:
Code: Select allExpand view
   bool DelayedSpecial;
   actor SpecialToucher;
   
   override void Touch(Actor toucher)
   {
      if (TouchCountdown>0) {return;}
      console.printf(GetCharacterName().."@"..level.time..": Touched by "..toucher.GetCharacterName());//DEBUG
      DelayedSpecial=true;
      SpecialToucher=toucher;
      TouchCountdown=TouchTimeout;
   }
   
   override void tick()
   {
      if (TouchCountdown>0){TouchCountdown--;}
      super.tick();
      
      if (DelayedSpecial && SpecialToucher)
      {
         SpecialToucher.A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]);
      }
      DelayedSpecial=false;
      SpecialToucher=null;
   }

Probably also an option, the difference is that DoEffect runs after the owner's tick code has been processed, making it more deterministic in regard to the order of execution. But I'm not sure if there is any real difference.
User avatar
Player701
 
 
 
Joined: 13 May 2009
Location: Russia
Discord: Player701#8214
Operating System: Windows 10/8.1/8/201x 64-bit
OS Test Version: No (Using Stable Public Version)
Graphics Processor: nVidia with Vulkan support


Return to Scripting

Who is online

Users browsing this forum: No registered users and 2 guests