AGS 3.6.2 - Beta 5 -- a WIP 3.6 update

Started by Crimson Wizard, Mon 07/10/2024 23:28:58

Previous topic - Next topic

Crimson Wizard

#20
I need an advice in regards to the new scripting API.

In the version we have an ability to do different file sorting when filling in list boxes using ListBox.FillDirList(). And I'm also currently working on another function which scans for saves and returns a dynamic array with slot numbers (for the purpose of prescanning valid saves etc, but that's a long story). It also can do sorting. This made me think that it's not a too bad idea to add optional sorting args to ListBox.FillSaveGameList() as well.

Currently there are two types of file sorting implemented:
- by name
- by time (modification time)

Here's where I realized that in case of saves there is an ambiguity in what to consider a "name". Initially I thought filename, because it also matches the slot number due to how save files are called; which would make "sort by name" = "sort by slot number".

But now, since ListBox is affected, and when ListBox is filled with saves it fills save descriptions, I begin to have doubts.
Is the description also valid case for sorting saves?

If yes, then how to deal with this situation?

I see 3 variants:
1. Consider sorting by "Name" be sort by description in case of filling ListBox, and loose ability to sort by a slot number.
2. Add another sorting style by "Index" or "Number" into the list of file sorts (it will be called e.g. "eFileSort_Index"), so have both sort by slot number and description.
3. Create a completely separate "save sorting style" which will have sorting by SlotNumber, Description or Time.


EDIT: Hmm, i'm gradually leaning towards separate enum, i.e. SaveSortStyle, because files may in theory be sorted by parameters that are not meaningful for saves at all (like size on disk), and saves may require specific ways to order too.

Crimson Wizard

#21
Updated to Beta 3
(Please use download links in the first post)

Includes all the fixes from 3.6.1 Patch 8.

Own changes:

Editor:
 - In "Start New Game Wizard" game template selection now displays template's description right in the dialog.
 - "Game statistics" now have Ctrl + F2 shortcut instead of F2.
 - F2 can be used for starting editing names of items in the Project Tree (ones that support that) and folders in the Sprite Manager.
 - Added a multiline Text edit window for Button and Label controls, that may be called by Ctrl+E, from context menu, or by pressing "..." button of the Text property in the Properties grid.
 - In Room Editor, during any area drawing mode Ctrl + LMB now works as area picker regardless of the currently selected tool.
 - Editor will now report any missing script functions that are assigned to events but not present in script as warnings when compiling the game.
 - Fixed font preview not updating after importing a new font over. (regression in 3.6.2 Beta)
 - Fixed editing Color property of multiple selected GUI controls.

Scripting:
 - Dynamic arrays now have Length readonly property that returns their number of elements.

Script API:
 - Events eEventGUIMouseDown and eEventGUIMouseUp now get additional parameters: mouse button, mouse x and y positions.
 - Added RestoredSaveInfo struct which contains information about game save's contents,
 - Support "validate_restored_save" callback, which lets user to validate restored saves with mismatching data and request cancellation or continuation of restoring this save.
 - Added FileSortStyle and SortDirection enum, and File.GetFiles() function that returns a dynamic array of filenames found using certain pattern, and optionally sorted by name or time, in ascending or descending order.
 - Added SaveGameSortStyle enum and Game.GetSaveSlots() function that returns a dynamic array of save slot indexes, optionally sorted in certain way.
 - Added Game.ScanSaveSlots() that scans a range of game saves and tests them for compatibility, using "validate_restored_save" too if it's present in user script. This action is not run immediately, but is scheduled to be executed after current script ends. It reports of scanning completion by sending eEventSavesScanComplete event.
 - Added optional "fileSortStyle" and "sortDirection" parameters to ListBox.FillDirList().
 - Added optional saveSortStyle and sortDirection parameters to ListBox.FillSaveGameList().
 - Added ListBox.FillSaveGameSlots(), which initializes a list of saves from a dynamic array of save slot indexes.
 - Added Object.DestinationX and DestinationY properties, complementing ones in Character.
 - Added SendEvent() function that allows to trigger "on_event" function calls in script. Special event value eEventUserEvent may be used as a base index for user-defined events.
 - Add SaveComponentSelection enum and game option OPT_SAVECOMPONENTSIGNORE, which lets to skip certain components when saving or restoring a game. This has dual purpose: reduce number of things in game that may break older saves by simply not having them in a save, and also reducing the size of game saves on disk (e.g. in case of dynamic sprites). Note that all things that were not restored from the save will retain their *current state*.

