For the past several months I've been working on something that's supposed to spice up my mapping. But I ended up working on this thing more than doing any actual mapping, heh. So without further ado.
This is a actor class whose main feature is the ability to carry other actors on top of itself while it moves around.
Possibilies include: - Old-fashioned platforming - Enemies that ride moving "cars" while attacking you. - Floors and ceilings opening up. - Any scenario at all where you can imagine a horizontally moving piece of "geometry" that you can stand on and be carried by.
Movement is based on GZDoom's PathFollower. That is, it moves by using map placed interpolation points. But it can also be moved/rotated via ACS since it has dedicated ACS utility functions.
A short demo map (MAP01) is included.
All the models in that map were created with UDB, but a platform can be a sprite, too.
Feel free to use this for your own projects as long as you mention me in the credits. This is licensed under MIT. I highly recommend you change the FCW_ prefix to avoid conflicts.
Spoiler: "More details"
What's also included is a platform specific interpolation point class. (Though the old interpolation point class is still usable). Features include: travel/hold time can be in seconds or tics. Travel/hold time can have independent time units. If you want to set pitch, you have to do it from the "Properties" tab, just like angle and roll. (Assuming you're using Ultimate Doom Builder as your map editor.) Speaking of, you can have platforms that interpolate their roll, just like angle and pitch.
Platforms can be grouped together so that when one is activated the others will move with it and orbit around it as the mover rotates. This main platform will be referred to as the "origin" or "group origin".
In addition to using PathFollower's options: - Linear path - Use point angle - Use point pitch - Face movement direction.
There are four new options: - Use point roll (Interpolates roll in the same fashion as angle/pitch) - Don't clip against geometry and other platforms (will still collide with non-platforms) - Start active (so you don't have to give it a TID and call Thing_Activate in a OPEN script.) - Mirror group origin's movement (instead of orbiting/moving with it.)
You can set how much crush damage a platform can inflict.
Unlike PathFollower and its subclasses, platforms can intelligently use portals. Though those are only static line portals and interactive sector portals.
Spoiler: "ACS util"
The FCW_Platform class has some ACS centric functions that are meant to be used with ScriptCall(). (See platform-base.zs file)
Please note that "double" for ACS are fixed point numbers. Meaning while 0 is basically 0.0, 90 is NOT 90.0
void Move (int platTid, double x, double y, double z, bool exactPos, int travelTime, [double ang = 0, double pi = 0, double ro = 0, bool exactAngs = false])
**Moves all platforms with tid "platTid" and/or makes them rotate**
- platTid = TID of the platform(s) to move/rotate.
- x/y/z = See exactPos.
- exactPos = if "false" x/y/z are offsets from the platform's current position. if "true" x/y/z are absolute map coordinates to move to.
- travelTime = How many tics until platform completes the move and/or rotation.
- ang/pi/ro (optional) - See exactAngs.
- exactAngs (optional) - if "false" (default) ang/pi/ro are offsets. If "true" ang/pi/ro are exact values.
Note: if "true" the platform will take the shortest rotation direction until its angle/pitch/roll matches the given values. If "false" the rotation direction is maintained.
Eg. if "false" and "ang" is 360.0, platform will rotate 360 degrees counterclockwise, while -360.0 will rotate 360 degrees clockwise.
In addition, the "Face movement direction" option (in the Thing arguments) in combination with the "angle", "pitch" or "roll" options takes precedence over any rotations on the affected axes (eg. angle.)
void MoveToSpot (int platTid, int spotTid, int travelTime, [bool dontRotate = false])
**Moves all platforms with tid "platTid" to the first found actor with tid "spotTid" (eg. MapSpot). This is the only way you can make a platform use any actor as a travel destination.**
- platTid = TID of the platform(s) to move.
- spotTid = TID of actor to move to.
- travelTime = How many tics until platform completes the move and/or rotation.
- dontRotate (optional) = if "false" (default), platform will rotate (the shortest direction) to match the spot's angle/pitch/roll. If "true", no rotation happens.
Again, "Face movement direction" takes precedence.
**Finds the first platform with tid "platTid" and returns "true" if it's active (Please note if the platform is in a group then what's actually checked is the group origin.)**
**Finds the first platform with tid "platTid" and returns "true" if it's active and has moved since last tic. (Please note if the platform is in a group then what's actually checked is the group origin.)**
- platTid = TID of the platform to check.
Check out the zscript files and the ACS source for more info.
Oh yeah, there's also a very crappy test map (TEST01) too.
Spoiler: "Known issues"
Due to how the engine handles actor-to-actor interactions, the platform class can only carry things that have the following actor flags: +CANPASS - Usually players and monsters +SPECIAL - Items
An actor with neither flag simply cannot stand on another actor. CANPASS actors can stand on other CANPASS actors. And actors with SPECIAL can stand on actors with ACTLIKEBRIDGE. The platform class has the ACTLIKEBRIDGE and CANPASS flags. In other words, simple decorations and the like would just fall through. I cannot think of a good way to work around this other than give decorations the +CANPASS flag.
Another thing, the platform cannot carry anything that does have one of the following actor flags: +FLOORHUGGER +CEILINGHUGGER +CANTLEAVEFLOORPIC
This is more deliberate on my part. Because floor/ceiling huggers aren't meant to be in mid-air and CANTLEAVEFLOORPIC is similar to FLOORHUGGER in that apparently CheckMove() considers it invalid if a move results in the actor's Z coordinate being different from its floorZ. So I decided to exclude CANTLEAVEFLOORPIC actors as well.
Last edited by FishyClockwork on Mon May 16, 2022 7:38 am, edited 5 times in total.
This is very nice. I haven't dived into the file yet to see how modular they are as a drop-in for any project but these really do the job very well.
There have been several attempts to do something similar in the past via a variety of means but I think these may feel the most solid and "part of the engine-like". They certainly seem incredibly flexible in what you can do with them.
Enjay
Everyone is a moon, and has a dark side which he never shows to anybody. Twain
As for anyone wondering how exactly to use this: Well, the idea is you inherit from the base class to create your platforms. And while you can use sprites, the workflow (I imagined for myself) was, you make your level geometry, you use UDB to export that as a model, you then make a subclass of FCW_Platform, and set it up accordinally.
If I had to make an analogy, it's like mapping in Quake where you turn a set of brushes into a func_mover or whatever. I know that doesn't sound really convenient.
Most of any platform's behavior can be set up from its thing arguments; there are defined Editor Keys specifically for UDB so in theory it should be pretty straightforward.
On the actor flag issue, you are just using the +CANPASS or +SPECIAL flags to tell your code "you can carry this actor" right? So would there be any merit in you using zscript to create your own +CANBECARRIED flag that is only used as a marker for that purpose (if such a thing can be done)?
Enjay
Everyone is a moon, and has a dark side which he never shows to anybody. Twain
No, no. What I meant by that is that actors lacking those flags simply fall through the platforms. Even if you were to attempt to carry anything they would just fall through.
What's keeping those passengers on top of a platform has nothing to do with the platform's code. I suspect it's more to do with that a object with CANPASS can stand on another CANPASS object. And the only way an actor with SPECIAL can stand on anything is if the lower actor has ACTLIKEBRIDGE (which FCW_Platform has set).
I didn't just arbitrarily decide an actor has to have one of those two flags. It's just that, if I know they will fall through the platform, then don't even bother trying to move them when the platform moves.
Last edited by FishyClockwork on Mon May 16, 2022 7:15 am, edited 1 time in total.