Forum rules
The Projects forums are only for projects. If you are asking questions about a project, either find that project's thread, or start a thread in the General section instead.
Connorpo1221 wrote: ↑Fri Sep 20, 2024 2:09 pm
I tried to load a map01 in it and it says 3Dplatform v2.2b.pk3:zscript/fishy/platform-base.zs, line 432: Unable to resolve LinePortal as a type.
So I'm messing around with this library, hoping to make use of it in my next map. Its really impressive and has massive potential for a platforming-themed WAD (Golden Souls 3 maybe?)
One thing I've noticed while testing is that setting the compatibility flag compat_crossdropoff to true (default is false) makes it so that active monsters will walk off moving platforms.
compat_crossdropoff is a weirdly named flag that seems to do the opposite of what its name is, but basically I've got it set on my project to prevent monsters like Archviles from being pushed off ledges by SSG blasts and rockets. At least MAPINFO allows you to set these flags for each map, so I can set it back to default for my Fishy platform map.
That build fixed the issue on my end, thanks for the quick response!
I did come across one other issue, but this is more of a cross-mod compatibility thing and probably isn't in-scope to fix on your end.
Basically, some mods have monster death animations (DECORATE or ZScript) with frames that loop until A_CheckFloor is true. It simulates stuff like falling and having the death animation restart when hitting the ground. Supercharge has this, and Brutal Doom probably does too.
The issue is that A_CheckFloor can only return true for sector floors, not actors (ie. 3d platforms)... so monsters can get stuck in a loop and not finish their Death state. Not only does this look silly, but Archviles can't resurrect things that haven't "finished" dying.
The workaround for this is for mods to use the A_CheckSolidFooting custom function instead of A_CheckFloor, but this might be ZScript-only.
Alright, so the problem is that A_CheckFloor checks the calling actor's floorz value to see if it goes below the current sector's floor or below the lower 3D floor. (As far as I can tell "floorz" never takes into account actors below other actors.)
I don't feel comfortable trying to hack around this by messing with a passenger's floorz value just to trick A_CheckFloor to do the state jump. (I've tried; I can't get it to work without messing up other things.) And at this moment in time there is no way to even detect if the passenger is going to call A_CheckFloor so I can't reliably intercept it.
And besides, checking the passenger's floorz is already done as part of the passenger management logic.
It checks if the passenger's floorz is above the platform's top. (The "top" is its Z position + its height.)
And if it is, it's assumed there must be a 3D floor above the platform - meaning the passenger should be ignored/not moved by the platform.
This kind of check is done repeatedly for each new/current passenger per tic so that I don't have to rely on functions like NextLowestFloorAt. Because it would make it more expensive.
One day I'd probably need to restructure some or all of the code, but not right now.
I tried to PM Fishytza, but I've not participated enough here. I really need some guidance on this ridable model subject. Is there anyway to get a simplified version of this mod, one that doesn't use the "test" models, and I could plug it into my mod? Credits to Fishy of course. I'm just not good enough with zscript, etc, to dissect this test mod setup, and use it for a simple, single ridable model that follows interpolation points. Any help appreciated, and will be credited when I release kmetl17.
- Several under-the-hood changes and bugfixes
- New feature: you can give platforms an arbitrary pivot point to rotate around
- New feature: perpetual rotation/turn speeds to complement the pivot feature
- New feature: platform interpolation points can selectively ignore an axis
- New feature: the generic subclass can adjust its radius and height to match the model it uses;
currently only works with OBJ models
- New feature: each platform can now play generic "start", "stop", "move", and "blocked" sounds
which can be easily declared in the Custom tab since they're user variables
There's also a "lite" version of the main PK3 that only includes the bare minimum assets/code
It contains the core platform class and its "generic model" subclass.
Assuming you're comfortable using the generic subclass
(IE you don't need to see the model it uses in UDB's Visual Mode)
you can use the "lite" PK3 as a mapping resource for UDB.
Check the spoilers for more details.
Spoiler: Pivot point
A "pivot" is a point to rotate around for the platform. Rotations occur when the platform's
angle(yaw), pitch, and/or roll changes.
The pivot can be defined by placing a specialized mapspot ("Platform pivot" / FishyPlatformPivot).
Or it can be defined/removed via ACS. See FishyPlat_SetPivot and FishyPlat_RemovePivot
The mapspot has a few mapthing arguments
The pivot can optionally be attached to the platform (the pivot distance is always the same).
The mapspot can also be set to need activation before it sets any pivot data. Otherwise it will set the data as soon as the map starts.
Please note that the pivot data is set internally and is not dependent on any spot's actual position!
If the "Platform(s)" argument is 0 and the activator is a platform then the spot will set the activator's pivot data.
Note about interpolation specials:
Unlike PathFollowers, when a platform triggers a InterpolationSpecial thing, the platform is considered the activator.
Therefore you can set up a InterpolationSpecial to activate a FishyPlatformPivot who in turn (assuming "Platform(s)" argument is 0) will set that platform's pivot data.
Note about groups:
If a platform is in a group and is not the "main mover/origin", it will ignore any assigned pivot!
See the "Pivot test" (PIVOTS) map to see this feature in action.
Spoiler: Perpetual rotation speeds
To complement the pivot feature, platforms can now have perpetual rotation speeds defined (turnspeeds).
These turnspeeds are user variables you can find in the Custom tab when placing a mapthing. (If you're using UDB.)
They come in three categories for angle, pitch, and roll. "onpath", "withvel" and "idle".
When a platform is actively following its path (and not waiting), the "onpath" turnspeeds will take effect.
Else, when a platform is moving because of its velocity, the "withvel" turnspeeds will take effect.
Else, when it's not moving whatsoever, the "idle" turnspeeds will take effect.
Note about path following:
If using one of the options "Use point angle/pitch/roll" then the associated turnspeed is nullified!
(Eg. setting "Use point angle" will override/nullify "user_turnspeed_angle_onpath".)
Also, when using ACS commands to move platforms around, if you declare a rotation to occur (eg. with FishyPlat_Rotate)
then that rotation will nullify/override the associated turn speed. In other words. No given rotation = use "onpath" turnspeed.
For example. To have a platform whose angle turns clockwise when idle/waiting but not when moving,
set "user_turnspeed_angle_idle" to -8. (Positive values turn counter-clockwise.)
To have a platform that only turns when following its path but not when idle/waiting,
set "user_turnspeed_angle_onpath" to -8.
To have a pushable crate-like platform that only turns when pushed (it's a velocity move)
set "user_turnspeed_angle_withvel" to -8.
Note about groups:
These turnspeeds have no effect on platforms that are part of a group and are not the "main mover/origin".
If every group member starts as inactive then there is no "origin".
In such a ambiguous situation, the first platform with a non-zero "idle" turnspeed
becomes the new "origin" simply because it starts turning at that point, which counts as movement for the group logic.
So in short. The priority is "onpath" (travelling) > "withvel" (velocity move) > "idle" (not moving)
Spoiler: Platform interpolation point user variables
The platform-centric interpolation point class has some new user variables as well, some which
are related to the new pivot feature.
bool user_ignoreaxis_x - If true, platform will ignore point's X pos
bool user_ignoreaxis_y - If true, platform will ignore point's Y pos
bool user_ignoreaxis_z - If true, platform will ignore point's Z pos
bool user_ignorepivot - If true and this point is the destination, platform will not rotate around its pivot
bool user_undopivotadjustment - If true, when this point is the platform's destination, reset the platform's offseted travel pos (if any)
Path following and pivot behavior can coexist.
Pivot behavior: Platform position changes by rotating
around its pivot due to angle/pitch/roll adjustments.
This affects the travel path, and the adjusted
position persists even if rotation stops.
NOTE: Variables ignoring position changes (e.g.
user_ignoreaxis_x) only affect path following, NOT
pivot-related position changes.
To ensure the platform moves to this point's exact
position ignoring prior pivot adjustments (assuming
no angle changes, or "user_ignorepivot" was true), set
"user_undopivotadjustment" to true.
Spoiler: Generic model size
The generic subclass can now adjust its radius and height to the model it uses (only works with OBJ models)
bool user_modelsetssize - If true and a model is provided, this will override "user_set_height" and "user_set_radius"
Spoiler: Generic sounds
Each platform can now play generic "start", "stop", "move", and "blocked" sounds.
string user_snd_start - plays when movement starts.
string user_snd_stop - plays when it's finished moving.
string user_snd_blocked - plays when movement is blocked.
string user_snd_move - plays looping sound when moving.
bool user_snd_movestopsstart - If true, "move" interrupts "start" when it plays. Otherwise they coexist.
int user_snd_delaytomove - tics to wait after "start" has played before playing "move" sound.
If "user_snd_delaytomove" <= 0, "move" will wait until "start" is done.
"user_snd_movestopsstart" is only relevant if "user_snd_delaytomove" is > 0
If you want "start" and "move" to play at the "same time", "user_snd_delaytomove" must be at least 1
See the "Generic sound test" (SNDTEST) map to see this feature in action
Spoiler: About the generic subclass
First to define two things - The "core class" and the "generic subclass"
1) the "core class" FishyPlatform - Here is all the fundamental logic/coding
for just about everything. Including carrying actors, the path following and the pivot logic etc.
This class is abstract so you're supposed to inherit from it, giving it a sprite animation or model.
2) the "generic subclass" FishyPlatformGeneric.
I want to clarify what the generic subclass's purpose was supposed to be as I feel like I haven't made this clear before.
If you've ever mapped for Doom 3 then you may be familiar with what a "func_mover" is supposed to be.
A func_mover is a generic entity you could assign any set of brushes as its "model" and tell it where to move/rotate.
The same applies here except with the additional step you have to use UDB's export geometry feature
to get a .OBJ model file. And as far as the generic class is concerned that is it!
There is no need for additional actor and MODELDEF definitions!
This is accomplished because the generic class makes use of A_ChangeModel to set its model
(It feeds A_ChangeModel parameters from its own user variables)
and, as of this release, is able to automatically adjust its radius and height, assuming it's the OBJ format.
(IE the same model type that UDB exports).
While you can use this however you want, the usual intended practice is
set "user_cm_modelpath" to the path (if any) where your models are
and set "user_cm_model" to the model file, including its extension.
Alternatively you can just set "user_cm_model" to "modelpath/modelfile.extension"
and it will work.
You are not limited to OBJ models, of course, but in that case
you have to manually adjust its size through "user_set_radius" and "user_set_height"
and you must set "user_modelsetssize" to false.
So why is this separated from the core class? Why is it its own subclass?
Because the model (and its size) this actor is going to use will not be shown
in UDB's Visual Mode. The alternative is, of course, making your own actors that inherit
from the core class, and give them a model through MODELDEF.
Spoiler: New known issue
Quaternions are used for all rotations in the group logic and pivot logic.
The algorithm for converting a quaternion to euler angles has one
math problem that I'm told is unsolveable. If the resulting pitch is close to
+90/-90 degrees it messes up the resulting angle(yaw) and roll.
This algorithm is used by the "main mover/origin" of a group to set
the euler angles of the other groupmates (not itself).
To see what I mean, assuming every group member faces the same direction
(has the same angle/pitch/roll values)
If the "origin" platform slowly turns 90 or more degrees on its pitch in either +/- direction
the other platforms will briefly "glitch" before "returning" to their normal euler angle-rotation.
This doesn't happen if the angle(yaw) or roll crosses the 90 degrees threshold, only pitch.
To be perfectly honest, I am not sure I'm describing this accurately. But you'll know it when you see it.
If you have any questions or bug reports - don't hesitate to share them!