Search In
• More options...
Find results that contain...
Find results in...

# How to get the Z coordinates of a floor?

## Recommended Posts

I'm back again with yet another question... sorry guys, but I keep hitting my head against walls LOL.

Here's my latest dilemma.

I have a moving 3D floor. I need to trigger actions based on its location in space. Ideally an if else statement that would look at the height of its floor or ceiling.

Only solutions I've found so far are:

1) GetSectorCeilingZ

2) GetZAt

I understand how the first works, but it apparently requires an actor. So that won't work for me as I need a way to check that position whether there is an actor there or not. Plus, if I understand correctly, it doesn't do well with 3D objects, so that alone kind of rules it out (unless there's a workaround?)

GetZAt sounded more promising as it handles 3D and doesn't seem to require an actor... I think? Problem is I can't make any sense of how it's supposed to work. I've read the wiki entry here ten times and still have no clue:

The problem is that the example they give, seen from the perspective of someone just starting to learn ACS (such as myself), is extremely obscure. Nothing in it is explained. It just says what it does but without dissecting the parts, so I'm left totally dumbfounded :o

One issue I'm seeing though is that the function itself doesn't reference anything at all. Only coordinates based on a specific spot, but what is that specific spot? There's no argument to enter a tag number, for instance, which is presumably what I would need.

I did try to use it (without really knowing what I was doing LOL so it was a bit random) and when I compiled the code an error was thrown saying that GetZAt hadn't been defined (or something similar)... I wasn't expecting that type of message, as I thought that was a built-in function??

So anyway... how can I use one of the functions above to attain my goal? Or is there something else that might be more appropriate?

if someone could help with this I would really really appreciate it.

Oh! And as a side question: is there something I can read out there that explains the exact *syntax* of ACS scripts? I mean beyond the basic functions... something that would make clearer what is going on for instance in that example on the GetZAt page (which is very different from anything I'd seen before). YouTube videos are welcome too.

Thanks.

EDIT: I can now answer that last question myself, as I came across a GREAT post that clearly and concisely explains the syntax of a decorate script (which is what that example is, not ACS)... it was posted by Gez on this very forum, and I figure it could be useful to someone else who runs into this thread looking for answers:

Edited by Andreas

GetZAt is not an ACS function. You have to use it as per the example provided, in DECORATE or zScript.

6 hours ago, Andreas said:

1) GetSectorCeilingZ

2) GetZAt

I understand how the first works, but it apparently requires an actor.

GetSectorCeilingZ doesn't require an actor.

6 hours ago, Cherno said:

GetZAt is not an ACS function. You have to use it as per the example provided, in DECORATE or zScript.

In English, that means GetZAt is called from inside an actor.

ACS = map scripts, doing stuff like invoking line specials like "raise this platform", "change this wall's texture", "play ambient sound", "spawn more Ettins at these MapSpots", and so on.

DECORATE = creates/modifies things, doing stuff like "look at my target", "play this animation when I take damage", and so on.

Since your platform is tied to the map rather than shared among types of sprites, it makes more sense to approach it from the ACS side. For example, have a script that tells the platform to move: and you will know its position by where you tell it to go. As I recall in Quake II, this sort of thing would be done with a brush entity for the platform and waypoints for it to move to, and the waypoints could invoke triggers when the platform reaches it (for example, if there's a door in the platform's path, the waypoint in front of the door would trigger the door to open, and the waypoint behind it would trigger the door to close again).

Within the Doom engine family, one of the enemies in Hexen is defined similarly, where its motion is defined by a network of waypoints (Mapspots) that refer to each other. However, the enemy is a sprite as usual rather than a type of level geometry, and its pathfinding is handled sprite-side, which would be a DECORATE sort of thing. 😕

In any case: your platform moves. It doesn't just move on its own: what triggers it to move? How is its motion defined? Is it fed xyz velocity values from a script (or Thing properties or something), or is it aimed at a waypoint (along with a target speed or time)? In principle, you should be able to access that information; and with it, the effect that triggers it to move should be able to calculate where it'll be.

1 hour ago, boris said:

GetSectorCeilingZ doesn't require an actor.

In that case, I'll edit what I wrote about it: I do NOT understand how it works LOL.

I mean... if you read that description it requires x and y... from the example, it uses x and y of a thing. How would I apply this to a sector? And how would I know the x and y anyway? I tried using it with 0, 0 and nothing happened.

