[SOURCE CODE]Modified AGS Engine and Editor

Started by helios123, Fri 13/05/2011 20:40:34

Previous topic - Next topic

helios123

Hi all,

I have been experimenting with the AGS source code for the past couple of days, in order to understand the working/code structure better. I decided the best place to start was by attempting to merge code from plug-ins (this and that) I had written into the engine.
Spoiler

The idea here being that plug-in code gives you an idea of where to look in the engine's source code for a particular piece of code, which happens on certain occasion, e.g. Any function where plug-in's AGS_EngineOnEvent is fired by passing AGSE_SAVEGAME as one argument is the function (or is called by the function) where the save game is written to the disk.
[close]

Without further waste of time, here are the changes carried out:

The change in the editor end is merely update of the script header with the new script function declarations. The AGS manual source has also been updated and is included in the download.

The following changes have been made:


  • Support for setting Custom Properties at runtime. The user set values are saved to the save game file when the game is saved and read back from the save game file when the game is restored.
         
    For rooms having room number greater than 300 (i.e. the non state-saving rooms), custom properties for hotspots, objects and room are reset to their initial values once the player character leaves the room.
         
    A call to the ResetRoom function will also reset the custom properties of the room as well as the objects and hotspots in that room.



  • GetRoomProperty has been renamed to Room.GetProperty. The old name has been retained for backward compatibility, but it won't show up in the editor's autocomplete list.


  • The method signature and behaviour of SetTimer has been changed slightly. The new declaration of SetTimer is:
         
    int SetTimer(int timer_id, int timeout);
           
    The behaviour is as below:
         
    Attempts to start timer TIMER_ID ticking. If timer TIMER_ID is not available, then it attempts to start a timer which is free.
    Returns the id of the timer actually started, or -1 if no timer is available.
         
    The main reason for this change is to allow the use of SetTimer in module scripts without any side-effects. For example, if the module script contins a statement like SetTimer(10, 100) and some other (room or global) script also contains a call to SetTimer setting the same timer id, e.g. SetTimer(10, 20), then the behaviour of both scripts becomes unpredictable (Although this scenario seems like a rare case, it is certainly possible in large games where the call to SetTimer itself depends upon some event whose exact time of occurrence can't be predicted, e.g. when a randomy wandering NPC enters the room).
         
    The suggested usage of this new SetTimer is as below:
    #define TIMEOUT 100
    int timer_id = 10;
         
    //initialization code
    timer_id = SetTimer(timer_id, TIMEOUT);
         
    //in repeatedly_execute or repeatedly_execute_always
    if(IsTimerExpired(timer_id)) {
     //do whatever is required to be done
      timer_id = SetTimer(timer_id, TIMEOUT);
    }
         
    function UpdateTimeout(int timeout)
    {
      UpdateTimer(timer_id, timeout); //see below
    }


  • A new method named UpdateTimer has been added. This method updates the timeout value of any running timer to a new value. A brief description follows.
         
    bool UpdateTimer (int timer_id, int timeout)
         
    Attempts to update the TIMEOUT value of the TIMER_ID. If TIMER_ID refers to an OFF timer, or timeout is not positive, then it does nothing.
    Returns true if the TIMEOUT for TIMER_ID was successfully updated, else returns false.
         
    The timer denoted by TIMER_ID must be a running timer (i.e. it must have been started by calling the SetTimer function) and its TIMEOUT should not have elapsed (no call to IsTimerExpired should not have returned true).
         
    TIMEOUT should be a positive value.

Update (July 19, 2011): Following changes also done:


  • AudioClip has two new static members for iterating over all the AudioClips in the game.
          These are:
          01. Count, which returns the number of AudioClips in the game, and
          02. GetAtIndex, which returns the AudioClip at a specified index.


  • Each AudioClip object also has a ChannelID property. This will be the id of the channel on which the AudioClip is playing. If the AudioClip is not playing, then ChannelID will be -1. This can be used in conjunction with System.AudioChnnels array to obtain the channel of any clip which is playing.


  • The 'Print dialog options upwards' in the game's General Settings dialog works as expected.


  • EXPERIMENTAL: The Import Font dialog can now load OpenType fonts (*.otf) as well. The same code which imports TrueType Fonts is used for this.


  • SOURCE ONLY: Some data mangling in save games is now possible. Define MANGLED_SAVE_GAMES whle compiling. See restore_game_data and save_game_data in ac.cpp.

See also the ReadMe included in the downoad.

Comments, criticisms and suggestions are most welcome.

Download links:
That's all for now,
helios123

timofonic

Hello.

I find posting this in a separate thread a total nonsense. I would recommend post it on the other forum threads.

Anyway, it seems interesting :)

