Navigating through and understanding the AGS Source: Part 1: Limits

Started by Joseph DiPerla, Wed 18/07/2012 21:14:55

Previous topic - Next topic

Joseph DiPerla

As I have mentioned before, I am a newbie when it comes to C++ and the AGS Source code. However, after studying the source for weeks now, I am starting to understand how things work a little better, particularly with the refactored code. So I figured I would start these little threads to see if I am getting this right. Perhaps you can help me out with this. I figured that for today I would start by trying to lower or raise the AGS limits in both the engine and the editor. This, in my opinion, seems fairly simple enough to do. However, I am also certain that somewhere in the C++ and C# code, more needs to be done. Please, note, I do not need these limits raised personally (Not yet anyway), its merely a way to get a handle on the code. So lets begin.

Lets say I wanted to raise the following levels:
Max Rooms from 300 to 800
Max inventory items from 300 to 1000
Max Hotspots from 50 to 100
Max number of background animations from 5 to 30

Lets stick with those for now.

For rooms, Hotspots and bscene, this is what we change in common_defines.h in the engine itself:

Code: AGS
 #ifndef __AC_DEFINES_H
#define __AC_DEFINES_H

#define EXIT_NORMAL 91
#define EXIT_CRASH  92

#define ROOM_FILE_VERSION 29
/* room file versions history
8:  final v1.14 release
9:  intermediate v2 alpha releases
10:  v2 alpha-7 release
11:  final v2.00 release
12:  v2.08, to add colour depth byte
13:  v2.14, add walkarea light levels
14:  v2.4, fixed so it saves walkable area 15
15:  v2.41, supports NewInteraction
16:  v2.5
17:  v2.5 - just version change to force room re-compile for new charctr struct
18:  v2.51 - vector scaling
19:  v2.53 - interaction variables
20:  v2.55 - shared palette backgrounds
21:  v2.55 - regions
22:  v2.61 - encrypt room messages
23:  v2.62 - object flags
24:  v2.7  - hotspot script names
25:  v2.72 - game id embedded
26:  v3.0 - new interaction format, and no script source
27:  v3.0 - store Y of bottom of object, not top
28:  v3.0.3 - remove hotspot name length limit
29:  v3.0.3 - high-res coords for object x/y, edges and hotspot walk-to point
*/
#define MAX_INIT_SPR  40
#define MAX_OBJ       16  // max walk-behinds
#define NUM_MISC      20
#define MAXMESS       100
#define NUMOTCON      7                 // number of conditions before standing on
#define NUM_CONDIT    (120 + NUMOTCON)
#define MAX_HOTSPOTS  100   // v2.62 increased from 20 to 30; v2.8 to 50
#define MAX_REGIONS   16

// careful with this - the shadinginfo[] array needs to be
// MAX_WALK_AREAS + 1 if this gets changed
#define MAX_WALK_AREAS 15

#define MAX_SCRIPT_NAME_LEN 20

//const int MISC_COND = MAX_OBJ * 4 + NUMOTCON + MAX_INIT_SPR * 4;

// NUMCONDIT : whataction[0]:  Char walks off left
//                       [1]:  Char walks off right
//                       [2]:  Char walks off bottom
//                       [3]:  Char walks off top
//			 [4]:  First enters screen
//                       [5]:  Every time enters screen
//                       [6]:  execute every loop
//                [5]...[19]:  Char stands on lookat type
//               [20]...[35]:  Look at type
//               [36]...[49]:  Action on type
//               [50]...[65]:  Use inv on type
//               [66]...[75]:  Look at object
//               [76]...[85]:  Action on object
//               [86]...[95]:  Speak to object
//		[96]...[105]:  Use inv on object
//             [106]...[124]:  Misc conditions 1-20

