Author Topic: [Suggestion] Ability to BLIT objects by script.  (Read 5003 times)

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
[Suggestion] Ability to BLIT objects by script.
« on: March 18, 2023, 06:18:11 pm »
This is a kind of big one, but it feels like it has a lot of possibilities. Correct me if I'm wrong, but I've dived in pretty deep to the scripting but there isn't anyway to do this at the moment. Instead scripts can either recolor sprites or select a new sprite to be displayed. XPZ uses the second style to achieve basically this for wounded/stunned markers on corpses. The ability to blit a sprite on top of the existing sprite would allow this to be done without having to generate a ton of extra sprites for this purpose. There are lots of other concepts it would be useful for, displaying data about an items state on the paper doll, or even maybe a numerical "damage" display when a soldier gets hit.

So I've been digging around a lot in the scripting section of the code to get a sense of what would be necessary to allow this. But this section is pretty wooly so it's somewhat slow going (not an insult, just the nature of the beast). I haven't started a fork yet (don't worry!) but I have dug around enough that I feel I could at least make an attempt at an approach, though I feel it would be somewhat suboptimal.

Using the existing selectSprite scripts as a guide, I could implement a similar method that exposed similar data, but the return value(s) would be used to identify another sprite to blit onto the current sprite. I could imagine making this pretty flexible in terms of returning data that identifies the sprite, sprite library, and possibly X/Y coordinates (probably... maybe). The blit operation would then be handled using these return values after the execution of the script.

The limitation here is it would allow for blit of only a single item. This is probably good enough for most use-cases, but I figure if we're going to do this feature, might as well make it full featured.

Alas while I have several ideas about how a more full-featured blit could be done, I don't currently see how I would implement them... yet. If people don't hate this idea, I'm willing to work on it though.

Any pointers as to a better approach would be appreciated. The ideas I currently have are:
 * pass in a pointer to a "blit instruction" stack or something. The stack could be filled up blit instructions (and maybe other instructions?) that would get executed after the script completes. Upside is I could imagine how to approach this. Downside is it's basically building a script engine within a script engine, which seems extra pointless.
 * Have the mutations happen while the script is live? A blit operator. This was my original idea but that static operators are, obviously, static, and would lack the proper contexts, which means it would need to be provided by the script execution engine. Maybe this is something I can crib from the recolor scripts? I haven't dove into them yet.
 * Pass in the surface? This seems reasonable and the script could mutate it to its hearts content without much risk. The issue, again, is lack of context. A surface object lacks easy access to the other sprites. I suppose the appropriate SurfaceSet could also be passed in, though passing data between the two via the script seems like a dubious idea (if perhaps possible).

In all cases this operation would run as a separate script, probably after the selectSprite scripts have run (in the same context though). Anyways, thoughts welcome.

Offline Meridian

  • Global Moderator
  • Commander
  • *****
  • Posts: 9075
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #1 on: March 18, 2023, 06:33:25 pm »
This is Yankes' area, I'll leave the reply to him.

But I'll at least link a related request: https://openxcom.org/forum/index.php/topic,7642.0.html

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #2 on: March 18, 2023, 10:06:53 pm »
Yeah this would have the ability to do that (and more). Though honestly a pure overlay might be a better fit for the existing recolor script, depending upon exactly the method wanted.

