MODULE: MiniGame V1.00 (update 04-21-08)

Started by RickJ, Sat 23/04/2005 01:01:38

Previous topic - Next topic

RickJ

Abstract
AGS  allows one game to activate another game via the RunAGSGame() script function.  This  is useful in implementing arcade sequences and other game-within-a-game type things. These can be called Mini Games  and  designed  to be run stand-alone or from within another game. 

This module provides an easy method of calling and returning from mini games and an easy method of passing data back and forth. Examples illustrating the use of these  functions  are presented in subsequent sections of this document.

Download:
MiniGame-M0100 (module only)
MiniGame-T0100 (template example)
or  DemoQuest GiP Thread

Description
What does  this  module do that RunAGSGame doesn't do? If you were just going to activate the games from the very beginning each time then this module wouldn't be necessary.  However,  the usual scenario is that the player wants to return to the previous game and game state from where he left.  For example if the player plays an arcade game in a pub,  when he quits playing he ought to find himself in the same spot in the same pub where he was standing just before activating the arcade game.  The MiniGame module makes this as easy as pie.

Game Requirements
The main game and each mini game it calls must have been created with the same AGS version, color depth, and resolution.   All game files must be kept in the same folder.  The main game and all the mini games must use the same version of MiniGame and have Room0.crm be a black/blank room.

Passing Data
When a mini game is called it is often desired to pass data to it.   For example, in some situations it would be desireable to use the player's  money  to  make wagers in the mini game and then return the resulting winings or losses.  The MiniGame module uses a data dictionary to  pass  data to and from mini games. Each data point is associated with a unique name.  Data can be either a string or  an integer and is accessed through the following functions.

   MiniGame.sSet(String name, string value)
   MiniGame.iSet(String name, int value)
   MiniGame.sGet(String name)
   MiniGame.iGet(String name)

Calling a Mini Game
Mini games are called using the MiniGame.Call() function below.  The parameter 
FILENAME specifies the  name of the game file to be called.  It may or may not include file extensions of ".exe" or ".ags".

   // Call a mini game (from a room script or the global script)
   miniGame.iSet("Money",player.InventoryQuanity[iMone.ID]);
   MiniGame.Call("DemoCycle");

In this example, the amount of money in the player's inventory is passed to the mini game. The amount of money is stored in the data dictionary with a key of "Money". Next  "minigame.exe" is  called,  when it  terminates  it  will  return to "mainagame.exe".

Starting the Mini Game
The  mini  game will be loaded and then started as a result of the above MiniGame.Call(). The next thing to do is to determine if the game has been started from the operating  system or if it has been called. The function MiniGame.IsCalled() provids for this.  A value of  true is returned if called from another game and returns false otherwise.  This function is normally used in Global Script's game start event handler, game_start().

   // Mini Game Global Script
   function game_start() {

      // Mini Game startup script
      if (MiniGame.IsCalled()) {         // Read data dictionary
         Money = MiniGame.iGet("Money"); Passed from caller
      }
      else {                             // Run from OS
         Money = 10;                     // Give default amount
      }
   }

In the example above a global variable, Money, is set to the value stored in the data dictionary  under a key "Money" if the game has been called.  It is  set  to a value of 10 if the game was started from the OS.

Quitting a Mini Game
If  the  mini  game has been called then data must be saved in the data dictionary and the calling game re-activated. If the game has been run directly from the OS it can simply quit in the normal way using the QuitGame() function.  The preferred method of doing this  is to create a global function in the global script similar to the example below.  This  function is then called from any interaction functions designated as game exit or quit.

   // Mini Game Global Script
   function ExitGame(int ask_first) {
      MiniGame.iSet("Money",Money);      // Save data and ...
      MiniGame.Return(ask_first);        // return to caller
   }

In  the  example above  the global variable Money is  saved in the data  dictionary  via  MiniGame.iSet(). Next the MiniGame.Return() function  determines  if the  game  was  called  or  run  from the operating system. If the game was called then the  calling game is re-activated  and  the data in the data  dictionary is passed back.  If the game was run from the operating system then it's terminated normally using the AGS QuitGame() function.
 
Saving a Mini Game
The  MiniGame  module makes a call to the Game.SetSaveDirectory(). The specified directory name is formed by extracting the extension from the Game.  FileName string.If this extraction does not result in a useable  string then "SaveGames" is used instead. The success or  failure of  the Game.SetSaveDirectory() call should be checked before executing SaveGameSlot(), RestoreGameSlot(), DeleteSavSlot(), as follows..

   // Save the Game
   if (MiniGame.SaveAllowed()) {
      SaveGameSlot(slot);
   }
   else {
      Display("Error saving game, save directory not found");
   }

Perhaps a better  approach is to simply  remove the Save functions from the Gui at start up using Gui control prperties. 

   // Remove save functions if not allowed
   if (!MiniGame.SaveAllowed()) {
      gOptionsSave.Visible = false;    // Disable the Save button
      gOptionsRestore.Visible = false; // Disable the Load button
   }