Also, there's the 3D thing.

39 minutes ago, Rainne said:

In English, that means GetZAt is called from inside an actor.

Thank you! That makes it clearer ;)

39 minutes ago, Rainne said:

For example, have a script that tells the platform to move: and you will know its position by where you tell it to go.

Well... yes and no. I have a script that moves the platform, but that just tells me where it *could* be. Since it can go back and forth between the initial state and the raised state, that's why I need some code that will check which of the two states... it's... wait...! I just realized one way I could do it is to set a variable once it's raised, then simply check the value of that variable to see which state it's in. Duh.

Gonna go try that.

39 minutes ago, Rainne said:

Is it fed xyz velocity values from a script (or Thing properties or something), or is it aimed at a waypoint (along with a target speed or time)? In principle, you should be able to access that information; and with it, the effect that triggers it to move should be able to calculate where it'll be.

I use FloorAndCeiling_RaiseByValue to increase the height by 128. Like mentioned above though, that just helps me know where it can be, not where it actually is at any given time.

EDIT: I just tried it and... it doesn't work. Though I don't know if it's my logic that is faulty or if it's just because I'm trying to use a function (GetSectorCeilingZ) that's known not to work with 3D objects?

Edited by Andreas

8 hours ago, Andreas said:

I mean... if you read that description it requires x and y... from the example, it uses x and y of a thing. How would I apply this to a sector? And how would I know the x and y anyway? I tried using it with 0, 0 and nothing happened.

Perhaps pay attention to your map editor? It uses the exact same coordinate system as the game. If a thing is at 512, 256 in the map editor, it is at 512, 256 in the game. (Until it moves, anyway.) If a vertex is at 384, 576 in the map editor, it is at 384, 576 in the game (and vertices don't move, so you're safe).

You can place a thing in the middle of your sector, notice its coordinates, and use those. Then you can remove the thing.

For a 3D floor, use the control sector, by the way.

x and y are only relevant for slopes. If you know for sure you do not have a slope, you can use 0, 0 for x and y.

Thanks Gez, that makes a lot of sense! I hadn't thought of that haha. I don't use slopes though, so I guess 0, 0 should work, as points out Graf... except it doesn't.

Sigh.

This is starting to drive me crazy LOL. I've tried a lot of different things, and I thought I'd finally figured a simple way to do it, except it doesn't work at all now.

Here's the code I'm currently using:

```script 2 (void)
{
int z, fldwn, flup;
z = GetSectorCeilingZ(6, 0, 0);

print(s:"The ceiling of the 3D floor is currently positioned at: ",f:z);

if(z == 160)
FloorAndCeiling_RaiseByValue (6, 8, 128);
if(z == 288)
FloorAndCeiling_LowerByValue (6, 8, 128);
if(z > 288)
{
fldwn = z - 160;
FloorAndCeiling_LowerByValue (6, 8, fldwn);
}
if(z < 160)
{
flup = 160 - z;
FloorAndCeiling_RaiseByValue (6, 8, flup);
}

print(s:"\nfldwn = ",i:fldwn,s:"\nflup = ",i:flup);
}```

I threw in some print's to try and understand why it's not working and the stuff printed makes no sense!

The 3D floor / dummy sector starts with its ceiling at 160. When you step on it, it's supposed to go up by 128 (bringing it to 288).