// game ver     whataction[]=
// v1.00              0  :  Go to screen
//                    1  :  Don't do anything
//                    2  :  Can't walk
//                    3  :  Man dies
//                    4  :  Run animation
//                    5  :  Display message
//                    6  :  Remove an object (set object.on=0)
//		                7  :  Remove object & add Val2 to inventory
//                    8  :  Add Val1 to inventory (Val2=num times)
//                    9  :  Run a script
// v1.00 SR-1        10  :  Run graphical script
// v1.1              11  :  Play sound effect SOUND%d.WAV
// v1.12             12  :  Play FLI/FLC animation FLIC%d.FLC or FLIC%d.FLI
//                   13  :  Turn object on
// v2.00             14  :  Run conversation
#define NUMRESPONSE   14
#define NUMCOMMANDS   15
#define GO_TO_SCREEN  0
#define NO_ACTION     1
#define NO_WALK       2
#define MAN_DIES      3
#define RUN_ANIMATE   4
#define SHOW_MESSAGE  5
#define OBJECT_OFF    6
#define OBJECT_INV    7
#define ADD_INV       8
#define RUNSCRIPT     9
#define GRAPHSCRIPT   10
#define PLAY_SOUND    11
#define PLAY_FLI      12
#define OBJECT_ON     13
#define RUN_DIALOG    14


#define MAX_ROOMS 800

#define MAXANIMS      10
#define MAX_FLAGS     15
#define MAXOBJNAMELEN 30
#define MAX_BSCENE    30   // max number of frames in animating bg scene

#define MAX_SPRITES         30000
#define MAX_CURSOR          20


//#ifdef DJGPP
//#include <unistd.h>
//#endif

#ifdef _MSC_VER
#undef VTA_LEFT
#undef VTA_RIGHT
#endif

#ifdef DJGPP
#define PCKD __attribute__((packed))
#else
#define PCKD
#endif

#ifndef int32
#define int32 long
#endif

#ifdef WINDOWS_VERSION
#define AGS_INLINE inline
#else
// the linux compiler won't allow extern inline
#define AGS_INLINE
#endif

// object flags (currently only a char)
#define OBJF_NOINTERACT        1  // not clickable
#define OBJF_NOWALKBEHINDS     2  // ignore walk-behinds
#define OBJF_HASTINT           4  // the tint_* members are valid
#define OBJF_USEREGIONTINTS    8  // obey region tints/light areas
#define OBJF_USEROOMSCALING 0x10  // obey room scaling areas
#define OBJF_SOLID          0x20  // blocks characters from moving
#define OBJF_DELETED        0x40  // object has been deleted

#endif // __AC_DEFINES_H

For Characterinfo.h, on line 7:
Code: AGS
#define MAX_INV             1001


Now for the editor.  In game.cs line 22:
Code: AGS
public const int MAX_INV_ITEMS = 1000;


In agsdefns.sh on line 8

Code: AGS
#define MAX_INV 1001


In room.cs, starting at line 14:
Code: AGS
 public const int MAX_BACKGROUNDS = 30;
        public const int MAX_OBJECTS = 40;
        public const int MAX_HOTSPOTS = 100;
        public const int MAX_WALKABLE_AREAS = 16;
        public const int MAX_WALK_BEHINDS = 16;
        public const int MAX_REGIONS = 16;


in unloadedroom.cs on line 15:

Code: AGS
public const int NON_STATE_SAVING_INDEX = 800;


Now, I mostly figured the engine iterates through the defined constants to decide what their limits are and the same with the Editor. So, making those modifications, I am "assuming" would be more than enough. Am I correct? If so, I will start a new thread with the next lesson for me. If I am wrong however, please, teach me oh wise ones!
Joseph DiPerla--- http://www.adventurestockpile.com
Play my Star Wars MMORPG: http://sw-bfs.com
See my Fiverr page for translation and other services: https://www.fiverr.com/josephdiperla
Google Plus Adventure Community: https://plus.google.com/communities/116504865864458899575

Joseph DiPerla

One last thing...

In NewRoomDialog.designer.cs on line 123 for the editor, I needed to change this:

Code: AGS
 this.label3.Text = "Room numbers below 800 save the room state; numbers above 800 do not";


So far, my compiled version seems to be working pretty well.
Joseph DiPerla--- http://www.adventurestockpile.com
Play my Star Wars MMORPG: http://sw-bfs.com
See my Fiverr page for translation and other services: https://www.fiverr.com/josephdiperla
Google Plus Adventure Community: https://plus.google.com/communities/116504865864458899575

Crimson Wizard

