Script: CR to OC
If you are coming from ClonkRage scripting, you will find some new things in OpenClonk. Not much and not very difficult, but you will have to know them.
This page does not explain every thing in detail, but should give you an overview.
IDs
Every object has an ID. In ClonkRage that were 4-character-strings like "CLNK" or "ROCK". In OpenClonk they can have any length and consist of the typical characters, for example "Clonk", "Rock", "Rule_NoPowerNeed".
That makes code so much more readable!
Proplists
Proplists are what you might know from other languages as a "map". You can assign values to keys.
var my_proplist = { foo = 3, bar = "Hello!" };
Log("%s %d", my_proplist.bar, my_proplist.foo);
// the following line does the same
Log("%s %d", my_proplist["bar"], my_proplist["foo"]);
// create new property!
my_proplist.test = 123;
A lot of things are now proplists, for example the objects:
var clonk = FindObject(Find_ID(Clonk));
// print Name property of the clonk
Log("Hello, I am %s!", clonk.Name);
or effects and the ActMap (see below).
ActMap
The ActMap.txt is completely gone, you can declare actions directly in the script of an object now, like this:
local ActMap=
{
Attach =
{
Prototype = Action,
Name="Attach",
Procedure=DFA_ATTACH,
NextAction="Be",
Length=1,
FacetBase=1,
}
};
This has the positive effect that you can modify actions when the game is running!
DefCore
A lot of entries from the DefCore.txt went into script, too. Here an example from the axe script:
local Collectible = 1;
local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Rebuy = true;
local ChopStrength = 10;
This helps if you need your own entries. Sadly there is no system which DefCore entries have moved to script and which haven't.
You can access those entries like properties in a proplist
var axe = FindObject(Find_ID(Axe));
// can collect the axe?
if (axe.Collectible)
Collect(axe);
nil value
Variables can now have the value "nil" which simply stands for "nothing". An unassigned variable will now have the value "nil" instead of "0" in ClonkRage
-> for function calls
In ClonkRage you could had "object" parameters for most of the functions. You could for example both write:
Collect(my_item, clonk); // or clonk->Collect(my_item, 0);
in OpenClonk a lot of the "object" parameters are gone and you should always use the direct call with the "->"
clonk->Collect(my_item);
// wrong: Collect(my_item, clonk);
zoomed Graphics.png
You can use 2D graphics just like in CR. But you can also defined versions for zoom levels so that the graphics do not look too pixelized at high zoom. You do that by simply putting a dot and the zoom level into the graphics name:
Graphics.4.png Overlay.4.png
3D Models
As you know, OpenClonk now supports 3D models. I won't go over how to create a model here, that would be overkill. I'd much rather just give a short overview:
Model files have to be named "Graphics.mesh", similarly to "Graphics.png". The skeleton-file name for the animation and the material-file name for the texture do not follow such a restriction.
Note that materials have a global namespace, so two different objects with the material "Gold" would not work (Nugget & Idol, for example) - prefix your material names with the object ID (Nugget_Gold and Idol_Gold)!
OpenClonk provides several functions to work with models or "meshes". Good entry points are:
int PlayAnimation(string animation, int slot, array position, array weight, int sibling);
and the properties "MeshTransformation" and "PictureTransformation" that you can use to f.e. rotate models.
Effect variables
Remember "EffectVar" from ClonkRage that you could use to store variables in effects? The effect is now a proplist and that means that you can simply assign properties to it if you need variables:
func FxMyEffectStart(object target, effect fx, bool temp)
{
// invent new property!
fx.max_time = 100;
}
func FxMyEffectTimer(object target, effect fx, int time)
{
if (time > fx.max_time) return -1;
Message("Not over yet!");
}
There are also some standard properties like "Interval" that you can use to change the interval at which Fx*Timer is called (see the reference)
Scenario saving
Remember the hassle with the "saving as scenario" in editor mode? There is no huge Objects.txt anymore, instead, all the objects placed in the editor mode are serialized into a script, Objects.c. Only standard properties are saved and only if they are different from the default. This makes scenarios created in the editor much more maintainable.
Each object can define certain properties which shall be saved on a scenario save, for example some switch will save its target object:
local target;
func SetTarget(object new_target) { target = new_target; return true; }
func SaveScenarioObject(props)
{
if (!inherited(props, ...)) return false;
if (target) props->AddCall("Target", this, "SetTarget", target);
return true;
}
This results in the generation of this script on scenario save in Objects.c:
var door235 = CreateObject(Door,2354,124);
...
var switch001 = CreateObject(Switch,124,354);
switch001->SetTarget(door235);
So, to make a complex object properly savable into a scenario, it needs to define which properties should be saved. See the reference for more information.