After more digging and pondering, I think I've decided (subject to other input, of course) that passing the script the Surface object (and binding some of its methods) is probably the best bet. It's virtually risk free and offers maximum flexibility with little extra work. The problem remains how to reference other sprites/surfaces for blit-ing. Natively they are stored in mod, which seems not that desirable to expose (though I'm still learning about how this all works).

I'll keep poking.

Edit: And I'm dumb, I see there *are* some exposed methods on Mod... interesting.
« Last Edit: March 18, 2023, 10:36:54 pm by MaxMahem »

Offline Yankes

  • Global Moderator
  • Commander
  • *****
  • Posts: 3346
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #3 on: March 19, 2023, 03:52:06 pm »
What exactly is use case you want to handle by this?

To have very basic custom biting in script should be very simple to add, you register `Surface` and add couple of helpers that blit one surface to another.
But question is again where you would like to use it?

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #4 on: March 19, 2023, 04:23:30 pm »
What exactly is use case you want to handle by this?
Oh man, lots of things. But I had two primary use cases in mind. One I would like to do immediately, one maybe sometime later:
 * being able to more easily give feedback on an item's state via script. Right now this can be done by selecting another sprite which involves creating another sprite, which can be tedious if you want to apply it to a lot of sprites and/or give information on a lot of states. Or via recolor which has limitations.
 * being able to give feedback on events in the battle state. Right now this can be accomplished via recolor and its "hit flash" which is very cool, but if you could blit sprites/draw on the surface you could essentially display arbitrary information. One thing I'd like for my personal use is to flash a number of damage taken to the user (I know some people hate the concept but allowing for this ability lets me write a script that people who do like it can use, and people who don't like it don't have to).

But if the surface is exposed via script potentially you can draw in almost arbitrary ways, depending upon what functionality is enabled. So there is a lot of potential. Meridian mentioned above someone wanting some sort of overlay graphic when a shield was active (Doing this via recolor is cool, but an overlay would also be cool).

Quote
To have very basic custom biting in script should be very simple to add, you register `Surface` and add couple of helpers that blit one surface to another.
This is the approach I've settled on. Just a bit hard to parse where all the different pieces get added. So far I've added the necessary binding to Surface and Mod (to allow for finding other sprites, though those sprites would be read-only).

Now I just need to figure out (am figuring out) how to add another script to call, how to pass it arguments, etc (basically you need the current battleState, itemState, and the sprite). I suppose in theory this should return a surface though really since all the modifications are happening to the surface, I guess I would hope that it could just be treated as an "out" parameter basically. Returning a pointer seems redundant.

And of course, all the things I didn't know I didn't know while working on this.

Offline Yankes

  • Global Moderator
  • Commander
  • *****
  • Posts: 3346
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #5 on: March 19, 2023, 09:08:00 pm »
Ok, this could work, but we should be careful when creating scripts like this to not interfering with engine. This mean you should be able to do any thing in script but game should be still playable. Another is "const correctness" and determinism.

What is place where you want exactly add this new hook?

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #6 on: March 20, 2023, 07:06:17 am »
What is place where you want exactly add this new hook?

My thinking was more or less immediately after the `selectSprite` scripts run in BattleItem and... BattleUnit (I haven't investigated that one yet, I assume the code is similar, though).

I feel like I'm about 90% of the way there right now. What I've done so far:

# Binding (this all looks to be working)
Added two bindings to Mod to allow retrieval of arbitrary surfaces (either by name or library/index)
Added two bindings to Surface to allow Blit-ing and writing of text.
Added the bindings of the Surface pointer type to the appropriate registers.

# execution
Added my new parser and its constructor.
Added the call (just sending the raw sprite for now).

I'll be honest this has been pretty rough going, though I feel I'm 90% of the way there. I went ahead and pushed my fork up. I haven't added all the calls just this one to test with. You can see it here..

This is, uh, coincidently, where I'm still having some trouble. Getting an error about unable to convert to initialize list, which I assume means I've got a type error between the call and the ctor someplace, but I ain't seeing it. (Unless there is something else I'm missing, totally possible).

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #7 on: March 22, 2023, 03:17:05 am »
Okay thanks to some helpful advice from Yankee's I got a proof of concept working. You can find the branch here (keep in mind it's still a WIP and git gives me grief so I may force push again unexpectedly).

Currently, I have this working only on the inventory (and alien inventory) screens, though adapting the code to the hand-sprites and the unit-sprites should be straightforward (famous last words, lol). A little screenshot to show the capabilities:



This was created with the following two scripts:
Code: [Select]
extended:
  scripts:
    transformItemSprite:
      - new: ItemBlitTest
        offset: 1
        code: |
          var int id;
          var int width;
          var int height;

          item.getId id;
          if eq id 12; # Heavy Cannon
            var ptr Surface indicator;
            rules.getSpriteFromSet indicator "BIGOBS.PCK" 21; #prox-grenade

            surface.getWidth width;
            surface.getHeight height;
            loop var x width;
              loop var y height;
                var int tempX x;
                var int tempY y;
               
                mul tempX INV_SLOT_WIDTH;
                mul tempY INV_SLOT_HEIGHT;

                surface.blitSprite indicator tempX tempY 0;
              end;
            end;
          end;
         
          return;

      - new: XitOutTest
        offset: 2
        code: |
          var ptr RuleItem ruleItem;
          var int id;
          var int width;
          var int height;

          item.getId id;
          if neq id 12; # Heavy Cannon

            item.getRuleItem ruleItem;
            ruleItem.getInvWidth width;
            ruleItem.getInvHeight height;

            mul width INV_SLOT_WIDTH;  # item width
            mul height INV_SLOT_HEIGHT; # item height

            surface.drawLine 0 0 width height COLOR_X1_RED;
            surface.drawLine 0 height width 0 COLOR_X1_RED;
          end;         
          return;

