Secondary animations for triggered objects: Experimental version available

Started by namida, May 02, 2019, 09:24:27 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Nepster

QuoteThe primary animation can be defined in either a $PRIMARY_ANIMATION segment (or as an alternate proposal: an $ANIMATION segment with a "PRIMARY" keyword), or in the main segment of the NXMO file.
Yeah, that's a much better way to ask my question, than what I wrote above. Until your latest post, I was confused when to use $PRIMARY_ANIMATION and when to use the main section (or whether I have to duplicate stuff now).

QuoteThe downside here is - either loading code needs to be duplicated, or the main segment has to be parsed a second time (as an $ANIMATION segment), or it just ends up getting translated internally to an $ANIMATION segment with special handling (which is pretty much what already happens).
If adding a $PRIMARY-ANIMATION part is still optional, then you will have to parse it (twice?) regardless of whether we change it to main-section only or not. So I don't see any downside of forcing primary animations to be defined in the main section. If nothing else, then it removes the confusion about when to define stuff in the main section and when to use $PRIMARY-ANIMATIONs.

QuoteAlso, I don't really know how I'd feel about having a "NAME" parameter for an animation, in the main segment - though perhaps NAME could just be disallowed altogether for the primary.
Good point, but so far it worked pretty well to use the same name for the .nxmo file and the main animation .png file.

namida

QuoteIf adding a $PRIMARY-ANIMATION part is still optional, then you will have to parse it (twice?) regardless of whether we change it to main-section only or not. So I don't see any downside of forcing primary animations to be defined in the main section. If nothing else, then it removes the confusion about when to define stuff in the main section and when to use $PRIMARY-ANIMATIONs.

The intent here - $PRIMARY_ANIMATION would eventually become the "normal" way of defining it, and - a long way down the track - use of the main section would be deprecated and maybe eventually removed. However, for now, use of the main section was preserved for backwards (and to some extent, forwards) compatibility. This is why I didn't bother to code full support for all of the new options, but instead just popped in code that translates in-memory the main section lines to a $PRIMARY_ANIMATION segment.

Had this been a new engine being made from scratch, with no worries of existing content or what existing users are familiar with, it would be only $PRIMARY_ANIMATION (or $ANIMATION with PRIMARY keyword - one or the other, not "either one is valid") for sure.

QuoteIf adding a $PRIMARY-ANIMATION part is still optional, then you will have to parse it (twice?) regardless of whether we change it to main-section only or not. So I don't see any downside of forcing primary animations to be defined in the main section. If nothing else, then it removes the confusion about when to define stuff in the main section and when to use $PRIMARY-ANIMATIONs.

Thinking a bit more clearly now - if the primary is defined in the main segment, there'll be some duplication either way, unless we literally just parse the main section twice - once as a main NXMO section, and once as an $ANIMATION section would be, keeping in mind this also means we need to make sure there's never keyword overlap between the two. At best, this would consist of the same translation code currently used to turn this into a $PRIMARY_ANIMATION segment (which can be loaded as a normal animation, just with a few things overridden / ignored), just without the option of using such a segment directly - and in turn, without the option of ever deprecating it.

This way, adds a small bit of extra work now, in exchange for - once this feature's stable and been around a good amount of time - being able to deprecate the main-segment way further down the track more cleanly.

QuoteGood point, but so far it worked pretty well to use the same name for the .nxmo file and the main animation .png file.

Yeah, this is probably the simplest way if we go with main-section-only - just add NAME to the fields prohibited on the primary. I would like to keep the "if no name is specified, the main PNG file is used" capability on secondaries though, as two secondaries with the same graphic (and thus, a primary and a secondary with the same graphic) is allowed, and at any rate prohibiting this would be trivial to bypass (by creating a second copy of the PNG file with a different name). Of course this shouldn't be used on things like exits, traps, etc, but it might be useful for decorative objects.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

namida

As per discussions me and Nepster have had in PM, the keywords "CUT_LEFT" etc are changed to "NINE_SLICE_LEFT" etc. They still work exactly the same; just the keyword itself is changed.

As per the discussions in this topic, the "LEFT" and "RIGHT" conditions have (for now) been removed. If a use case where these are needed arises, please bring it up, and we can consider re-adding them.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

Nepster