Eh, well, that's one a simple one. Perhaps too simple. ;)

Yes, AGS Engine hardcodes the limits, and yes it sets them by use of defines. To be honest I did not check that through, I am assuming that you made everything correct (at least not far from correct). Frankly I just do not see much interest in checking how program will behave if we raise those numbers.

You probably may know that in C++ there are two types of built-in arrays: static arrays and dynamic arrays. Static arrays may never change their size, dynamic arrays technically may, but it is not necessarily done in the program.
An example of static array is:
(Engine/ac/game.cpp)
Code: cpp

int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];

Those arrays' sizes are defined at compile time and you may sure they stay the same all the time through a program execution. That's the cause of a limit.

An example of dynamic array is:
(Engine/ac/game.cpp)
Code: cpp

GUIMain*guis;

Well, that declaration is not actually array itself, it is a pointer, as you may be aware of, we just know that it is used as a pointer to array:
(Engine/main/game_file.cpp)
Code: cpp

read_gui(iii,guis,&game, &guis);

(Common/gui/guimain.cpp)
Code: cpp

void read_gui(FILE * iii, GUIMain * guiread, GameSetupStruct * gss, GUIMain** allocate)
{
...
  if (allocate != NULL)
  {
    *allocate = (GUIMain*)malloc(sizeof(GUIMain) * gss->numgui);
    guiread = *allocate;
  }

Well, perhaps not the best example, since there's a lot of extra stuff going on here, but the main point is the "malloc" line, it is basically an old, C-style way to create something in the heap, nowadays done mostly by C++-style "new" operator; that would be almost the same if it was
Code: cpp
*allocate = new GUIMain[gss->numgui];
.
The array created this way is not "final", we may delete it and remake with totally new size and contents.
While it is technically possible, that is never done in AGS engine. But what makes GUI array not limited is the fact that its size is not hard-coded, but read from game data, hence may be theoretically of any number.


I guess it is quite possible to remove the limits we know once and for all by replacing the static arrays with dynamic ones. Interesting thing is that although static arrays's sizes are hard-coded, the number of items is still read from game data. Well, that's obvious - we must know how much actual items are there.
For example, inventory items:
(Common/ac/gamesetupstructbase.cpp, reading number of inventory items)
Code: cpp

numinvitems = getshort(fp);

(Common/ac/gamesetupstruct.cpp, reading inventory items of known count into array of hard-coded size)
Code: cpp

fread(&invinfo[0], sizeof(InventoryItemInfo), numinvitems, f);


The problem is, however, that there is more then one array affected by the size number. For example, for inventory items there are:
in GameSetupStruct:
Code: cpp

InventoryItemInfo invinfo[MAX_INV];
NewInteraction   *intrInv[MAX_INV];
CustomProperties  invProps[MAX_INV];
char              invScriptNames[MAX_INV][MAX_SCRIPT_NAME_LEN];

in CharacterInfo:
Code: cpp

short inv[MAX_INV];


In other words, it is not that it would be hard technically, it will just take some time to rewrite how those arrays are managed (created, deleted, etc).
That would be easier and safer to achieve if we use array classes (which manage underlying dynamic array on its own).

Joseph DiPerla

At this point, I would just up the total number of limits to numbers that are ridiculous, like 10,000 rooms and 100,000 inventory items. Really, no one would use that much in the first place anyway. Atleast that can be a work around for now. I do agree though that using dynamic arrays would be ideal, but since that would take some time, I guess we could do it my way for now :)
Joseph DiPerla--- http://www.adventurestockpile.com
Play my Star Wars MMORPG: http://sw-bfs.com
See my Fiverr page for translation and other services: https://www.fiverr.com/josephdiperla
Google Plus Adventure Community: https://plus.google.com/communities/116504865864458899575

Crimson Wizard

Quote from: Joseph DiPerla on Thu 19/07/2012 00:02:41
At this point, I would just up the total number of limits to numbers that are ridiculous, like 10,000 rooms and 100,000 inventory items. Really, no one would use that much in the first place anyway. Atleast that can be a work around for now. I do agree though that using dynamic arrays would be ideal, but since that would take some time, I guess we could do it my way for now :)
I disagree. There's definitely no point in adjusting those numbers, since no one has ever requested that AFAIK. By doing that we achieve literally nothing.
On other hand since we are planning to implement new class system and gradually rewrite the engine we will do change arrays naturally.
By the way, by increasing the number of state saving rooms we will break backwards compatibility, since the rooms that are non-state-saving in the previously made games will become state-saving and that might break game logic.