Basically, this allows you to "blit" items onto items, essentially arbitrarily. As well as having access to some basic geometry drawing. What I haven't got working yet (but would like to before I'm done) is the ability to draw text. (Well actually I do have it working, but not in a way that pleases me yet).

Multiple scripts can run against the same item and stack their overlays. They will stack in order of execution. (IE, the last script to run will be the one on top).

What I'd like some feedback on is two issues:
  • Is there any other functionality I should consider while I'm here doing this?
  • Right now, I have limited the script's ability to draw to the boundaries of the sprite. For item sprites, this is probably always what is wanted, but for unit sprites, I can see the case where you might want to draw beyond those boundaries.

Also, here are a couple of things to consider now that I've got the groundwork in place:
  • These sorts of scripts should easily be able to duplicate some of the OG, OXC, and OXCE effects. For example, the primed grenade indicator, two-hand indicator, ammo counts, body wound indicator, or any overlay currently handled in code. I think we should seriously consider pulling these out and implementing them via these sorts of scripts instead. This would reduce the branching in the C++ code necessary to handle this stuff, as well as making these sorts of effects open to customization by mods as desired.

    This would become more valuable as more code elements are opened up to scripting. As I see it, feedback to the player on the scripted item state is a major roadblock currently. Being able to remove or override these elements with new behavior can really help here, I think.

    I'm willing to do the work to convert these effects if given the thumbs up. This should be completely backward compatible to boot.
  • With this capability in place, a next step might be to allow blit-ing onto the entire screen. This could be used to give feedback on persistent game state information, for example a turn-countdown. I can't see at the moment an easy way to capture a player click into a script, but I still think there could be some uses. Anyways, probably a separate project, but something to think about. The hard part is (probably) done.

Offline Yankes

  • Global Moderator
  • Commander
  • *****
  • Posts: 3346
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #8 on: March 22, 2023, 11:44:43 am »
I open to adding functionality like this to engine, but thing that prevent me from doing it was hammer out all details and semantic of this new script hook.

I could say that your experimental version fulfill around 60% thing I would like to have in this new hook(s).


For moving some logic to scripts, yes some thing could be moved there without any problem, but some other I would keep hardcoded.
This depend how complex it is and how fundamental is. Prime grade I would keep fixed, but status indicators (stun & overstun) could be moved to scripts.

Btw when you are toying with scripts and inventory, I have some unfinished business with paper-dolls, one biggest missing functionality is lack of animations and custom script blitting, I had plans to add both but not enough time to do it. Would you be interested to checking it too?

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #9 on: March 22, 2023, 12:27:16 pm »
I open to adding functionality like this to engine, but thing that prevent me from doing it was hammer out all details and semantic of this new script hook.

I could say that your experimental version fulfill around 60% thing I would like to have in this new hook(s).
;D But bare in mind, I'm not done yet. I actually changed up the semantics in my current working version (calling the hook an "spriteOverlay" instead of a transformation, which better describes what it is I think).

I also definitely want to get text rendering in a 1.0 version. I just finished getting numberText rendering done. And I have yet even to touch the unit-sprite side of things (though I hope it should be straightforward after I get all the kinks worked out here).

I've also added a different "handOverlay" hook. Because the hand slots have some potential differences in how they might be scripted. A hand slot is 2x3, always, with the item centered, while an item sprite doesn't have to be that size (I know you know this but just for anyone else listening in :P). For an item in the hand slot, both script hooks are called. I can't see this being changed, but we reference the constant in case it ever is.

So yeah, still more to come, but please let me know what else you think this could use.

Quote
For moving some logic to scripts, yes some thing could be moved there without any problem, but some other I would keep hardcoded.
This depend how complex it is and how fundamental is. Prime grade I would keep fixed, but status indicators (stun & overstun) could be moved to scripts.
I'd really like to push hard for moving out as much as possibly can be. Prime grenade is a great example of something that someone could want to put a custom way of giving user feedback on, but doing so will be blocked or at least at great deal more complicated if this logic is hard-coded.