Quote from: namida on May 17, 2019, 07:39:39 PM
The intent here - $PRIMARY_ANIMATION would eventually become the "normal" way of defining it, and - a long way down the track - use of the main section would be deprecated and maybe eventually removed.
Ok, now I can understand why you introduced the $PRIMARY_ANIMATION. While I wouldn't have done so myself, it might actually prove useful in the long run. So let's keep the $PRIMARY_ANIMATION (which I prefer over adding a PRIMARY attribute to secondary animations), but do three other things as well:
1) Deprecate the old way right away (which is mainly a case of making this change in the graphics tool - but does anyone still use it?)
2) Clearly communicate that new styles should use $PRIMARY_ANIMATIONs. Don't we have some guidelines about graphic style creation here on the forums, where this should be added?
3) Fully change a few styles right now, so that other graphic designers have enough examples to copy from.

namida

QuoteOk, now I can understand why you introduced the $PRIMARY_ANIMATION. While I wouldn't have done so myself, it might actually prove useful in the long run. So let's keep the $PRIMARY_ANIMATION (which I prefer over adding a PRIMARY attribute to secondary animations), but do three other things as well:
1) Deprecate the old way right away (which is mainly a case of making this change in the graphics tool - but does anyone still use it?)
2) Clearly communicate that new styles should use $PRIMARY_ANIMATIONs. Don't we have some guidelines about graphic style creation here on the forums, where this should be added?
3) Fully change a few styles right now, so that other graphic designers have enough examples to copy from.

1. Alright. Let's have a solid plan for when it will be removed altogether - maybe something like "whichever comes last, 2 further major version updates, or the first major version update after 6 months from now". Not sure re: use of graphic set tool, I know the majority of users don't use it but I don't know if it goes as far as "no one does". Latest version has 431 downloads, but (a) this is over a whole year, (b) a lot of that likely comes from webcrawlers, (c) if the installer downloads it via a neolemmix.com/download.php link, that will be included in these counts, and (d) downloaded != uses.
2. Yep, this makes sense. We should have a "Information regarding V12.05.00 for style creators" topic, separate from the release topic, and stickied. As well as drawing attention to this change, it can also explain the new features, including best practices for using them.
3. I'm a bit reluctant to change anything we don't need to right away. It would be preferable that - at least for a little while - the styles download remains physics-compatible with older versions, as well as as graphically compatible as can be achieved without either (a) being detrimental to the new version in any way, or (b) using ugly hacks, like having both info in main segment and a $PRIMARY_ANIMATION. Perhaps wait until a V12.5.1 update to do this.

In regards to actually converting styles, it should be possible to make a command line tool that automates this. As explained in a post earlier in the topic, NeoLemmix handles data in the main segment of the file, by internally translating it to a $PRIMARY_ANIMATION segment - this isn't a metaphor, it's literally what NeoLemmix does. Doing this, removing the old lines, and writing the result to a file, would be a sufficient tool. The only case it wouldn't cover is objects that use the MASK feature, and as far as I know, this was only ever default:pickup and default:owa_XXXX - easy enough to handle these two manually, plus the mask side of things is already updated in the repo.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

namida

As per Nepster's suggestion earlier in the topic (or maybe in PM?), "INITIAL_FRAME RANDOM" is now valid and recognized input. A negative number will also have the effect of a random initial frame, but let's encourage use of "INITIAL_FRAME RANDOM" rather than "INITIAL_FRAME -1".

Additionally, I have removed the support for recognizing the older "PREVIEW_FRAME" and "RANDOM_START_FRAME". Why? Because (unless they're used by some very recent - as in last few weeks - addition to the styles download) the former hasn't been used at all, and the latter is only used in a handful of my own styles - which I can easily make sure are up to date, and already have done so in the repo. Very possibly because the style convertor didn't handle them properly, and their existance was never really drawn attention to.

The earlier post detailing the system has been updated to reflect:
a) $PRIMARY_ANIMATION should be used in all cases, and define-in-main-segment should be treated as deprecated (previously, it was suggested that for now $PRIMARY_ANIMATION should only be used if use of an unsupported-in-main-segment new feature was desired)
b) Change of "CUT_xxxx" to "NINE_SLICE_xxxx"
c) Removal of "PREVIEW_FRAME" and "RANDOM_START_FRAME" in the backwards-compatibility code
d) Removal of the "LEFT" and "RIGHT" conditions
e) Addition of "INITIAL_FRAME RANDOM" as recognized (previously, no special keyword, but a negative number would have the same effect - negative number still works, because it'd be extra effort to make it not work, but advise against using it as "RANDOM" is clearer)
f) "HIDE" specified without a corresponding STATE, will set the state to PAUSE (previously STOP)