Returning from a Mini Game
When  returning  from a mini game, any data it passes back must be read from  the data dictionary. This is done with a series of "if, else-if" statements  in  the  Global Script's repeatedly_execute() event handler function. The MiniGame.IsReturningFrom() function is used in the  conditional expression to determine which if any mini game has returned.
   
   // Main Game Global or Room Script
   function repeatedly_execute() {

      // Script to return from mini game
      if (MiniGame.IsReturningFrom("minigame.exe")) {
         Read data dictionary returned by mini game
         player.InventoryQuantity[iMone.Id] = MiniGame.iGet("Money");
      }
      else if (MiniGame.IsReturningFrom("anotherminigame.exe")) {
         Read data dictionary returned by another mini game
      }
   }

Any  data returned by the mini game can be retrieved and copied to the appropiate  game variables  using  the MiniGame.iGet() and the MiniGame.sGet() functions as shown above.

Note:  THIS MUST BE DONE IN THE REPEATEDLY EXECUTE HANDLER and not in the start game event handler because when returning from a mini game a  RestoreGameSlot()  operation is  performed to return to the point in the game from where the mini game was launched. This will overwrite  all game variables, so the data dictionary must be read after this occurs.  The  MiniGame.IsReturningFrom()  function will return true only after the restore opertion is complete. 

Known Issues
The following issues are known to the author, who can be contacted by posting on the AGS tech forum or by sending PM correspondance.

  • Double Screen Transitions - As of this writing it is not possible to execute RestoreGameSlot() before the first screen is displayed.  This  was briefly mentioned above.  So instead of returning to the  screen from which the mini
    game was launched directly, it returns to  ROOM0 whoose background is  momentarily  displayed  before  the  desired screen is finally displayed  as a result of RestoreGameSlot()'s  execution.  To make this less noticable, ROOM0's background image is a single color of black and contains no script or interactions.  So when running directly from the OS ROOM0 is skipped and the game starts in another room as designated by the player character. When the game starts after returning from a call the GUI and mouse  are turned off and the player is left waiting in a black room.When the RestoreGameSlot() executes  the palyer is transported  to the room from where he called the mini game.

    Nested Calls
    Nested MiniGame.Call()s are  supported but have not been thourghly tested as of this release.  Should  someone try to do nested calls it is likely that it would work and that some issues would come to light.

    API Documentation
    A summary of this module's functions an Properties is given below.  There are three categories of functions as follows:

  • Functions used in the calling game
         - MiniGame.Call()
         - MiniGame.IsReturningFrom()

  • Functions used in the called game
         - MiniGame.Return()
         - MiniGame.IsCalled()
         - MiniGame.SaveSlot()

  • Functions that are used in both
         - MiniGame.iGet()
         - MiniGame.iSet()
         - MiniGame.sGet()
         - MiniGame.sSet()

    static function MiniGame::Call(String filename)
    This function calls a mini game. The file containing the mini game must be in the same directory as the main game.  It must also have the same resolution and color depth as the calling game.  The file name of the mini game is  given by  the FILENAME parameter.

    Save slots  are numbered  from 0 to 999. The game restart function uses slot 999 so it is reserved. The call stack uses utilizes save slots 998 to 998 - MiniGame_STACKSIZE.

    In  debug  mode error messages are displayed if the specified game  files do not exist or if  the call  operation  cannot be completed.  When debug is  disabled these  messages are surpressed. In  either case the following error status is returned as follows.

       Return:
       eMiniGame_Ok                      - call complete successfully
       eMiniGame_MiniFilenameErr  - game file doesn't exist
       eMiniGame_MiniGameDatErr   - can't write to minigame.dat file

    static function MiniGame::IsReturningFrom(String filename)
    This function returns true if the game is starting up as result or returning from a previously called mini game. This fuinction is to be  called  from  the repeatedly_execute() function of the calling game to know when  to  copy  data  from  the memory buffer to game variables.

       Return:
       true                       - just returned from specified game
       false                      - did not return from specified game

    static function MiniGame::Return(int ask_first)
    This function is called from any interaction or custom function to return to the calling game or to quit if the game was started from the operating system.

    static function MiniGame::IsCalled()
    This function returns true if the game was started from the OS and returns false if it was called by another game or is returning from a called game.

       Return:
       false                      - game was started directly from OS
       true                       - game was called from another game

    static function MiniGame::SaveAllowed()
    This function returns true if a save directory was setup correctly.  It  returns  false  otherwise, in which case the main game and the mini games share the same save game space and would interfere with each other.  In  this  case the mini game should be prevented from executing SaveGameSlot() operations. 

       Return:
       false                      - prevent mini games from being saved
       true                       - allow mini games to be saved

    static function MiniGame::iGet(String name)
    This function returns the integer value corresponding to NAME.  If NAME doesn't exist an error mesage is generated.

    static function MiniGame::iSet(String name, int value)
    This function modifies value corresponding to NAME as specified by the VALUE integer parameter.  If NAME  doesn't exist the name-vale pair is appended to the end of the memory  buffer.  If there is no more room and error message is generated.

    static String MiniGame::sGet(String name)
    This function returns the string value corresponding to NAME.  If NAME doesn't exist an error mesage is generated and a null string is returned.

    static function MiniGame::sSet(String name, String value)
    This function modifies value corresponding to NAME as specified by the VALUE string  parameter.  If NAME  doesn't exist the name-vale pair is appended to the end of the memory  buffer.  If there is no more room and error message is generated.