For example, lets say I wanted to change the high-explosive sprite (or use my own sprite) to have a count-down display (number of turns till explosion). This seems like a reasonable and really cool feature to have. But doing so would be tough unless the prime-grenade overlay could be disabled. And since you've already coded in the "override script" functionality, it seems like all the pieces are already in place to make this happen, except for this one. On that specific item, they override the script with their custom behavior, while all the other items get the default behavior.

I actually already went ahead and wrote a script that could mimic the effect as a proof of concept.
Code: [Select]
extended:
  scripts:
    inventorySpriteOverlay:
      - new: GrenadeIndicator
        offset: 1
        code: |
          var int timer;
          var int width;

          # the original logic guarded against 0 width here
          # not sure why but we preserve that check.
          item.getFuseTimer timer;
          if or ge timer 0 neq width 0;
            var int shade anim_frame;
            var int fuseEnabled;
            var int newColor 0;

            var ptr Surface indicator;
            rules.getSpriteFromSet indicator "SCANG.DAT" 6;

            item.isFuseEnabled fuseEnabled;
            if eq 0 fuseEnabled;
              set newColor 32;
            end;

            # this generates a shade offset in the [0, 1, 2, 3, 4, 3, 2, 1] pattern.
            wavegen_tri shade 8 8 4;
            overlay.blitShadeRecolor indicator 0 0 shade newColor;
          end;
          return;

If you want to cross-reference with the current code you'll see they are basically the same, minus a lambda and using your wavegen_tri method instead of the array (though I guess one could argue that a script is just a really big expensive lambda :P). Graphically the two effects are basically identical, minus some potential differences in initial sequence timing which is irrelevant.


(Top one is new script, bottom one is old).

Likewise, a thing I think I might like to do is write a script that replaces the "2 hand" indicator with maybe an indicator that gives the numerical percentage minus to accuracy. Or what if I wanted to change the ammo indicator to have a custom color depending on the ammo type? (Maybe even a brief letter or symbol to represent it?) Lots of potential here I think, but they necessitate opening up some flexibility in the program, which this feature would allow.

The alternative to this is coding in a flag that conditionally disables these elements. But that requires someone to write that code, it to be accepted, and still adds more branching in the code, making it messier, and adds yet another option to the increasingly long list. I'm hardly one to talk as I've done my part in making this situation worse, but I think we should increasingly be looking at ways these options can be reduced and more easily managed. Scripting is one way of doing that.

Quote
Btw when you are toying with scripts and inventory, I have some unfinished business with paper-dolls, one biggest missing functionality is lack of animations and custom script blitting, I had plans to add both but not enough time to do it. Would you be interested to checking it too?
I'm open to looking at it, I'll just need to know more specifically what you want to have happen?

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #10 on: March 28, 2023, 01:40:51 am »
So I've made some progress though I got a bit sidetracked with some restructuring, though that is mainly behind me now. I'm at the point where some more feedback on what default behavior is desirable.

So to give a point of reference again, I'm reimplementing default behavior via script so that it can be modifiable. There are currently 5 different effects I'm considering:

  • The grenade primed indicator. - This applies at the "sprite" level and is always present.
  • The 2-hand indicator. - This applies when the weapon is in a "hand slot"
  • The ammo indicator. - This applies only to the battlescape "HUD" and not the inventory screen.
  • The corpse-state indicator (wounded, stunned, etc...) applies only to a body on the ground in the inventory screen. (You only see these if you have extra sprites that represent them added to your game).
  • The corpse mortal wound indicator. - This applies only to a body on the ground in the inventory screen.

