AGS 3.4.0
Introductory topic
AGS 3.4.0 is the next version of Adventure Game Studio Editor and Engine, being in development since mid-2014. Currently it is in
relatively stable stage (you actually can make games with it), but its main planned features are not yet fully implemented, therefore we call it Alpha version.
As previously, we try to keep backwards compatibility with previous versions, meaning that:
1. You should be able to load your game project made with previous versions of AGS Editor, and continue to work on it, possibly with only minor changes to script (or enabling compatibility mode in General Settings).
2. You should be able to run your previously compiled game with new 3.4.0 engine without recompilation.
This thread is meant to serve for preliminary acquaintance with the new version.
The notable features of AGS 3.4.0 are:* Custom resolutions. (http://www.adventuregamestudio.co.uk/forums/index.php?topic=52499.msg636518114#msg636518114) Since AGS 3.4.0 you will be able to create games with any size, and run it in any display resolution using existing scaling filters.
* Extended build system with support to build game for multiple platforms. (http://www.adventuregamestudio.co.uk/forums/index.php?topic=52499.msg636518324#msg636518324) AGS 3.4.0 supports extensions that let it compile games for additional platforms (e.g. Linux). They are optional, and require installing extra files to AGS folder, as well as enable them in project's General Settings.
* Custom properties are now mutable. (http://www.adventuregamestudio.co.uk/forums/index.php?topic=52499.msg636518328#msg636518328) Custom properties may now be changed at runtime.
* Extended AGS Script. (http://www.adventuregamestudio.co.uk/forums/index.php?topic=52499.msg636523588#msg636523588) A number of new commands and constructions were added to AGS script language, such as user-defined managed structs, "for" and "switch" blocks. Few existing features were improved, for example now it is possible to define static extender functions and have dynamic arrays in structs.
* Extended Dialog Options customization. (http://www.adventuregamestudio.co.uk/forums/index.php?topic=52499.msg636523561#msg636523561) You may now easily create custom rendered dialog options that animate and react to key presses.
TO BE UPDATED
Custom Resolutions
AGS 3.4.0 lets you choose literally any resolution (width and height) for your game. It does not mean, that every variant will work well, but reasonable ones should.
There is no restriction to width/height relation, you could even make "portrait orientation" games with height greater than width.
An example of 180x400 game made in AGS (mockup scenery used)
[imgzoom]http://i.imgur.com/EWJ91vX.png[/imgzoom]
Inside your project, resolution is now set not with drop-down list, but with a slightly more advanced dialog:
(http://i.imgur.com/bm880R4.png)
Select "Resolution" option and click on "..." button
(http://i.imgur.com/TZanyej.png)
In the dialog either choose one of the presets, or type in your own width & height, then press OK.
Free display resolutionsTo accompany custom game resolutions, AGS engine is now capable to run the game in any display mode supported by your monitor and graphics card.
The WinSetup utility was modified with different graphics options added. At the moment we are in the process of testing this design out. It may be simplified in future updates to make it easier for players to quickly set it up the way they want.
(http://i.imgur.com/I8TVwGZ.png?1)
Currently you may choose the fullscreen display mode by setting following parameters:
* Mode: this determines the screen size your monitor will set to run the game;
* Filter: this determines which algorithm will be used to scale things up or down;
* Scaling (multiplier): this determines how much the game will be scaled up or down inside the screen;
For better results I recommend using following combinations:
1. If you feel selecting exact resolution more suitable for you, then choose wanted Mode and set scaling multiplier to "Max fit". This will scale the game by the maximal integer multiplier that fits in the Mode.
2. If it is rather convenient for you to select scaling (e.g. if you wish exactly x2 or x3 size of the game), then choose wanted scaling multiplier, and set "Bind to game scaling (force desktop ratio)" in the Modes list.
"Force desktop ratio" means that the game will refer to your desktop aspect ratio to choose gfx mode that looks better on your monitor.
With some of the display modes your game won't take all screen space if scaled up by an integer multiplier. In such case the black borders will appear around the game to fill the extra space. If you do not like to have these borders, you may enable couple of extra options on "Advanced" panel:
(http://i.imgur.com/97jVtb7.png?1)
* "Stretch to fit screen" will make your game stretch more until it fills whole screen.
* "Keep aspect ratio" will ensure that the game's aspect ratio will be kept when stretching, so that image will still look proportional.
Latter option may cause some of the black borders to remain.
On thing to remember when using "stretch" options: the random stretching of the low-resolution game may cause image look slightly skewed (the higher resolution is, the less noticeable this effect will be).
On letterboxed gamesAGS supports making your game letterboxed (I call it "letterbox-by-design"), implicitly increasing its height and automatically adding black borders to all rooms
at runtime. This mode is enabled by setting "(Setup) -> Enable letterbox mode" property in General Settings.
This property had always had a limited functionality and worked only for 320x200 and 640x400 games. It was kind of a hack really, therefore we do not extend it on custom resolutions. Currently it remains only for backwards compatibility (and still works only with two mentioned resolutions).
In the future we may add better support for automatic letterboxing (if there would be demand for it); for now I recommend making explicit black borders in your game rooms and modify GUI appropriately instead, if you wish to achieve this effect for other resolutions.
On High and Low resolution typeIn older versions of AGS there was a distinction between "High" and "Low" resolutions. 320x200 and 320x240 were considered "low", 640x400, 640x480, 800x600 and 1024x768 were considered "high" ones.
Such project items as Sprites, Fonts and Room backgrounds depended on this algorithm to know whether they should be automatically scaled.
For example, Sprites have "Resolution" property which let you set whether they were created for "High" or "Low" one. If the Sprite's resolution type does not match the game's, the sprite will be scaled up or down, respectively.
Similarly, there is a global "Text output -> Fonts designed for 640x480" setting in game project. Normally, for the High-res game the fonts will be scaled up. Enabling this option will stop them being stretched.
To support backwards-compatibility, following rule is applied for aforementioned properties:
* The resolution which has width equal or lower than 320
AND height equal or lower than 240 is considered to be "low-res".
* The resolution which has any of the sides higher; e.g width higher than 320
OR height higher than 240 - is considered to be "hi-res".
As you may realize, this distinction is pretty forced. I should underline, that it exists only to support old behavior for standard resolutions. It is possible that in future we will change how these properties work in the new game projects.
TO BE CONTINUED...
Building for multiple platforms
AGS 3.4.0 can build your game for more than one operating system. This is configured with this new option in General Settings (Compiler -> Build target platforms):
(http://i.imgur.com/PlbtQ3u.png)
Select the systems you like to build the game for by pressing on "drop down" button to the right and checking or unchecking items in the list:
(http://i.imgur.com/N4UaLWg.png)
The contents of this list depends on what Editor components are installed. Editor has Windows build component by default, but you may also install Linux component to create Linux versions of your game.
"DataFile" option cannot be unchecked. It builds main game data file, which is used when making game for other systems.
Other available build targets may be selected in any combination.
This selection only affects doing full build (Build -> Build EXE, or Build -> Rebuild all files). When running in test mode only Windows version is built always.
The "Compiled" folder has changed. Now each build target has its own subdirectory: "Windows", "Linux", etc. To distribute your game - pack/copy contents of corresponding folder(s).
Example of "Compiled" folder contents:
(http://i.imgur.com/6xiZiFL.png)
Example of "Compiled/Windows" folder contents
(http://i.imgur.com/k4PR34S.png)
Any extra files put into Compiled folder will be copied to every build subfolder, when the game is compiled. This way you may distribute any additional files your game needs.
You may also use the contents of "Compiled" folder itself (without subdirectories) to distribute game data files alone, without game executable. Then you may use stand-alone AGS engine on any system to run this game data.
This is important to mention, that when you run setup program from the Editor (Build -> Run game setup...), you will be modifying Windows config only. Same config will be used when you are test-running your game from the Editor (Build -> Run).
For other build configs (Linux, etc) you will have to modify acsetup.cfg file yourself right now (use plain text editor for that).
NOTE: for now you may still use old building system if you go to Editor's Preferences (File -> Preferences) and check "Use legacy compiler" option. As soon as we make sure that the new building system works well, this check will be removed forever though.
TO BE CONTINUED...
Dynamic Custom Properties
Custom properties were convenient way to bind additional descriptions to game objects, such as Characters, Room Objects and Inventory Items, which may then be used in script. Unfortunately, they had two limitations: there could only be 30 of them for the whole project, and they could not be changed at runtime.
AGS 3.4.0 removes both of these restrictions. This lets you to extend named object types with any number of extra attributes.
Changed system limits
Item | Old limit | New limit |
Custom properties | 30 | unlimited |
Custom property name lengths | 20 | unlimited |
Custom property value lengths (for strings) | 500 | unlimited |
You may now change property value at runtime from script. The related script functions are added to all classes that already supported GetProperty() and GetTextProperty(), that is -
Room,
InventoryItem,
Hotspot,
RoomObject and
Character:
/// Sets an integer (or boolean) custom property.
static bool SetProperty(const string property, int value);
/// Sets a text custom property.
static bool SetTextProperty(const string property, const string value);
They return 'true' if the property value was changed, or 'false' if such property does not exist, or property type is incorrect (like setting text value for integer property).
The values set this way will be kept for the duration of the game, unless modified again.
Also,
Room,
Hotspot and
RoomObject property values
are reset to defaults when ResetRoom() is called.
TO BE CONTINUED...
Improved Custom Dialog Options rendering
Custom Dialog Options rendering system was extended to give game developers more freedom in setting it up. In the original variant the callback behavior was strictly tied to mouse movement and clicks. It now supports wider range of callbacks, covering all kinds of events that may occur when Dialog Option GUI is on screen.
To achieve this following changes were implemented.
1. Support for two more callbacks added:
// runs each tick when dialog options are on screen
void dialog_options_repexec(DialogOptionsRenderingInfo *info);
// runs when user pressed a key while dialog options are on screen
void dialog_options_key_press(DialogOptionsRenderingInfo *info, eKeyCode key);
The "dialog_options_get_active" callback is now NOT called, at all. It is supported only for backwards compatibility when running old games.
You will need to slightly change the logic of your script. In most cases it will be enough to simply rename "dialog_options_get_active" to "dialog_options_repexec".
2. Custom dialog options are no longer have automated reaction on mouse click and movement. Instead, following functions are added to the DialogOptionsRenderingInfo struct:
bool RunActiveOption(); // runs the active dialog option (defined with ActiveOptionID property)
void Update(); // forces dialog options to redraw itself ("dialog_options_render" callback will be called)
The "dialog_options_mouse_click" is now called always, even if user clicks on the option. In the most common case you should just call RunActiveOption() from there.
In short, scripting custom dialog options requires a bit more work on gamedev part, but is much more versatile now.
To prove this point, two basic examples follow.
Example A. Classic mouse controlsSpoiler
int dlg_opt_color = 14;
int dlg_opt_acolor = 13;
int dlg_opt_ncolor = 4;
function dialog_options_get_dimensions(DialogOptionsRenderingInfo *info)
{
// Create a 200x200 dialog options area at (50,100)
info.X = 50;
info.Y = 100;
info.Width = 200;
info.Height = 200;
}
function dialog_options_render(DialogOptionsRenderingInfo *info)
{
info.Surface.Clear(dlg_opt_color);
int i = 1, ypos = 0;
// Render all the options that are enabled
while (i <= info.DialogToRender.OptionCount)
{
if (info.DialogToRender.GetOptionState(i) == eOptionOn)
{
if (info.ActiveOptionID == i)
info.Surface.DrawingColor = dlg_opt_acolor;
else
info.Surface.DrawingColor = dlg_opt_ncolor;
info.Surface.DrawStringWrapped(5, ypos, info.Width - 10,
eFontFont0, eAlignLeft, info.DialogToRender.GetOptionText(i));
ypos += GetTextHeight(info.DialogToRender.GetOptionText(i), eFontFont0, info.Width - 10);
}
i++;
}
}
function dialog_options_repexec(DialogOptionsRenderingInfo *info)
{
info.ActiveOptionID = 0;
if (mouse.y < info.Y || mouse.y >= info.Y + info.Height ||
mouse.x < info.X || mouse.x >= info.X + info.Width)
{
return; // return if the mouse is outside UI bounds
}
int i = 1, ypos = 0;
// Find the option that corresponds to where the player clicked
while (i <= info.DialogToRender.OptionCount)
{
if (info.DialogToRender.GetOptionState(i) == eOptionOn)
{
ypos += GetTextHeight(info.DialogToRender.GetOptionText(i), eFontFont0, info.Width - 10);
if ((mouse.y - info.Y) < ypos)
{
info.ActiveOptionID = i;
return;
}
}
i++;
}
}
function dialog_options_mouse_click(DialogOptionsRenderingInfo *info, MouseButton button)
{
if (info.ActiveOptionID > 0)
info.RunActiveOption();
}
Example B. Keyboard controlsSpoiler
int dlg_opt_color = 14;
int dlg_opt_acolor = 13;
int dlg_opt_ncolor = 4;
function dialog_options_get_dimensions(DialogOptionsRenderingInfo *info)
{
// Create a 200x200 dialog options area at (50,100)
info.X = 50;
info.Y = 100;
info.Width = 200;
info.Height = 200;
info.ActiveOptionID = 1; // set to first option
}
function dialog_options_render(DialogOptionsRenderingInfo *info)
{
info.Surface.Clear(dlg_opt_color);
int i = 1, ypos = 0;
// Render all the options that are enabled
while (i <= info.DialogToRender.OptionCount)
{
if (info.DialogToRender.GetOptionState(i) == eOptionOn)
{
if (info.ActiveOptionID == i)
info.Surface.DrawingColor = dlg_opt_acolor;
else
info.Surface.DrawingColor = dlg_opt_ncolor;
info.Surface.DrawStringWrapped(5, ypos, info.Width - 10,
eFontFont0, eAlignLeft, info.DialogToRender.GetOptionText(i));
ypos += GetTextHeight(info.DialogToRender.GetOptionText(i), eFontFont0, info.Width - 10);
}
i++;
}
}
function dialog_options_key_press(DialogOptionsRenderingInfo *info, eKeyCode keycode)
{
if (keycode == eKeyUpArrow && info.ActiveOptionID > 1)
info.ActiveOptionID = info.ActiveOptionID - 1;
if (keycode == eKeyDownArrow && info.ActiveOptionID < info.DialogToRender.OptionCount)
info.ActiveOptionID = info.ActiveOptionID + 1;
if (keycode == eKeyReturn || keycode == eKeySpace)
info.RunActiveOption();
}
TO BE CONTINUED...
Scripting improvements
In version 3.4.0 AGS scripting language is enriched with new commands and constructs.
Do...While loopsIn addition to the previously existing "while" loop, the "do...while" loop construct is now supported. Unlike "while", "do...while" runs the first iteration *before* evaluating the condition.
Example:
Spoiler
x = 1;
do
{
x++;
Display("%d", x);
} while(x < 1);
The loop above will run once.
For loopIn addition to a "while" loop, you can now use a "for" loop. The syntax is:
for([initialisation];[condition];[increment])
[single statement to repeat];
for([initialisation];[condition];[increment])
{
[loop body]
}
Examples:
Spoiler
for(player.x = 0; player.x < 100; player.x++)
Wait(1);
for(int i = 10; i > 0; i--)
{
Display("i = %d", i);
}
Break and continue statementsYou can now break out of loops (any kind of loops) using the "break" statement.
Example:
Spoiler
Code: Adventure Game Studio
i = length - 1;
while(i >= 0)
{
if(page[i] == target)
break;
i--;
}
This will halt the loop when a match is found or leave i as -1 if there was no match.
You can also skip the rest of loop and go straight to the next iteration using the "continue" statement.
Example:
Spoiler
for(x = 0; x < 100; x++)
{
if(x % 2 == 0)
continue;
Display("%d", x);
}
This will display only odd numbers between 0 and 100.
Switch statementAdded support for "switch" statement, which may serve as a replacement for multiple "if/else if" coming in sequence.
Strings and other variable types are allowed to be checked in switch condition. Standard case statement features like fallthrough and break are also supported.
Example:
Spoiler
String x = "a";
switch(x)
{
case "a":
Display("X is A");
case "b": // fall-through
Display("X is A or B");
break; // break here
case "c":
Display("X is C");
case "d": // fall-through
default: // fall-through
Display("X is C, D, or something except A and B");
}
Dynamic Arrays in StructsDynamic arrays are now permitted inside structs.
Example:
Spoiler
struct DieRoll
{
int BaseModifier;
int DieCount;
int Dice[ ];
import function GetTotalValueOfRoll();
};
function PrepareDice()
{
DieRoll a;
a.DieCount = 3;
a.Dice = new int[a.DieCount];
a.Dice[0] = 6; // d6
a.Dice[1] = 6; // d6
a.Dice[2] = 8; // d8
...
}
And the dynamic array "Dice" can be initialised and used like any other dynamic array.
Dynamic arrays returned by the Struct member function.
It is now possible to define a member function of a struct, that returns dynamic array.
Example:
Spoiler
struct MyClass
{
int Max;
int Arr[];
import void InitArray(int max);
import int[] GetArray();
import int GetArrayLength();
};
void MyClass::InitArray(int max)
{
this.Max = max;
this.Arr = new int[this.Max];
int i;
for (i = 0; i < this.Max; i++)
this.Arr[i] = i;
}
int[] MyClass::GetArray()
{
return this.Arr;
}
int MyClass::GetArrayLength()
{
return this.Max;
}
function game_start()
{
MyClass my_obj;
my_obj.InitArray(5);
int get_arr[] = my_obj.GetArray();
int i;
for (i = 0; i < my_obj.GetArrayLength(); i++)
Display("#%i = %i", i, get_arr[i]);
}
Static extender functionsIt is now possible to create static extender functions, that is extender function that do not require an actual object.
The syntax is:
[return_type] function_name(static [Struct_Name], [parameters]);
Example:
Spoiler
int AbsInt(static Maths, int value)
{
if (value < 0)
value = 0 - value;
return value;
}
This works in the same way as the normal extender method syntax (e.g. this Character *) but for static methods. The above code will define a new method in the static Maths called AbsInt. You can then import it in a header:
import int AbsInt(static Maths, int value);
And then use it elsewhere in your code just like it were a built-in Maths function:
int x = Maths.AbsInt(-3);
More assignment operatorsThe following C-style assignment operators are now supported in addition to += and -=:
*= // Multiply by and assign
/= // Divide by and assign
&= // Bitwise AND and assign
|= // Bitwise OR and assign
^= // Bitwise XOR and assign
<<= // Bitshift left and assign
>>= // Bitshift right and assign
Improvements to macros#define command can now refer to other #define'd constants. Like in VC++, #define symbol expansion only needs to make sense at the time of reference. Also like VC++, the order of previously defined constants isn't important.
Example:
Spoiler
#define RED GREEN
#define BLUE 456
#define GREEN BLUE
Display("%d", RED); // Prints 456
#undef BLUE
#define BLUE 123
Display("%d", RED); // Prints 123
Important: To prevent circular references, a #define cannot refer to itself or anything previously used to expand the #define symbol.
Code RegionsYou can now define an arbitrary region for code folding in your script. Then the AGS editor will allow you to fold and unfold this region as though it were a code block. Regions are purely cosmetic feature and have no effect on compiled code.
Example:
Spoiler
#region MyRegion
do stuff;
do stuff;
do stuff;
#endregion MyRegion
TO BE CONTINUED...