Pumaman

Good stuff Rick, looking good!

Although, I haven't yet managed to win on the slot machine, it must be rigged  >:(

;D

Scorpiorus

Good work! :)

The site is down at the moment but I'm looking forward to finally download and try this out.

RickJ

The gaia-spa website  should be back up tomorrow morning or sooner.  The domain name expired and needed to be renewed.   I am going to post a new version tomorrow so you all may as well wait for that anyway. 

Cheers.

spook1

I am using a slightly modified SlotMachine script.
Lately I cannot save anymore, because of an error:

Type Mismatch, cannot convert const string to string.

The code is:

   function GameQuit() {
//
// This function is called to terminate the game.
//-------------------------------------------------------------------
   // Mini Game quit Script
   MiniGame.iSet("Credits",GmCredits);         // so save data and ...
   MiniGame.iSet("HighScore",GmHighScore);   // so save data and ...
   MiniGame.Return(false);                     // return to main game 
}


GmCredit is defined as integer.

I cannot understand why this used to work fine before, and now this error.

Any suggestions?

spook1

#5
I am sorry to tell that I cannot access the links to download the scripts.

I did so before but lately errors come up in the MiniGame.ISet function (see my previous post).

Martijn

Pumaman

I don't believe that the files have been updated yet to work with AGS 2.71, which will probably be the cause of your problem.

spook1

Thanks. Is there a way I can update them myself?
If not, who could help me? Is Rick the great genious behind this?

Martijn

Scorpiorus

Generally the problem you're having is that now AGS won't let you try to change old-style strings if they are constants.

In order to fix the error, you have to go to the place where the MiniGame.iSet() function is declared and defined and change its first parameter type from string to const string.

There other functions may have the same problem.

Just don't change all of the functions, only those causing the error.

But yeah, ultimately it would be cool to update the script to support new-style strings.

spook1

would that be in some dll or so?
(sorry, I start to know the ags scripting language reasonably well, but I still am not an expert on programming :-()

Scorpiorus

It's a script module, just open the module manager (Main menu -> Script -> Module manager...).

Then choose the MiniGame module in the list.

Click Edit header, then find and change the function declaration there, as I described above (look into struct MiniGame for import function iSet(...)).

The exit&save and click Edit script, then find and change the function definition there too (look for something like MiniGame::iSet).

RickJ

Hi Martijn,

Hehe, it's funny you ask about this as I am currently working on this.  If you can wait a few days I should have the entire demo up to AGS V2.72 compatibility.   

Also for general info the main game and all mini games must re-import the updated minigame module. 

CJ, to update a module it's currently necessary to remove the module and then import it again.  Would it be possible to just display a warning dialog and give the user the choice of ovewrwriting or aborting?

spook1

Thanks for helping.
The errors are solved. Something seems to have gone wrong in the main code though. My version does not reach the GameState = START anymore.

Can I download the original code somewhere? The link mentioned above:
http://www.gaia-spa.com/project/demo/Archive/DQSLOT-S000-001.zip
is dead.

regards,
Martijn

RickJ

Links to the latest version of Demo Quest are maintained in the "Games in Produiction" forum in this thread:

http://www.adventuregamestudio.co.uk/yabb/index.php?topic=23083.0

Let me know if you have any trouble with them.  Also keep in mind there will soon be another update for V2.72 compatibility.

spook1

Hi Rick,

Nice to work with you again!

I downloaded the beta-versions, but the DQcycle game does not run.
I repaired the const string definitions, but I still cannot run the game.
I do not reahc the gamestate start.

Any suggestions?

Martijn


RickJ

What vesion of AGS are you using?  The links I gave you only work with AGS 2.70.  I will have a new version of everything in a couple of days that works with V2.72.

Pumaman

Quote from: RickJ on Thu 02/02/2006 02:41:32
CJ, to update a module it's currently necessary to remove the module and then import it again. Would it be possible to just display a warning dialog and give the user the choice of ovewrwriting or aborting?

Interesting point, that's something that could perhaps be improved.

spook1

Quote from: RickJ on Thu 02/02/2006 14:19:39
What vesion of AGS are you using?Ã,  The links I gave you only work with AGS 2.70.Ã,  I will have a new version of everything in a couple of days that works with V2.72.

Rick, thanks for answering. I use AGS 2.71.
The reason i am eager to adjust this version is that I already impoterd many sprites, changed obejct and character interactions, and created  many new functions to work in the script.
I would love to make this "newly adjusted old version" work with AGS27.1.

If it is not possible, I'll have to look into the new code and redo the work,

martijn

spook1

Is it due to the restartgame(); problem, mentioned by largopredator elsewhere in this forum?

RickJ

#19
I'll publish a new version of the module this weekend, probably Sunday.  It don't think it uses any of 2.72 features and so should be compatible with 2.71. 

Edit:
Thanks  monkey for the correction.

SMF spam blocked by CleanTalk