adventuregamestudio / ags

AGS editor and engine source code
Other
707 stars 159 forks source link

Script API: SaveGameSlot with arbitrary sprite + make screenshots of specific render layers #2526

Closed ivan-mogilko closed 2 months ago

ivan-mogilko commented 2 months ago

For #2509

This adds couple of related features:

  1. Added support for making screenshot with selected render layers:
enum RenderLayer
{
  eRenderLayerNone      = 0x00000000,
  eRenderLayerEngine    = 0x00000001,
  eRenderLayerCursor    = 0x00000002,
  eRenderLayerUI        = 0x00000004,
  eRenderLayerRoom      = 0x00000008,
  eRenderLayerAll       = 0xFFFFFFFF
};

DynamicSprite* CreateFromScreenShot(int width=0, int height=0, int layer=eRenderLayerAll);
  1. Changes the SaveGameSlot as:
void SaveGameSlot(int slot, const string description, int sprite = -1);

The SaveGameSlot will use provided sprite number if it's >= 0, or make a standard screenshot if it's < 0. Note that since saving game is scheduled until after the script finishes, the image gets copied, the copy is stored internally until saving is done. This makes this call safe even if you use dynamic sprite and delete one too early.

In regards to function linking, original AGS compiler had a quirk that it did not add argument number postfix to the imported global function names (not belonging to a class). This makes it impossible to recognize whether the running game expects an old or new function. For that reason I had to make a switch, which links one of the function variants depending on a "Script API level" setting.

In combination with CreateFromScreenShot update, this lets do:

DynamicSprite *shot = DynamicSprite.CreateFromScreenShot(Screen.Width, Screen.Height, eRenderLayerRoom);
SaveGameSlot(slot, description, shot.Graphic);
  1. Also added a new game option OPT_SAVESCREENSHOTLAYER which may be set at runtime as
SetGameOption(OPT_SAVESCREENSHOTLAYER, RenderLayer);

This option affects the default screenshot made by SaveGameSlot, IF OPT_SAVESCREENSHOT is on, and NO custom sprite passed as an argument.

ivan-mogilko commented 2 months ago

Added support for making screenshot with selected render layers:

enum RenderLayer
{
  eRenderLayerNone      = 0x00000000,
  eRenderLayerEngine    = 0x00000001,
  eRenderLayerCursor    = 0x00000002,
  eRenderLayerUI        = 0x00000004,
  eRenderLayerRoom      = 0x00000008,
  eRenderLayerAll       = 0xFFFFFFFF
};

DynamicSprite* CreateFromScreenShot(int width=0, int height=0, int layer=eRenderLayerAll);

In combination with previous SaveGameSlot update, this lets do:

DynamicSprite *shot = DynamicSprite.CreateFromScreenShot(Screen.Width, Screen.Height, eRenderLayerRoom);
SaveGameSlot(slot, description, shot.Graphic);
ericoporto commented 2 months ago

What is layer engine here? Is it above the cursor?

I will test this tonight to see how it works.

ivan-mogilko commented 2 months ago

What is layer engine here? Is it above the cursor?

FPS and debug layers iirc.

ericoporto commented 2 months ago

https://github.com/user-attachments/assets/d0da875d-7e9b-40ae-b207-2e68576e009b

Hey, the new screenshot allows to do some pretty cool hackery with overlays! So far this appears to work.

What's the idea of screenshot without the room layer? Like, should the empty pixels be transparent or be black? When I try to do the screenshot with only the engine or the cursor layer I get black but I think I am getting transparent for the UI layer. This is not a problem, as I think they will mostly be used along the room layer, but I was curious.

ivan-mogilko commented 2 months ago

What's the idea of screenshot without the room layer? Like, should the empty pixels be transparent or be black? When I try to do the screenshot with only the engine or the cursor layer I get black but I think I am getting transparent for the UI layer.

I did not have much idea about this, but the result should depend on what renderer does. I assumed that it should have a clear black screen underneath. But maybe it has not a 0xFF000000 black, but 0x00000000 black, in which case it's fully transparent. I also have not tested with software renderer, it might have its quirks.

ivan-mogilko commented 2 months ago

Also added OPT_SAVESCREENSHOTLAYER option that may be set as SetGameOption(OPT_SAVESCREENSHOTLAYER, RenderLayer), and this affects making a default screenshot when SaveGameSlot is called with no custom sprite.

ericoporto commented 2 months ago

Also added OPT_SAVESCREENSHOTLAYER option that may be set as SetGameOption(OPT_SAVESCREENSHOTLAYER, RenderLayer), and this affects making a default screenshot when SaveGameSlot is called with no custom sprite.

This is not working for me, I still get it with all layers rendered. I tried this

```AGS Script function game_start() { SetGameOption(OPT_SAVEGAMESCREENSHOTLAYER, eRenderLayerRoom); } int prev_slot = -1; DynamicSprite* spr; void update_screenshot(ListBox* lbx_saves, Button* btn_thumb) { int index = lbx_saves.SelectedIndex; if(lbx_saves.ItemCount <= 0 || index < 0) return; int slot = lbx_saves.SaveGameSlots[index]; if(prev_slot == slot) return; prev_slot = slot; spr = DynamicSprite.CreateFromSaveGame(slot, Screen.Width, Screen.Height); spr.Resize(btn_thumb.Width, btn_thumb.Height); btn_thumb.NormalGraphic = spr.Graphic; } function repeatedly_execute_always() { if(lstRestoreGamesList.OwningGUI.Visible) update_screenshot(lstRestoreGamesList, btnScreenShot); if(lstSaveGamesList.OwningGUI.Visible) update_screenshot(lstSaveGamesList, btnSaveScreenShot); } ```

Here is the game project and binary: TestScreenshotSave.zip

ivan-mogilko commented 2 months ago

This is not working for me, I still get it with all layers rendered. I tried this

I don't know which exactly problem affected the above test project, but i fixed couple of mistakes.

ericoporto commented 2 months ago

Finally had time to test this! Yes, all working now!!

And I think this now fixes #2509 completely too