In terms of what the editor needs to know to account for these:
a) Nothing, for now. The editor still needs to, for now, be able to understand either format.
b) Keyword change
c) The editor can remove the code (if any) for handling these two keywords
d) The editor can remove the code (if any) for handling these two conditions
e) The editor should recognize "INITIAL_FRAME RANDOM" and treat it as equivalent to a negative INITIAL_FRAME, however the editor handles those (player-side, a random frame is chosen, but editor side a rule like "always frame 0" might make more sense)
f) Nothing. HIDE + PAUSE and HIDE + STOP are equivalent as far as the editor needs to account for (there are subtle differences player-side - when the animation becomes visible again, it'll be on frame 0 in the case of HIDE + STOP, but on whatever frame it was on before disappearing (or the initial frame, if it's never animated or been STOP'd yet) in the case of HIDE + PAUSE).
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

Simon

If DISABLED is one-shot trap that has been used, then rename to EXHAUSTED. It's the same principle as behind the exit that closes after a given number of exiters.

Will read more tomorrow.

-- Simon

namida

QuoteIf DISABLED is one-shot trap that has been used, then rename to EXHAUSTED. It's the same principle as behind the exit that closes after a given number of exiters.

I proposed this earlier: Remove "DISARMED" (which I think is what you're thinking of here - "DISABLED" has a much larger scope, though does include such a situation), and expand the definition of "EXHAUSTED" to include such traps instead. As far as I can tell, with no changes to "DISABLED" (ie: a single-use trap fulfills this condition when either used-up or disarmed), anything possible with the "DISARMED" keyword (fulfilled by a single use trap when disarmed, but not when used-up) is also possible with the proposed expansion of the "EXHAUSTED" keyword (fulfilled by a single use trap when used-up, but not when disarmed) - any such setup using the current keywords (which can be changed without BC concerns, as they don't yet exist outside of experimentals and experimental features are never guaranteed), reverse the order of the DISABLED and DISARMED conditions, then replace DISABLED with EXHAUSTED, and DISARMED with DISABLED.

Some changes to be aware of on other object types:
- "DISABLED" and "DISARMED" are currently identical for infinite-use traps. However, I feel "EXHAUSTED" should never be true for these (thus, disarmed infinite-use traps can only be detected via the DISABLED condition).
- I feel it would make sense for "EXHAUSTED" to be true on a pickup skill that's been used. I have no particularly strong preference either way as to whether "DISABLED" also remains true for such; it's currently that way because no other rule allows detecting a used-up pickup skill, and DISABLED's general rule fits better than any other rule does. (Although maybe based on the "general rule" argument, DISABLED should remain true as well for pickup skills that are used up.)
- Same logic as pickup skills, also goes for unlock buttons.

The changes described in this post are not yet implemented; just proposals.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

Nepster

QuoteDISABLED vs. DISARMED vs. EXHAUSED
To make namida's suggestion somewhat easier to digest, I tried to create a table for the current implementation.
- "--" means that this situation can never happen
- "disarmed" means that a lemming with a disarmer skill worked on it
- "used" means that (one or more) lemmings interacted with the object normally and due to that it can no longer interact

   Type    | DISABLED | DISARMED         | EXHAUSTED
-----------+----------+------------------+-----------
Inf. Trap  | disarmed | disarmed         |  --
Once Trap  | disarmed | disarmed or used | used
Pickup     | used     |  --              | used
Button     | used     |  --              | used
Limit Exit |  --      |  --              | used

The suggestion seems to be something like:

   Type    | DISABLED | EXHAUSTED
-----------+----------+-----------
Inf. Trap  | disarmed |  --
Once Trap  | disarmed | used
Pickup     |  --      | used
Button     |  --      | used
Limit Exit |  --      | used

Please correct me (or just edit this post), if I made mistakes in summarizing your post. I am in favor for the change (i.e. having only DISABLED and EXHAUSED).

Quote from: namida on May 19, 2019, 08:25:10 PM
2. Yep, this makes sense. We should have a "Information regarding V12.05.00 for style creators" topic, [...] Perhaps wait until a V12.5.1 update to do this.
The shimmier together with the secondary animations seem large enough to me to go even to V13.0.0. While this might not be totally according to semantic versioning, this might help players to distinguish versions and ensure compatibility between player versions and level packs.

namida

Your "current" table is correct on everything except one - "EXHAUSTED" would currently be "--" for everything except limited entrances / exits. (Exhausted exists only in the limited entrance / exit branch, and is currently only applicable to them.)

For the proposed, DISABLED would not be changed in any way. Your thoughts on "EXHAUSTED" are spot-on.

However - I am now thinking (and this is thinking ahead to the limited exits / entrances again), based on how "DISABLED" is applied in general, it should be true for limited entrances/exits when used up, too. For limited entrances and limited non-lockable exits, the two would thus become identical. For locked exits, there'd be a difference: "DISABLED", but not "EXHAUSTED", is true before the entrance has been unlocked.

See my earlier table (post #42, about half way through the post, behind a spoiler tag) for the general rules I've been applying here, keeping in mind (a) the rules themself can be changed if there's good reason, and (b) there are definitely cases where it's questionable if I applied it consistently, so if you disagree with how I've applied it (and it isn't obviously a case of "this is a situation we need to distinguish, but it's close enough to this, that we don't need an additional condition altogether"), please mention this.

I consider it acceptable for two conditions to have near or full overlap for a certain object type, as long as this is consistent with the general rules of the conditions.

QuoteThe shimmier together with the secondary animations seem large enough to me to go even to V13.0.0. While this might not be totally according to semantic versioning, this might help players to distinguish versions and ensure compatibility between player versions and level packs.

Are we going to increase the major version number every time we eg. introduce a new skill? Or is it mostly the secondary anims you feel this way about? If it's the former, I'd suggest we try to get in any other planned new skills first as well (which IIRC is just the Jumper) - and maybe a few more of the planned features as well. (In exchange, I think it's also fine for a major update to take longer than a normal update to be ready.) I also think a major version upgrade would be a perfect time to introduce the username feature - I recall you wanted more clarification around this; the best place to discuss that would be Proxima's topic about record overwriting from other user's replays, which was the reason for introducing this feature.

In this case, I suggest we look at back-porting some of the bugfixes and minor features (like mass image dumping) to make a V12.4.1. If we go with such a plan, I'm happy to work on preparing such an update. (EDIT: See the v12.4.1 branch on my repo. This should integrate all bugfixes and minor features (restored image dumping, slightly nicer alpha blending, external and directional-select cursors, solid color objects / lemmings in clear physics mode), without pulling in the Shimmier, secondary animations, or nine-slicing.)

EDIT: Okay, implemented this now.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

namida

Okay, I've made some updates to this.

Spoiler
  OBJECT TYPE     | gatcUnconditional (no condition)
  ----------------|-----------------------------------
  GENERAL RULE    | Always true, for all objects
  Anything        | Always true


  OBJECT TYPE     | gatcReady (READY)
  ----------------|-----------------------------------
  GENERAL RULE    | The condition will be true if the object would able to interact with a lemming at this moment
  DOM_EXIT        | True when the exit's lemming limit has not been reached, or if the exit has no limit
  DOM_TRAP        | True when the trap is idle (but not disabled)
  DOM_TELEPORT    | True when the teleporter and its paired receiver (if any) are idle
  DOM_RECEIVER    | True when the receiver and its paired teleporter (if any) are idle
  DOM_PICKUP      | True when the skill has not been picked up
  DOM_LOCKEXIT    | True when the exit is fully open and the lemming limit has not been reached, or it doesn't have one
  DOM_BUTTON      | True when the button has not been pressed
  DOM_WINDOW      | True when the window is fully open, and if it has a lemming limit, hasn't yet reached it
  DOM_TRAPONCE    | True when the trap has not yet been triggered (or disabled)
  All others      | Always true


  OBJECT TYPE     | gatcBusy (BUSY)
  ----------------|-----------------------------------
  GENERAL RULE    | The condition will be true when the object is transitioning between states, or currently in use
  DOM_TRAP        | True when the trap is mid-kill
  DOM_TELEPORT    | True when the teleporter, or its paired receiver, are mid-operation
  DOM_RECEIVER    | True when the receiver, or its paired teleporter, are mid-operation
  DOM_LOCKEXIT    | True when the exit is in the process of opening
  DOM_WINDOW      | True when the window is in the process of opening
  DOM_TRAPONCE    | True when the trap is mid-kill
  All others      | Always false


  OBJECT TYPE     | gatcDisabled (DISABLED)
  ----------------|-----------------------------------
  GENERAL RULE    | The condition will be true when the object is unable to interact with a lemming, either permanently or
                  | until some external condition is fulfilled.
  DOM_EXIT        | True if the exit has a lemming limit and it has been reached
  DOM_TRAP        | True if the trap has been disabled (most likely by a disarmer)
  DOM_TELEPORT    | True if no receiver exists on the level
  DOM_RECEIVER    | True if no teleporter exists on the level
  DOM_PICKUP      | True if the skill has been picked up
  DOM_LOCKEXIT    | True while the exit is in a locked state, or if the exit has a lemming limit and it has been reached
  DOM_BUTTON      | True when the button has been pressed
  DOM_WINDOW      | True if the window has a lemming limit and it has been reached
  DOM_TRAPONCE    | True when the trap has been disabled (most likely by a disarmer) or used
  All others      | Always false


  OBJECT TYPE     | gatcExhausted
  ----------------|-----------------------------------
  GENERAL RULE    | True if an object with limited uses has been used up.
  DOM_EXIT        | True if the exit is limited-use and has zero remaining uses
  DOM_PICKUP      | True if the skill has been picked up
  DOM_LOCKEXIT    | True if the exit is limited-use and has zero remaining uses
  DOM_BUTTON      | True when the button has been pressed
  DOM_WINDOW      | True if the window is limited-use and has released all lemmings
  DOM_TRAPONCE    | True when the trap has been used
  All others      | Always false


Footnotes:
- DOM_NONE and DOM_BACKGROUND should logically be false for READY, and true for DISABLED, based on the general rule. They're not because (1) it's a bit silly to be using trigger conditions on those object types anyway, (2) it's simpler to code by letting them fall into the default, and (3) it's possible, by somewhat convoluted logic, to claim this way around is correct.
- In any branch without limited-count entrances and exits, EXHAUSTED is always false for DOM_EXIT, DOM_LOCKEXIT and DOM_WINDOW.
- I'm on the fence as to whether infinite windows should also be deemed not-READY + DISABLED + EXHAUSTED when no more lemmings remain to be released. Currently, a limited-count window will be all three of these once its remaining lemming count reaches zero. In fact - maybe the answer is to go a step further, and for future levels, only use limited-count windows, with the lemming count being determined based on the total of windows and preplaced lemmings. (This cannot also be done for the save requirement, as doing so would remove the ability to have multiple exits without a specific requirement of how many lemmings go to each.)
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

namida

I've now implemented defined behaviour, for cases where an image is to be drawn nine-sliced, to a target rectangle smaller than the nine-slice margins. I think how this works is easier to explain with examples. I'm only going to explain it in terms of the horizontal slicing; the vertical works exactly the same way (except with top / bottom / height as applicable, rather than left / right / width).

Example 1
Let's suppose we have an object with a left nine-slice margin of 10px, and a right nine-slice margin of 8px (18px total margin). We're trying to draw this resized to 13px wide.

This gives 5px of overlap. Half of this is removed from each side; in the case of an odd value, the remainder is taken off whichever side has the greater margin. So in this case - 3px is taken off the left margin, and 2px is taken off the right margin.

The object will be rendered, with the left 7px and the right 6px.

Example 2
Let's suppose we have an object with a left nine-slice margin of 3px and a right nine-slice margin of 12px. We want to draw this resized to 6px wide.

If we follow Example 1's formula, we end up with -1px from the left and 7px from the right. Of course, we can't really draw -1 pixels. Instead, if a margin is pushed under 0, it gets set to exactly 0, with the other side reduced to compensate. So in this case - we'd get 0px from the left, and 6px from the right.


While the extreme cases of this seem unlikely in real-world situations, I have already found a use case that benefits from more moderate usage of this behaviour.




That aside, I've also implemented an explicit test for "we're trying to resize in one direction, when the center area size in that dimension is zero". For example, if an object that's 32 pixels wide, has a left and right nine-slice margin of 16px each - there's no center area, so NeoLemmix will now raise an exception (whereas previously, it would have either crashed with a generic exception or gotten stuck in an infinite loop) if you try to draw this nine-sliced horizontally to a larger size. However - assuming it has a non-zero center area vertically, nine-slicing it vertically would still be 100% valid, as would drawing it with a smaller horizontal size (as this doesn't need a center area).


Both of the above changes are implemented in commit e697982.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)