ZScript TryMove and CheckMove put monster in walls

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!)
Post Reply
peewee_RotA
Posts: 370
Joined: Fri Feb 07, 2014 6:45 am

ZScript TryMove and CheckMove put monster in walls

Post by peewee_RotA »

In ZScript I have some of the monsters in Hexen able to teleport away on a hit. An inventory has a ModifyDamage override that calls another function to do this. I've tried combinations of CheckMove, TryMove, and then a combination of both with the TeleportMove as the actual move call. In every case the monster thinks that outside of the walls is valid and teleports out there sometimes.

Here's the code I have:

Code: Select all

void DoLightningLeaderTakeDamage(int damage, Name damageType, Actor inflictor, Actor source)
    {
        //don't teleport on death
        if (Owner.Health - damage < 0)
            return;

        if (!IsTimedOutExpired())
            return;
        SetTimeout();
        
        int moveTries = 0;
        Vector2 newPos;
        let oldPos = Owner.Pos;

        while (moveTries >= 0 && moveTries < 10)
        {
            moveTries++;

            newPos.x = random(-LIGHTNINGBOSS_TELEPORT_DIST, LIGHTNINGBOSS_TELEPORT_DIST) + Owner.Pos.X;
            newPos.y = random(-LIGHTNINGBOSS_TELEPORT_DIST, LIGHTNINGBOSS_TELEPORT_DIST) + Owner.Pos.Y;

            if (Owner.TryMove(newPos, 1, true))
            {
                let mo = Spawn("LightningLeaderFx1");
                if (mo)
                    mo.SetOrigin(oldPos + (0,0,28), false);

                moveTries = -1;
            }
        }
    }
It finds random positions and does a try move. If it works it exits the loop and creates an effect. It retries on a failure a few times and then ultimately exits if it fails too many times.

Has anyone ran into this before? I just want to know if it is a safe move position.

These two methods don't have a whole lot of documentation, and I had to go into the C++ code to figure out what the dropoff field does in TryMove (apparently 1 is yes, and 2 is yes sometimes but the logic is really complex and confusing)
User avatar
Player701
 
 
Posts: 1640
Joined: Wed May 13, 2009 3:15 am
Graphics Processor: nVidia with Vulkan support
Contact:

Re: ZScript TryMove and CheckMove put monster in walls

Post by Player701 »

Try to use the same combination of checks as I suggested here. Upd: You should first change the position of the monster or they won't work. Reset to the original position on fail.
peewee_RotA
Posts: 370
Joined: Fri Feb 07, 2014 6:45 am

Re: ZScript TryMove and CheckMove put monster in walls

Post by peewee_RotA »

That's genius! I'll give it a shot.
peewee_RotA
Posts: 370
Joined: Fri Feb 07, 2014 6:45 am

Re: ZScript TryMove and CheckMove put monster in walls

Post by peewee_RotA »

Did some more testing and I think it's a Hexen issue. The outside sectors are valid after it teleports, they have floors, and appear to have heights and all that. After doing more digging, there is another problem. Rotating and moving walls in hexen have extra sectors so they can be lit. Meaning a monster that is required to be defeated could end up in a valid space in these extra rooms. So I had to make it so that it is not just a valid landing space, but a valid flight path and angle.

Meaning I run a linetrace to the new position. If the trace hits something it is a fail.

In the end the position has to be valid and a hitscan style trace has to be possible between the original spot and new spot. I do this line trace before the move and check and just hold onto its value.

Code: Select all

while (moveTries >= 0 && moveTries < 10)
        {
            moveTries++;

            newPos.x = random(-LIGHTNINGBOSS_TELEPORT_DIST, LIGHTNINGBOSS_TELEPORT_DIST) + Owner.Pos.X;
            newPos.y = random(-LIGHTNINGBOSS_TELEPORT_DIST, LIGHTNINGBOSS_TELEPORT_DIST) + Owner.Pos.Y;
            newPos.Z = Owner.Pos.Z;

            let vecOffset = newPos - oldPos;
            let vecLen = vecOffset.Length();
            let vecAngle = VectorAngle(vecOffset.x, vecOffset.y);
            let newPitch = VectorAngle(vecLen, vecOffset.Z);

            bool traceFail = Owner.LineTrace(vecAngle, vecLen, newPitch);

            Owner.SetOrigin(newPos, false);
            newPos.z = Owner.CurSector.NextLowestFloorAt(Owner.pos.x, Owner.pos.y, Owner.pos.z, Owner.pos.z, FFCF_NOPORTALS);

            Owner.SetZ(newPos.z);

            if (traceFail || !Owner.TestMobjLocation() || Owner.height > (Owner.ceilingz - Owner.floorz) || !Owner.CheckMove(Owner.Pos.XY))
            {
                //Move unsuccessful, move back
                Owner.SetOrigin(oldPos, false);
            }
            else
            {
                //Move Successful, spawn lightning and exit
                let mo = Spawn("LightningLeaderFx1");
                if (mo)
                    mo.SetOrigin(oldPos + (0,0,28), false);
                
                moveTries = -1;
            }
        }
peewee_RotA
Posts: 370
Joined: Fri Feb 07, 2014 6:45 am

Re: ZScript TryMove and CheckMove put monster in walls

Post by peewee_RotA »

Last note. The Stalker (sea creature) already does a thing where it goes invisible, walks around, and then reappears. Anyone trying to do what I am doing should ignore this approach, set their monster invisible & invincible, let it walks around a second, and then set it visible again. Achieves the exact same effect with none of the fuss.
Post Reply

Return to “Scripting”