So I was expecting the first print to show me z with a value of 160 (since it's before it moves). When I step on the sector, it should go through the "z == 160" statement and raise up to 288, and the second print message should give me the value of fldwn/flup, in this case 0 since it wouldn't have gone through the z > or z < clauses (which are there as a safeguard as I've had cases where the platform would just keep rising and rising and rising ha).

So... instead... the platform doesn't move and the first print doesn't even show at all while the second claims that fldwn = 10485600 (??) with the second iteration (when I walk over the line again) shows that flup = 6291616 (with still no first message printed and still no platform movement)...

Why is this not working?

I'm guessing variables in ACS don't work at all the same way that I'm used to (in php)...

For context, here's the code I was using previously... which did make the platform move as expected, except it was buggy, as there were cases (like if I stepped off the platform too quickly and/or ran under it) where it would go up or down beyond the point it was supposed to, making it impossible to bring back to the starting point (which is why I tried the code above and included those safeguards):

```int PF = 0;

script 2 (void)
{
int z;
z = GetSectorCeilingZ(6, 0, 0);

if(PF == 1 && LineSide() == LINE_BACK)
{
FloorAndCeiling_LowerByValue (6, 8, 128);
PF = 0;
print(s:"The platform is going back down.\nActorCeilingZ = ",
f:acz,s:"\nSectorCeilingZ = ",
f:z,s:"\nPF = ",d:PF);
delay(3);
}
if(PF == 0 && LineSide() == LINE_FRONT)
{
FloorAndCeiling_RaiseByValue (6, 8, 128);
PF = 1;
print(s:"Your feet are on the ground... rising!\nActorCeilingZ = ",
f:acz,s:"\nSectorCeilingZ = ",
f:z,s:"\nPF = ",d:PF);
delay(3);
}
}```

Here too, the prints are for testing/debugging, though they would give me some more logical output.

This is the closest I've come to getting the result I wanted, but it's way too buggy...

HA! It looks like I managed to fix my code... not completely to be fair, there's still a small bug. But here's what I'm using now:

```script 2 (void)
{
int x, y, z, zz;
x = GetActorX(0);
y = GetActorY(0);
z = GetActorZ(0);
zz = GetSectorCeilingZ(6, 0, 0);

// the platform is above the player, bring it down
// this is a safeguard in case the player jumps off while the platform
// is in the air...
if(zz > z)
FloorAndCeiling_LowerByValue (6, 8, 128);

// otherwise, only raise or lower the platform if the player is actually on it
if(z == zz && LineSide() == LINE_BACK)
{
FloorAndCeiling_LowerByValue (6, 8, 128);
// PF = 0;
print(s:"The platform is going back down.",
s:"\nSectorCeilingZ = ",
f:zz,s:"\nPF = ",d:PF,
s:"\nXYZ = ",f:x,s:"-",f:y,s:"-",f:z);
delay(3);
}
if(z == zz && LineSide() == LINE_FRONT)
{
FloorAndCeiling_RaiseByValue (6, 8, 128);
// PF = 1;
print(s:"Your feet are on the ground... rising!",
s:"\nSectorCeilingZ = ",
f:zz,s:"\nPF = ",d:PF,
s:"\nXYZ = ",f:x,s:"-",f:y,s:"-",f:z);
delay(3);
}

}```

This is doing a pretty good job and got most of the issues resolved. The only problem I still have with this is if I run over the platform just as it's coming down, before it's completely done coming down, then it gets stuck, no more rising or lowering. Not sure how to fix that hmm.

You could use "TagWait()" to prevent one script from interrupting another until the initial movement is complete.

That kinda works, but not completely... It solves running back and forth over the platform while its moving. But it's still possible to break the code if you step on the platform while it's still coming down, wait, then step off. Problem is that in this scenario, it goes to the LINE_BACK clause and so lowers the platform instead of raising it, thus becoming stuck.

I thought adding && z == 160 to the LINE_FRONT clause and && z == 288 to the LINE_BACK one would fix the issue, but no luck. That completely breaks it, platform won't budge anymore for some reason. Hmm.

4 hours ago, Graf Zahl said:

x and y are only relevant for slopes. If you know for sure you do not have a slope, you can use 0, 0 for x and y.

x and y are also relevant if you're looking for the height of an untagged sector.

Which can be useful if you don't want to use a tag for your control sector.

I'm happy to announce that I figured this out!

I couldn't understand why my zz == 160 solution wasn't working... until I came across a video today that showed numerals printed as floats. So I thought I'd try that and tada! It fixed the issue.

So in other words, my if statements now look like this:

if(zz == 288.0 && z == zz && LineSide() == LINE_BACK)

This one checks if the platform should go down.

if(zz == 160.0 && z == zz && LineSide() == LINE_FRONT)

This one checks if the platform should go up.

No more bugs now, as far as I can tell ;)

Thank you to everyone who helped with this.

Testing floats for equality is generally not best practice, since they can't represent exact values unless they're sums of powers of two (½, ¼, etc); so something like 1.1 can't be represented exactly for the same reason 1/3 can't.

So if zz is supposed to be between 160 and 288, check against "zz >= 288.0" instead of "zz == 288.0", and likewise "zz <= 160.0". So if it doesn't land precisely on the target value, it'll still work. And if it does, it'll continue to work as written anyway.

Fixed. Thanks ;)