Regards

helios123

Quote from: timofonic on Sat 14/05/2011 01:49:35
Hello.

I find posting this in a separate thread a total nonsense. I would recommend post it on the other forum threads.

Anyway, it seems interesting :)

Regards

There is a separate thread for engine related changes and another one for editor related changes, but no thread for changes made to BOTH the engine and the editor. Hence, I thought it better to post a separate thread.
That's all for now,
helios123

Lt. Smash

Very nice work helios! Another big step forward to AGS 4.0 :)

For OO's sake I would suggest using a new class "Timer".
Like Timer.Set(), Timer.Update()....
By the way it would be cool, if you could "classify" all of the non-object-oriented functions. Like
AbortGame (Game.Abort), AreThingsOverlaping (Room.AreThingsOverlaping), CallRoomScript (Game.CallRoomScript), CDAudio (?), ClaimEvent, CyclePalette (new Palette class perhaps?), Debug (Game.Debug(enum)), DeleteSaveSlot, DisableInterface, FadeIn/Out, FlipScreen, FloatToInt, (lower) game.variables, ...

Keep up the good job ;)

monkey0506

    Quote from: helios123 on Fri 13/05/2011 20:40:34The following changes have been made:
    • Support for setting Custom Properties at runtime. The user set values are saved to the save game file when the game is saved and read back from the save game file when the game is restored.

    Good. I like this, exactly as you described it.

    Quote from: helios123 on Fri 13/05/2011 20:40:34
    • GetRoomProperty has been renamed to Room.GetProperty. The old name has been retained for backward compatibility, but it won't show up in the editor's autocomplete list.

    Good, but I have a question. When you say it won't show up in the autocomplete, did you use $AUTOCOMPLETEIGNORE$ or did you include it between #ifdef STRICT and #endif? The autocomplete is specifically designed to work with the STRICT macro for these type of backwards-compatible commands, you shouldn't manually hide it entirely as it's inconsistent with the way other backwards-compatible commands are handled. Some actually have had their imports removed entirely, but that's the least friendly way of moving forward and then requires modules like SSH's BackwardsCompatibility to redefine the imports (for the functions which still exist). ;) Also, note that using the STRICT macro as I described would break existing scripts, but it's a simple fix with a "Find & Replace", and is the most consistent way of doing it.

    Quote from: helios123 on Fri 13/05/2011 20:40:34
    • The method signature and behaviour of SetTimer has been changed slightly.
           
      The main reason for this change is to allow the use of SetTimer in module scripts without any side-effects.

    • A new method named UpdateTimer has been added.

    I actually don't like this at all. There is zero reason for a module writer to rely on the built-in SetTimer functions. Those functions should be exclusively used by the game author, not a module author. Modules can create an integer variable and update that in repeatedly_execute, repeatedly_execute_always, or wherever else they may need an update. This actually gives them a finer level of control over the timer in question, and it doesn't obstruct the end-user's code in any way.

    Quote from: helios123 on Fri 13/05/2011 20:40:34
    • AudioClip has two new static members for iterating over all the AudioClips in the game.
            These are:
            01. Count, which returns the number of AudioClips in the game, and
            02. GetAtIndex, which returns the AudioClip at a specified index.

    These actually seem congruent with a recent suggestion of mine, but I disagree with the implementation, because it seems inconsistent with the way other items are handled in AGS. I would actually suggest keeping the two members, but renaming them:

    - Game.AudioClipCount (from AudioClip.Count)

    This is consistent with Game.CharacterCount, Game.DialogCount, Game.FontCount, Game.GUICount, Game.InventoryItemCount, Game.MouseCursorCount, Game.ViewCount, and System.AudioChannelCount.

    - Game.AudioClips[int index] (from AudioClip.GetAtIndex)

    This is consistent with Game.GlobalMessages, Game.GlobalStrings, and System.AudioChannels.

    Keeping things in a consistent naming convention is important.

    Quote from: helios123 on Fri 13/05/2011 20:40:34
    • Each AudioClip object also has a ChannelID property. This will be the id of the channel on which the AudioClip is playing. If the AudioClip is not playing, then ChannelID will be -1. This can be used in conjunction with System.AudioChnnels array to obtain the channel of any clip which is playing.

    I actually don't know how I feel about this one. I haven't tested it, but I believe it should actually be possible to have an AudioClip playing simultaneously on more than one channel. I can't think of a reason why you would want to do that, but I can't think of a technical reasoning why you shouldn't be able to. Also, why return an integer ID instead of an AudioChannel* directly? What would seem more in-alignment with the current naming conventions (to me at least) would be something more like AudioChannel* Game.GetAudioChannelByPlayingClip(AudioClip*). I don't have any strong ties to that particular name, but something along those lines would seem more consistent to me.

    Quote from: helios123 on Fri 13/05/2011 20:40:34
    • The 'Print dialog options upwards' in the game's General Settings dialog works as expected.

    Was it not working before? I'm absolutely certain it was working before.

    Quote from: helios123 on Fri 13/05/2011 20:40:34
    • EXPERIMENTAL: The Import Font dialog can now load OpenType fonts (*.otf) as well. The same code which imports TrueType Fonts is used for this.

    Support for more types of fonts is good! :) I'd also like it if somehow we could introduce a FontSize property to the various items that use fonts (applicable only to TTF and the newly supported OTF font types, ignored otherwise), so we wouldn't have to import our fonts at a static size for use throughout the whole game.

    Quote from: helios123 on Fri 13/05/2011 20:40:34
    • SOURCE ONLY: Some data mangling in save games is now possible. Define MANGLED_SAVE_GAMES whle compiling. See restore_game_data and save_game_data in ac.cpp.

    What exactly is this for? "data mangling" sounds like it could lead to some issues in the save game state..but then I've never really heard the term coined before, so I have no idea what it's for.

    In any case, this seems to me like you've mostly done some great work. My main point of concern is just the consistency with the existing conventions. Once that's addressed I feel that moving the global pointers and functions to a more OO-system would be a good move. Especially, as Lt. Smash said, some of the game.* variables. Those have next to no documentation (outside of a single line per property listed on a single page), and many of them are quite largely overlooked. One cool item from the list that comes to mind is the text_shadow_color property which Ben304 actually told me about years ago. I had no idea it existed (and I think most people don't!).

    In addition to that, I would also like to see a lot of the arbitrary int values for things like the Debug function and unhandled_event ported to enumerated values. Particularly for the latter I have (on occasion) set up a custom function that takes CursorMode and LocationType values instead of the what and where integers, and linked it through the built-in unhandled_event manually. Of course that also brings up the fact that LocationType treats InventoryItems as eLocationNothing, which I don't understand. My items are something! They are something!! :=

    Anyway, overall good job. ;)

    Shane 'ProgZmax' Stevens

    If I'm reading it right, he's using 'data mangling' to mean a quick and dirty form of savegame encryption.  I could be wrong.

    helios123

    Thanks for the feedback!

    Quote from: ProgZmax on Thu 21/07/2011 01:13:11
    If I'm reading it right, he's using 'data mangling' to mean a quick and dirty form of savegame encryption.  I could be wrong.

    That is correct. I added this when I found that any global strings, or custom properties, are written to the save game files as they are, and can be easily read using, say, a hex editor.

    Another thing I found is that most of the data is read in blocks (e.g. fread(&play,sizeof(GameState),1,ooo) ; ), the size being given using the sizeof operator. This means that we cannot change the struct definitions of the built-in types, as the data written by the editor when compiling the game (this is done by AGS.Native, I think), will not be read properly by the engine.


    Quote from: monkE3y_05_06
    Good, but I have a question. When you say it won't show up in the autocomplete, did you use $AUTOCOMPLETEIGNORE$ or did you include it between #ifdef STRICT and #endif?

    I used $AUTOCOMPLETEIGNORE$


    I will probably take a second look at the AudioClip related changes and give a thought about more object oriented naming conventions in my spare time ... :)


    Regarding the Print dialog options thing...

    When that option is selected, the expected behavior (I believe) should be that the first dialog option (corresponding to @1) should be displayed last, the second one should be the second-last, and so on, and the last dialog option should be the first. I found that this was not happening (at least in my case. The code change is actually very small (in the methods run_dialog_script and write_dialog_options in ac.cpp).


    As far as the game.* variables are concerned, I believe someone more well versed with the source code should take that up (maybe I'll give it a try when I have more in-depth knowledge about the workings of the game.* variables).


    That's all for now,
    helios123

    SMF spam blocked by CleanTalk