Engine:
 - Engine now potentially supports reading saves made in older game versions with different content, so long as these saves have *less* data in any given type (i.e. less Characters, or less controls on a certain GUI, or less variables in script, and so forth). This feature is enabled by having a special "validate_restored_save" function anywhere in the game script, that accepts an argument of type RestoredSaveInfo. RestoredSaveInfo contains various information about the restored save, and "Cancel" property, that must be explicitly set to "false" in script in order to allow to restore such save.
 - Engine supports returning to previously saved rooms which have less script data. It's important to remember though that it does no validation of restored room state on its own.
 - Engine no longer quits with error in case a script function assigned to a Room event is missing. Logs warnings about missing script functions assigned to any events.
 - Support calling StopDialog() inside a dialog script, that will schedule dialog's stop command at the end of the current option script.
 - Added new config setting to "access" section called "textreadspeed" which lets to override game's text reading speed parameter.
 - Fixed calling Dialog.Start() inside a dialog script would create a nested "dialog state", which could eventually lead to internal mistakes and program stack overflow. Dialog.Start() will now schedule a proper dialog topic switch, equivalent to "goto-dialog" command.
 - Fixed Character may get stuck in "move without animating" state if Move called before Walk. (regression in 3.6.2 Beta)
 - Fixed 8-bit room bgs in 32-bit game are imported filled with "magenta". (regression in 3.6.2 Beta)
 - Fixed crash on game quit if there was any queued music. (regression in 3.6.2 Beta)

WinSetup:
 - Added "Text reading speed" to "Accessibility" settings tab.
 - Fixed setup could save wrong setting values in categories located on other tabs, if player never opened these tabs.
 - In "disabled" section of config "access_skipstyle" setting lets disable all options in setup related to the speech and text skipping.



This update finally adds the feature that was one of the reasons to plan 3.6.2 release (at least for me): a support for loading saves from older game version. This function is currently limited in what it can do: you can only load saves with *less* data, and it will be stable so long as you did not change the order of previously existing things. That's why it's primarily purposed for patching already released games, and not for creating multi-chapter games or games with DLCs, for example (although, I suppose, one could try to pull that off too with certain effort).

This is still experimental, and requires testing potential use cases.

Another complementary feature is a function that *prescans* saves without restoring them. This allows to test which saves are "safe" to load prior to displaying them in the save/load menu, for example.

All this have to be properly documented and explained. Unfortunately I'm too tired now, so for the moment I'll give two links to the tickets in our github tracker, which contain overview of these features:
* Support loading older saves: https://github.com/adventuregamestudio/ags/pull/2489
* Prescanning saves: https://github.com/adventuregamestudio/ags/pull/2579

And the forum thread that discusses this all:
https://www.adventuregamestudio.co.uk/forums/engine-development/load-older-game-saves-into-updated-game-attempt-2/



OTOH I'd very much prefer to wrap things up in 3.6.2, stop adding things to it, and focus on testing, fixing and documenting what's already added.

eri0o

On fixing things, we need Length to appear in autocomplete for dynamic arrays and also check if any updates are required in the templates - adding additional data parameters in on_event, check if mouse position handling is done using in event position parameters when possible, descriptions.

I guess if we plan at least one more beta release I could upgrade the dependencies versions - notably SDL2, to see if things are alright.

Crimson Wizard

#23
Well, there still may be few minor additions, for example I wanted to try adding a starting monitor selection in setup.

Also, we could update to VS 2019 at least (which would change the required VC runtime).

There may be as many Beta updates as necessary - until we're certain that things are stable.

Crimson Wizard

I am in doubts about something again...

In the latest update I've expanded eEventGUIMouseDown and eEventGUIMouseUp events with following parameters:
- mouse button
- mouse x
- mouse y

the position is given in screen coordinates.

Would it be better to have them in respective GUI coordinates instead?

eri0o

I think if there's a need there could be an API for that like Point* GetRelativePosition(int x, int y). Does GUI.ProcessClick(int x, int y) uses screen or GUI relative coordinates? (I don't remember)

Crimson Wizard

#26
Quote from: eri0o on Sun 24/11/2024 21:18:21I think if there's a need there could be an API for that like Point* GetRelativePosition(int x, int y). Does GUI.ProcessClick(int x, int y) uses screen or GUI relative coordinates? (I don't remember)

There's no need for this in ags3, as there are no complex transformations, it's simply a matter of subtracting GUI.X or Y. There's already a function for this in ags4, where gui may have more transformations.

But I'm asking not because there is a problem of converting, but because I cannot decide what makes more sense in the context of event params.
Also, I don't remember if it's now in this particular case, but at least in theory triggered events may be processed not instantly, but with certain delay, in which case it may be important to describe the exact state of the context in which the event occured.


EDIT: GUI.ProcessClick uses screen coordinates, because it's not called for specific gui, but for the whole "gui layer".

greg

Quote- Support reordering folders in Project Explorer with drag & drop.

Does this feature include reordering folders in the Sprites tab?

I just installed the latest beta, and I'm not able to reorder my sprites folders.

eri0o

Nope, sprites is implemented very differently. The changes are in the project explorer, where now you can drag things and move them between things there.

greg

While testing out 3.6.2 Beta 3, I've noticed an issue with array scoping.  Here are the reproduction steps:

  • An array is defined within a top-level script.  Its values can be set/get via functions in that same script.
  • A lower-level script sets a value in the top-level script's array via a call to the top-level script's set function.
  • When the lower-level script reads the value from the array via the top-level's script's get function, it gets the value it just set.  However, when the top-level script reads the value directly from the array, it gets the value that preceded the lower-level script's set call.