Joseph DiPerla

I totally agree about that. However, for the time being, IF someone wanted to change the way the limits work, one would have to change the constants and static arrays in the source. I do think its bad practice and we should eventually change the system.

Joseph DiPerla--- http://www.adventurestockpile.com
Play my Star Wars MMORPG: http://sw-bfs.com
See my Fiverr page for translation and other services: https://www.fiverr.com/josephdiperla
Google Plus Adventure Community: https://plus.google.com/communities/116504865864458899575

Rocco

When you talk about limits then i have to say,
there are two limits that restrain me really hard with my Top-Down Games.

These are the limitations of 15 walkable areas and 15 regions for a single room.
If this could easily be raised, to lets say 50 like the hotspots i would be more as pleased.
I suggested this already years ago.

Joseph DiPerla

The drawable area stuff seems to require some more behind the scenes than just changing the variables. If I just change the variables, it seems something might break. I need to look into that a little more.
Joseph DiPerla--- http://www.adventurestockpile.com
Play my Star Wars MMORPG: http://sw-bfs.com
See my Fiverr page for translation and other services: https://www.fiverr.com/josephdiperla
Google Plus Adventure Community: https://plus.google.com/communities/116504865864458899575

Crimson Wizard

Just a note: no one can tell when the refactored version of code will be put into use. It would probably be better to do this in dev.3.2.2 or JJS's "main" if you expect early update.

Joseph DiPerla

Rocco, working on your limits issue right now. I added a new thread for Interaction modes. Next I am working on Screen Resolutions so that more are offered. However, this is a pain. There is a lot to consider. But thats the next "Navigating and understanding the AGS Source" Thread that I will create. After that, I want to try and figure out how to add Inventory Categories and types and I will compile the sources for you to work with Rocco.
Joseph DiPerla--- http://www.adventurestockpile.com
Play my Star Wars MMORPG: http://sw-bfs.com
See my Fiverr page for translation and other services: https://www.fiverr.com/josephdiperla
Google Plus Adventure Community: https://plus.google.com/communities/116504865864458899575

Rocco

Thx this is really great.  ;-D
But no sweat, take your time.
I'm as good as finished with my actual game, only polishing, so its not an burning issue.
But its a weighty issue for my following project(s).

Shane 'ProgZmax' Stevens

The very real problem to consider with static limits is that storage has to be allocated somewhere in memory at runtime, whereas with dynamic limits it is only used as needed, so the bigger the limits you set, the larger the compiled game is going to be and, eventually, the slower certain tasks are going to become.  Converting all the major entities over to dynamically allocated ones is eventually going to be essential and, while requiring more effort than adjusting some defines, is going to benefit a lot more people in the long run.

Igor Hardy

Quote from: Rocco on Fri 27/07/2012 11:33:53
When you talk about limits then i have to say,
there are two limits that restrain me really hard with my Top-Down Games.

These are the limitations of 15 walkable areas and 15 regions for a single room.
If this could easily be raised, to lets say 50 like the hotspots i would be more as pleased.
I suggested this already years ago.

Perhaps you could do what you need with collisions rather than regions. There's an excellent 'Pixel Perfect Collisions' module by SSH.

Rocco

Well thx, but this is not really an option in my case, the more akward limitation is the walkable area limit.

Quote from: Joseph DiPerla on Fri 27/07/2012 16:30:33
Rocco, working on your limits issue right now. I added a new thread for Interaction modes. Next I am working on Screen Resolutions so that more are offered. However, this is a pain. There is a lot to consider. But thats the next "Navigating and understanding the AGS Source" Thread that I will create. After that, I want to try and figure out how to add Inventory Categories and types and I will compile the sources for you to work with Rocco.
would be great if you are working on that walkable area limitation furthermore....  :)

SMF spam blocked by CleanTalk