Since I am reimplementing these effects, there is room to adjust (or not) this behavior. So my question specifically are:
  • Should the ammo indicator show up when an item is in hand in the inventory? (It can probably not show up at other times, as weapon sprites are often not large enough for it.
  • Should the corpse-state indicator appear when an item is in inventory? As opposed to just on the ground.
  • Should the corpse wound count indicator appear when an item is in inventory? As opposed to just on the ground.
I still need to implement the ammo indicator so there may yet be some "gotcha" for doing it in the inventory that is not readily apparent yet. However, I think it should be possible.

I have implemented the corpse indicators, and having them in other locations in the inventory works fine as long as any corpse remains 3x2 or so, like the vanilla corpses. The display will become rather cramped if used on a smaller corpse. However, this would also be present in the current code, although it would only happen when the corpse is on the ground.

Offline Meridian

  • Global Moderator
  • Commander
  • *****
  • Posts: 9075
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #11 on: March 28, 2023, 09:08:00 am »
Since I am reimplementing these effects, there is room to adjust (or not) this behavior. So my question specifically are:
  • Should the ammo indicator show up when an item is in hand in the inventory? (It can probably not show up at other times, as weapon sprites are often not large enough for it.
  • Should the corpse-state indicator appear when an item is in inventory? As opposed to just on the ground.
  • Should the corpse wound count indicator appear when an item is in inventory? As opposed to just on the ground.

None of these are needed.
I would have done them already if they were required/requested :)

So to give a point of reference again, I'm reimplementing default behavior via script so that it can be modifiable. There are currently 5 different effects I'm considering:

  • The grenade primed indicator. - This applies at the "sprite" level and is always present.
  • The 2-hand indicator. - This applies when the weapon is in a "hand slot"
  • The ammo indicator. - This applies only to the battlescape "HUD" and not the inventory screen.
  • The corpse-state indicator (wounded, stunned, etc...) applies only to a body on the ground in the inventory screen. (You only see these if you have extra sprites that represent them added to your game).
  • The corpse mortal wound indicator. - This applies only to a body on the ground in the inventory screen.

There is also no need to re-implement the existing behavior using scripts.
It is enough to add ability to add new behavior using scripts.

Vanilla grenade indicator, fatal wound indicator and ammo indicator are very unlikely to be changed by anyone. (If someone disagrees, please let me know or create a poll.)
OXCE 2-hand indicator can be made optional if it would disturb someone (I don't have such feedback yet).
OXCE corpse-state indicators are already all optional and moddable... and can't/shouldn't be re-implemented, because of backwards-compatibility with mods that are already using them.

And for completeness, the OXCE preferred reaction weapon indicator is also optional and moddable and can't/shouldn't be re-implemented.

Unless I misunderstood what  "reimplementing" means here.

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #12 on: March 28, 2023, 11:25:58 am »
There is also no need to re-implement the existing behavior using scripts.
It is enough to add ability to add new behavior using scripts.

Vanilla grenade indicator, fatal wound indicator and ammo indicator are very unlikely to be changed by anyone. (If someone disagrees, please let me know or create a poll.)
Being able to adjust or reconfigure some of these indicators (primarily the primed grenade indicator and the ammo indicator) is a major rationale for this feature. Examples:

* A high-explosive that displayed its "primed" state instead via display of its timer on its sprite. Or an armed black-powder bomb via a lit fuse. Or a proximity mine via a flashing light.
* A "charged" weapon might display its ammo state via a bar showing a charge percent.
* Weapons with multiple ammo types might display the ammo in a different color as a hint to the ammo type loaded (just today, I had to switch back to the inventory screen to confirm this information).
* The two-hand indicator could be optionally replaced with one that displays the current percentage penalty (or bonus) for a shot taking into account known static factors (kneeling, other hand being occupied) from the items rule.

All of this behavior and more is achievable within the new scripting framework, but it relies upon the ability to disable and/or reconfigure the existing OXCE behavior, preferably on a per-item basis. This is what I've been working to achieve.

Quote
Unless I misunderstood what  "reimplementing" means here.
To make the current OXCE behavior configurable and disableable, by necessity, some of the code that generates these effects must be changed or "reimplemented." I've been working with Yankes on the best way to do that. It is still somewhat up in the air if it is more preferable for these effects to be implemented via script (as some other behavior is now via scripting) or "hardwired" C++ code with flags accessible to the script to enable or disable the default behavior. Or some combination of the two. In any case, the visual appearance and the way the features work for the end user are no different. It has just changed in the backend.

But since, in this process, these effects have been abstracted out in a more general manner to make them more easily configurable, I think it is worth considering to what degree maintaining all aspects of the current behavior is desirable.

Specifically, the three indicators I mentioned are probably useful in other locations but are not currently displayed in these other locations. It seems to me that bringing these effects to the other locations where they could locally appear would be a minor improvement, and doing so would make the code simpler to boot. But as it is a change from the current behavior, I thought it worthwhile to bring up the issue.

Ultimately, if someone is dead-set on this information *not* being displayed in this minorly different manner for whatever reason, then that could be achieved as the entire system is designed to be configurable.

Quote
And for completeness, the OXCE preferred reaction weapon indicator is also optional and moddable and can't/shouldn't be re-implemented.
This and other elements that are more part of the "UI" you might say, and not dependent upon the items themselves are not touched by this patch. However, the groundwork will be in place to make such modifications easier down the road if that is desired. Ultimately I think making the UI configurable via script to the maximum degree possible is desirable, but it's not the feature I am putting forth today.

Offline Meridian

  • Global Moderator
  • Commander
  • *****
  • Posts: 9075
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #13 on: March 28, 2023, 12:09:28 pm »
Being able to adjust or reconfigure some of these indicators (primarily the primed grenade indicator and the ammo indicator) is a major rationale for this feature. Examples:

* A high-explosive that displayed its "primed" state instead via display of its timer on its sprite. Or an armed black-powder bomb via a lit fuse. Or a proximity mine via a flashing light.
* A "charged" weapon might display its ammo state via a bar showing a charge percent.
* Weapons with multiple ammo types might display the ammo in a different color as a hint to the ammo type loaded (just today, I had to switch back to the inventory screen to confirm this information).
* The two-hand indicator could be optionally replaced with one that displays the current percentage penalty (or bonus) for a shot taking into account known static factors (kneeling, other hand being occupied) from the items rule.

All of this behavior and more is achievable within the new scripting framework, but it relies upon the ability to disable and/or reconfigure the existing OXCE behavior, preferably on a per-item basis. This is what I've been working to achieve.

This is fine.
The ideas are nice, and if somebody is interested in having a custom primed grenade indicator, I support it too.
We should allow disabling of the vanilla indicator(s) (globally and/or per item) and adding custom indicator(s) via scripting.

My comment was primarily about the modders who don't want a custom indicator.
They should not be forced to reimplement the existing behavior using scripts, the existing behavior should still work without modders having to do anything.

To make the current OXCE behavior configurable and disableable, by necessity, some of the code that generates these effects must be changed or "reimplemented." I've been working with Yankes on the best way to do that. It is still somewhat up in the air if it is more preferable for these effects to be implemented via script (as some other behavior is now via scripting) or "hardwired" C++ code with flags accessible to the script to enable or disable the default behavior. Or some combination of the two. In any case, the visual appearance and the way the features work for the end user are no different. It has just changed in the backend.

But since, in this process, these effects have been abstracted out in a more general manner to make them more easily configurable, I think it is worth considering to what degree maintaining all aspects of the current behavior is desirable.

Specifically, the three indicators I mentioned are probably useful in other locations but are not currently displayed in these other locations. It seems to me that bringing these effects to the other locations where they could locally appear would be a minor improvement, and doing so would make the code simpler to boot. But as it is a change from the current behavior, I thought it worthwhile to bring up the issue.

Ultimately, if someone is dead-set on this information *not* being displayed in this minorly different manner for whatever reason, then that could be achieved as the entire system is designed to be configurable.

If the devs have to reimplement something, that's of course OK.
I just wasn't sure if modders would be forced to reimplement or not (Hint: they should not be forced).

If you're already working with Yankes on this then you're in good hands.
He has full authority to decide what gets or doesn't get into OXCE (and how), and he's doing a good job at it, probably even better than I :)

Offline MaxMahem

  • Captain
  • ***
  • Posts: 60
    • View Profile
Re: [Suggestion] Ability to BLIT objects by script.
« Reply #14 on: April 01, 2023, 06:31:09 pm »
Okay, so I've made some good progress and can show off some effects. Plus, some people thought sharing this with a wider audience might be beneficial. The current code fork is here and working, though still not done and WIP.

So with this new scripting capability, you can essentially draw anything onto the screen and even animate it to a degree. However, as a practical matter, "bliting" or drawing a sprite on top of another sprite is probably the most useful. Some example effects.

By replacing a weapon like the heavy cannon ammo clip sprite with a new "empty" sprite, you can then blit that full sprite onto it. But then crop that sprite as ammunition is expended to show that ammunition has been expended. For example:


As shown in the example, only one base clip needs to be prepared, and you can apply the same effect to all of them. With a well-designed script, you can even use the same principle across different weapon types, for example...



These sorts of "diegetic" elements can render the same across all locations the item is rendered (except the ufopedia), even if you pick the item up with your cursor. However, you can also control when they take effect if you like.

In any case, the script to produce this effect is fairly straightforward.

Code: [Select]
extended:
  tags: # Remember to add these tags to the same file as items ruleset
    RuleItem:
      NEW_BIGOB: int    # new clip. This will be the empty clip.
      CURRENT_MOD_OFFSET: RuleList
      # CROP_MULT_L or CROP_MULT_R should be 0 if that side of the crop is static.
      CROP_MULT_L: int  # amount to crop left per spent round
      CROP_MULT_R: int  # amount to crop right per spent round (negative if feeding from left)
      CROP_OFFS_L: int  # amount to offset the crop from the left.
      CROP_OFFS_R: int  # amount to offset the crop from the right.

  scripts:
    selectItemSprite:
        # replaces an existing sprite with a new one, everwhere but the ufopedia.
      - new: SpriteReplacement
        offset: 2
        code: |
          if eq blit_part blit_item_big;
            var int newSprite;
            var ptr RuleItem ruleItem;

            item.getRuleItem ruleItem;
            ruleItem.getTag newSprite Tag.NEW_BIGOB;

            if neq newSprite 0;
              var int spriteIndex;
              var int modOffset;
              set sprite_index newSprite;
              ruleItem.getTag modOffset Tag.CURRENT_MOD_OFFSET;
              rules.getSpriteOffsetBigobs sprite_index modOffset;
              return sprite_index;
            end;
          end;
          return sprite_index;

    inventorySpriteOverlay:
      # the script works by bliting the a cropped portion of a full clip onto an image of an empty clip
      # (replaced by script in SpriteReplacement, so the correct image shows in ufopedia)
      # the values need to be set so that the cropOffL and cropOffR represent the min and max x
      # of the full clip to display, in the weapons current state.
      - new: ShowUsedAmmo
        offset: 1
        code: |
          var ptr Sprite fullclip;
          var ptr RuleItem ruleItem;
          var int cropMulL;   # the amount to crop per round on the left side
          var int cropMulR;   # the amount to crop per round on the right side
         
          item.getRuleItem ruleItem;
          ruleItem.getTag cropMulL Tag.CROP_MULT_L;
          ruleItem.getTag cropMulR Tag.CROP_MULT_R;

          if or neq cropMulL 0 neq cropMulR 0;
            var int ammoLeft;    # the number of rounds remaining.
            var int ammoMax;     # the max number of rounds.
            var int cropOffL;    # the amount to offset the left crop.
            var int cropOffR;    # the amount to offset the right crop.
            var int fullClipId;  # id of the original cip

            item.getAmmoQuantity ammoLeft;
            ruleItem.getClipSize ammoMax;
            ruleItem.getTag cropOffL Tag.CROP_OFFS_L;
            ruleItem.getTag cropOffR Tag.CROP_OFFS_R;

            # calculate the crops The formula is. cropOff = (ammoMax - ammoLeft) * cropMul + cropOff
            sub ammoMax  ammoLeft;  # ammoMax  = ammoMax - ammoLeft
            mul cropMulL ammoMax;   # cropMulL = cropMulL * ammoLeft
            mul cropMulR ammoMax;   # cropMulR = cropMulR * ammoLeft
            add cropOffR cropMulR;  # cropOffR = cropOffR + cropMulL
            add cropOffL cropMulL;  # cropOffL = cropOffL + cropMulL

            # get the original clip sprite
            ruleItem.getBigSpriteIndex fullClipId;
            rules.getSpriteFromSet fullclip "BIGOBS.PCK" fullClipId;

            # blit the original sprite onto the empty sprite
            # with a crop from (cropOffL, 0) to (cropOffR, 47)
            overlay.blitCrop fullclip cropOffL 0 cropOffR 47;
          end;
          return;

items:
  # heavycannon ammo appears to feed from the right, 4px per round.
  # Starting 6px from the left. The right side is fixed at 31px.
  - type: STR_HC_AP_AMMO
    tags:
      NEW_BIGOB: 201 # empty AC ammo.
      CURRENT_MOD_OFFSET: UsedAmmo
      CROP_MULT_L: 4
      CROP_OFFS_L: 6
      CROP_MULT_R: 0
      CROP_OFFS_R: 31
  - type: STR_HC_HE_AMMO
    tags:
      NEW_BIGOB: 201 # empty AC ammo.
      CURRENT_MOD_OFFSET: UsedAmmo
      CROP_MULT_L: 4
      CROP_OFFS_L: 6
      CROP_MULT_R: 0
      CROP_OFFS_R: 31
  - type: STR_HC_I_AMMO
    tags:
      NEW_BIGOB: 201 # empty AC ammo.
      CURRENT_MOD_OFFSET: UsedAmmo
      CROP_MULT_L: 4
      CROP_OFFS_L: 6
      CROP_MULT_R: 0
      CROP_OFFS_R: 31

  # autocannon ammo appears to feed from the left, -2px per round.
  # Starting 31px from the left. The left side is fixed at 0px.
  - type: STR_AC_AP_AMMO
    tags:
      NEW_BIGOB: 202 # empty AC ammo.
      CURRENT_MOD_OFFSET: UsedAmmo
      CROP_MULT_L: 0
      CROP_OFFS_L: 0
      CROP_MULT_R: -2
      CROP_OFFS_R: 31
  - type: STR_AC_HE_AMMO
    tags:
      NEW_BIGOB: 202 # empty AC ammo.
      CURRENT_MOD_OFFSET: UsedAmmo
      CROP_MULT_L: 0
      CROP_OFFS_L: 0
      CROP_MULT_R: -2
      CROP_OFFS_R: 31
  - type: STR_AC_I_AMMO
    tags:
      NEW_BIGOB: 202 # empty AC ammo.
      CURRENT_MOD_OFFSET: UsedAmmo
      CROP_MULT_L: 0
      CROP_OFFS_L: 0
      CROP_MULT_R: -2
      CROP_OFFS_R: 31

This effect may not seem very useful (though I like it!). But it demonstrates the ability to write one sort of effect/asset and then reuse it multiple times easily. Doing this with sprite replacement (only) would be a lot more work.

Next up, we have the ability to write numbers onto the interface. Same as numbers are currently written for ammo, medkit charges, etc. These sorts of effects can be recreated exactly (and in previous drafts, they were). So you can use them to define your own sort of interface if wanted. But you can also use them in other creative ways like...



So I hope what's happening here is obvious. But this also demonstrates some limited ways you can animate effects. Since the scripts all take the anim_frame member, if you can build a transformation around that value, you can use it to power an animation. Like the blinking light effect shown here. In addition, you might notice that the normal grenade-primed indicator has been turned off, which is another thing you can do with the scripting.

Code: [Select]
items:
  - type: STR_HIGH_EXPLOSIVE
    bigSprite: 203
    scripts:
      inventorySpriteOverlay: |
        var int timer;

        # unset the grenadeIndicator, we don't want it.
        overlay.unsetOptions OVERLAY_DRAW_GRENADE_INDICATOR;

        item.getFuseTimer timer;
        if ge timer 0;
          var int offsetX 14; # centered in frame for 1 digit
          var int flash anim_frame;
          var int period timer;
         
          # if greater than 10, offset by -2 to allow space
          if ge timer 10;
            sub offsetX 2;
          end;

          # Flash period based on timer. Period starts at 6
          # and increases by 3 every other timer increment.
          # period = (period / 2 + 2) * 3
          div period 2;
          add period 2;
          mul period 3;

          # generate saw wave but only display timer if value greater than 2.
          # this gives a fixed "off" period of 3 ticks (0, 1, 2) with the on period
          # increasing with timer length.
          wavegen_saw flash period period 40;
          if gt flash 2;
            overlay.drawNumber timer 7 5 offsetX 3 55;
          end;
        end;
        return;

One last one, showing a combination of the two effects.



In this example, the normal proximity grenade sprite has been replaced with an "unlit" sprite (replaced via rule file, not script, so the change will appear in the ufopedia). However, when the grenade is primed, we blit another image on top to represent the blinking light. Additionally, that light is shaded using a wave function to give it the "flashing" effect.

The script:
Code: [Select]
items:
  - type: STR_PROXIMITY_GRENADE
    bigSprite: 204 # unlit proximity grenade
    scripts:
      inventorySpriteOverlay: |
        var int timer;

        # unset the grenadeIndicator, we don't want it.
        overlay.unsetOptions OVERLAY_DRAW_GRENADE_INDICATOR;

        item.getFuseTimer timer;
        if ge timer 0;
          var ptr Sprite light;
          var int shade anim_frame;

          rules.getNamedSprite light "Proximity_Grenade_Light";
          wavegen_tri shade 8 8 4;
          add shade -3;
          overlay.blitShade light 0 0 shade;
        end;
        return;

So that's all I got for right now. This is still a WIP, and I still have more to do, but I think these effects are really only scratching the surface of what is possible.

(To be clear, these are just demonstrations of what is possible, not a proposal to change the default behavior that remains unchanged, though I still have the tweaks I suggested earlier).