Here's some sample code:

topMostScript.ash

Code: ags
#define MY_INDEX 1

import function DisplayValues(int value);
import function GetGlobalInt(int index);
import function SetGlobalInt(int index, int value);

topMostScript.asc

Code: ags
int g_global_ints[10];

function DisplayValues(int value) {
  Display(String.Format("%d:%d", g_global_ints[MY_INDEX], value);
}

function GetGlobalInt(int index) {
  return g_global_ints[index];
}

function SetGlobalInt(int index, int value) {
  g_global_ints[index] = value;
}

lowerLevelScript.asc

Code: ags
function test() {
  SetGlobalInt(MY_INDEX, 1);
  
  // I'd expect this to display "1:1". Instead, it displays "0:1".
  DisplayValues(GetGlobalInt(MY_INDEX));
}


Crimson Wizard

#30
@greg that's because SetGlobalInt and GetGlobalInt are engine API functions, so engine actually calls its own functions instead of yours.

https://adventuregamestudio.github.io/ags-manual/Globalfunctions_General.html#getglobalint

Simply rename the functions, and you will see expected values.


The problem is that engine is not made to differentiate between selected script api levels, and always links its deprecated api, even if not requested by the game settings. I think this issue may be already recorded in our bug tracker, and it must be resolved.

Alternatively, you may still import these old engine functions by declaring them, but not defining their bodies.

eri0o

Also the above code won't compile because it's parentheses are unbalanced.

Crimson Wizard

#32
Quote from: eri0o on Sat 30/11/2024 16:44:44Also the above code won't compile because it's parentheses are unbalanced.

That's just a typo in a forum post. The problem in the engine is real.

EDIT: That's quite strange, I had memories of someone having these deprecated functions recreated in their game scripts. How would they work then, I wonder. Maybe these are false memories, and it was something else.


greg

Thanks, CW!  I'd been using the built-in, long-deprecated GetGlobalInt and SetGlobalInt in 3.6.1.  When I upgraded to 3.6.2, I got a compilation error of "undefined symbol 'GetGlobalInt'", so I'd assumed the functions had finally been removed and created my own version with the same name.

I've renamed my version to GetGlobalIntQ and SetGlobalIntQ, and now everything is working as expected.  Thanks!

Crimson Wizard

Quote from: greg on Sat 30/11/2024 17:49:22Thanks, CW!  I'd been using the built-in, long-deprecated GetGlobalInt and SetGlobalInt in 3.6.1.  When I upgraded to 3.6.2, I got a compilation error of "undefined symbol 'GetGlobalInt'", so I'd assumed the functions had finally been removed and created my own version with the same name.

No, they were not removed, they were hidden under the script API setting, as they should have long time ago.

If you are actually using these in your games, they may be enabled again, if you wish, like any of the deprecated functions:
https://adventuregamestudio.github.io/ags-manual/GeneralSettings.html#backwards-compatibility
These two in particular require "Enforce post-2.62 scripting" set to false.

Alternatively, you may explicitly declare these functions in any of your headers. This will allow scripts to use them, since they are still available in the engine.


greg

Understood, thanks!  In anticipation of 4.0 (which will remove GetGlobalInt, SetGlobalInt, and all deprecated functionality), I've switched over to my version rather than relying on deprecated functions.

Crimson Wizard

Quote from: greg on Sat 30/11/2024 18:00:02Understood, thanks!  In anticipation of 4.0 (which will remove GetGlobalInt, SetGlobalInt, and all deprecated functionality), I've switched over to my version rather than relying on deprecated functions.

Is there a reason why are you not using normal global variables instead?

greg

QuoteIs there a reason why are you not using normal global variables instead?

With Game.InputBox(), I've created a mini-console that allows me to view and edit global variables while play testing.

For example, I can parse input string "SetGlobalInt 1 10" and use that to call SetGlobalInt(1, 10).  To do this, I parse out tokens "SetGlobalInt", "1", and "10", use a switch statement to map "SetGlobalInt" to SetGlobalInt(), and use String.AsInt to convert "1" and "10" to 1 and 10.

If I switched to the normal global variables, I'd need to parse "SetGlobalInt myVar 10" and use that to call myVar = 10.  To do this, IIUC, I'd need a switch statement that mapped every global variable's name ("myVar") to the global variable itself (myVar), and that's more cumbersome than referring to global variables by index.

Crimson Wizard

Quote from: greg on Fri 29/11/2024 16:07:07
Quote- Support reordering folders in Project Explorer with drag & drop.

Does this feature include reordering folders in the Sprites tab?

I just installed the latest beta, and I'm not able to reorder my sprites folders.

I wrote a variant that supports dragging Sprite folders, copying the Project Tree functionality;
if you like you may download this temporary build and test it out:
https://cirrus-ci.com/task/5728199629340672

SMF spam blocked by CleanTalk