1 /* User action execution handler */
2 // Handles actions set in editor e.g. for dialogues, switches, etc.
3 // An object is sometimes needed to show a menu or start a timer, so this definition is created whenever a user action is run
4
5 local Name = "UserAction";
6 local Plane=0;
7
8 /* UserAction definition */
9
10 // Base classes for EditorProps using actions
11 local Evaluator;
12
13 // EditorProps for generic user action callbacks
14 local Prop, PropProgressMode, PropParallel;
15
16 // Base props for action execution conditions
17 local ActionEvaluation ;
18
19 // Proplist containing callback function. Indexed by option names.
20 local EvaluatorCallbacks;
21
22 // Proplist containing option definitions. Indexed by option names.
23 local EvaluatorDefs;
24
25 // Call this definition early (but after EditorBase) to allow EditorProp initialization
26 local DefinitionPriority=99;
27
28 // Localized group names
29 local GroupNames = { Structure="$Structure$", Game="$Game$", Ambience="$Ambience$", Disasters="$Disasters$" };
30
31 // Storage for global user variables
32 static g_UserAction_global_vars;
33
34 // Localized evaluator names
35 local EvaluatorTypeNames = {
36 Action = "$UserAction$",
37 Color = "$UserColor$",
38 Object = "$UserObject$",
39 ObjectList = "$UserObjectList$",
40 Definition = "$UserDefinition$",
41 Player = "$UserPlayer$",
42 PlayerList = "$UserPlayerList$",
43 Boolean = "$UserBoolean$",
44 Integer = "$UserInteger$",
45 String = "$UserString$",
46 Position = "$UserPosition$",
47 Offset = "$UserOffset$"
48 };
49
50 // All evaluator types (unfortunately, EvaluatorReturnTypes->GetProperties() does not work)
51 local EvaluatorTypes = ["Action", "Object", "ObjectList", "Definition", "Player", "PlayerList", "Boolean", "Integer", "Color", "String", "Position", "Offset", "Any"];
52
53 // Evaluator return types
54 local EvaluatorReturnTypes = {
55 Action = C4V_Nil,
56 Object = C4V_C4Object,
57 ObjectList = [C4V_C4Object],
58 Definition = C4V_Def,
59 Player = C4V_Int,
60 PlayerList = [C4V_Int],
61 Boolean = C4V_Bool,
62 Integer = C4V_Int,
63 String = C4V_String,
64 Position = [C4V_Int, 2],
65 Offset = [C4V_Int, 2],
66 Any = C4V_Nil
67 };
68
69 // Array of specialized object evaluators
70 local object_evaluators;
71
Definition(def)72 func Definition(def)
73 {
74 // Typed evaluator base definitions
75 Evaluator = {};
76 Evaluator.Action = { Name="$UserAction$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$None$", Priority=100 } ] };
77 Evaluator.Object = { Name="$UserObject$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$None$", Priority=100 } ] };
78 Evaluator.ObjectList = { Name="$UserObjectList$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$None$", Priority=100 } ] };
79 Evaluator.Definition = { Name="$UserDefinition$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$None$", Priority=100 } ] };
80 Evaluator.Player = { Name="$UserPlayer$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$Noone$", Priority=100 } ] };
81 Evaluator.PlayerList = { Name="$UserPlayerList$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$Noone$", Priority=100 } ] };
82 Evaluator.Boolean = { Name="$UserBoolean$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$None$", Priority=100 } ] };
83 Evaluator.Integer = { Name="$UserInteger$", Type="enum", OptionKey="Function", Sorted=true, Options = [ {Name="0", Priority=100 } ] };
84 Evaluator.Color = { Name="$UserColor$", Type="enum", OptionKey="Function", Sorted=true, Options = [ {Name="$Default$", Priority=100 } ] };
85 Evaluator.String = { Name="$UserString$", Type="enum", OptionKey="Function", Sorted=true, Options = [ {Name="($EmptyString$)", Priority=100 } ] };
86 Evaluator.Position = { Name="$UserPosition$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$Here$", Priority=100 } ] };
87 Evaluator.Offset = { Name="$UserOffset$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$None$", Priority=100 } ] };
88 Evaluator.Any = { Name="$UserAny$", Type="enum", OptionKey="Function", Sorted=true, Options = [ { Name="$None$", Priority=100 } ] };
89 EvaluatorCallbacks = {};
90 EvaluatorDefs = {};
91 // Object constant evaluator may be needed early be evaluators referencing filtered objects
92 AddEvaluator("Object", nil, ["$ConstantObject$", ""], "$ConstantObjectHelp$", "object_constant", [def, def.EvalConstant], { Value=nil }, { Type="object", Name="$Value$" });
93 // Action evaluators
94 AddEvaluator("Action", "$Sequence$", "$Sequence$", "$SequenceHelp$", "sequence", [def, def.EvalAct_Sequence], { Actions=[] }, { Type="proplist", DescendPath="Actions", HideFullName=true, Display="{{Actions}}", EditorProps = {
95 Actions = { Name="$Actions$", Type="array", Elements=Evaluator.Action },
96 } } );
97 AddEvaluator("Action", "$Sequence$", "$Goto$", "$GotoHelp$", "goto", [def, def.EvalAct_Goto], { Index={Function="int_constant", Value=0} }, new Evaluator.Integer { Name="$Index$" }, "Index");
98 AddEvaluator("Action", "$Sequence$", "$StopSequence$", "$StopSequenceHelp$", "stop_sequence", [def, def.EvalAct_StopSequence]);
99 AddEvaluator("Action", "$Sequence$", "$SuspendSequence$", "$SuspendSequenceHelp$", "suspend_sequence", [def, def.EvalAct_SuspendSequence]);
100 AddEvaluator("Action", "$Sequence$", "$Wait$", "$WaitHelp$", "wait", [def, def.EvalAct_Wait], { Time=60 }, { Type="proplist", Display="{{Time}}", EditorProps = {
101 Time = { Name="$Time$", Type="int", Min=1 }
102 } } );
103 AddEvaluator("Action", "$Sequence$", "$WaitForcondition$", "$WaitForConditionHelp$", "wait_condition", [def, def.EvalAct_WaitCondition], { Interval=20 }, { Type="proplist", Display="{{Condition}}", EditorProps = {
104 Interval = { Name="$CheckInterval$", Type="int", Min=1 },
105 Condition = new Evaluator.Boolean { Name="$Condition$", EditorHelp="$WaitConditionHelp$", Priority=60 }
106 } } );
107 AddEvaluator("Action", "$Ambience$", "$Sound$", "$SoundHelp$", "sound", [def, def.EvalAct_Sound], { Pitch={Function="int_constant", Value=0}, Volume={Function="int_constant", Value=100}, TargetPlayers={Function="all_players"} }, { Type="proplist", Display="{{Sound}}", EditorProps = {
108 Sound = { Name="$SoundName$", EditorHelp="$SoundNameHelp$", Type="sound", AllowEditing=true, Priority=100 },
109 Pitch = new Evaluator.Integer { Name="$SoundPitch$", EditorHelp="$SoundPitchHelp$" },
110 Volume = new Evaluator.Integer { Name="$SoundVolume$", EditorHelp="$SoundVolumeHelp$" },
111 Loop = { Name="$SoundLoop$", EditorHelp="$SoundLoopHelp$", Type="enum", Options=[
112 { Name="$SoundLoopNone$" },
113 { Name="$SoundLoopOn$", Value=+1 },
114 { Name="$SoundLoopOff$", Value=-1 }
115 ] },
116 TargetPlayers = new Evaluator.PlayerList { EditorHelp="$SoundTargetPlayersHelp$" },
117 SourceObject = new Evaluator.Object { Name="$SoundSourceObject$", EditorHelp="$SoundSourceObjectHelp$", EmptyName="$Global$" }
118 } } );
119 AddEvaluator("Action", "$Object$", "$CreateObject$", "$CreateObjectHelp$", "create_object", [def, def.EvalAct_CreateObject], { SpeedX={Function="int_constant", Value=0},SpeedY={Function="int_constant", Value=0},SpeedR={Function="int_constant", Value=0},Rotation={Function="int_constant", Value=0} }, { Type="proplist", Display="{{ID}}", EditorProps = {
120 ID = new Evaluator.Definition { EditorHelp="$CreateObjectDefinitionHelp$", Priority=100 },
121 Position = new Evaluator.Position { EditorHelp="$CreateObjectPositionHelp$" },
122 CreateAbove = { Name="$CreateObjectCreationOffset$", EditorHelp="$CreateObjectCreationOffsetHelp$", Type="enum", Options=[
123 { Name="$Center$" },
124 { Name="$Bottom$", Value=true }
125 ]},
126 Owner = new Evaluator.Player { Name="$Owner$", EditorHelp="$CreateObjectOwnerHelp$" },
127 Container = new Evaluator.Object { Name="$Container$", EditorHelp="$CreateObjectContainerHelp$" },
128 SpeedX = new Evaluator.Integer { Name="$SpeedX$", EditorHelp="$CreateObjectSpeedXHelp$" },
129 SpeedY = new Evaluator.Integer { Name="$SpeedY$", EditorHelp="$CreateObjectSpeedYHelp$" },
130 Rotation = new Evaluator.Integer { Name="$Rotation$", EditorHelp="$CreateObjectRotationHelp$" },
131 SpeedR = new Evaluator.Integer { Name="$SpeedR$", EditorHelp="$CreateObjectSpeedRHelp$" }
132 } } );
133 AddEvaluator("Action", "$Object$", "$CastObjects$", "$CastObjectsHelp$", "cast_objects", [def, def.EvalAct_CastObjects], { Amount={Function="int_constant", Value=8},Speed={Function="int_constant", Value=20},AngleDeviation={Function="int_constant", Value=360} }, { Type="proplist", Display="{{Amount}}x{{ID}}", EditorProps = {
134 ID = new Evaluator.Definition { EditorHelp="$CastObjectsDefinitionHelp$", Priority=100 },
135 Position = new Evaluator.Position { EditorHelp="$CastObjectsPositionHelp$" },
136 Amount = new Evaluator.Integer { Name="$Amount$", EditorHelp="$CastObjectsAmountHelp$" },
137 Speed = new Evaluator.Integer { Name="$Speed$", EditorHelp="$CastObjectsSpeedHelp$" },
138 MeanAngle = new Evaluator.Integer { Name="$MeanAngle$", EditorHelp="$CastObjectsMeanAngleHelp$" },
139 AngleDeviation = new Evaluator.Integer { Name="$AngleDeviation$", EditorHelp="$CastObjectsAngleDeviationHelp$" },
140 Owner = new Evaluator.Player { Name="$Owner$", EditorHelp="$CastObjectsOwnerHelp$" }
141 } } );
142 AddEvaluator("Action", "$Object$", "$RemoveObject$", "$RemoveObjectHelp$", "remove_object", [def, def.EvalAct_RemoveObject], { }, { Type="proplist", Display="{{Object}}", EditorProps = {
143 Object = new Evaluator.Object { EditorHelp="$RemoveObjectObject$", Priority=100 },
144 EjectContents = { Name="$EjectContents$", EditorHelp="$EjectContentsHelp$", Type="enum", Options=[
145 { Name="$EjectContentsNo$" },
146 { Name="$EjectContentsYes$", Value=true }
147 ] },
148 } } );
149 AddEvaluator("Action", "$Object$", "$SetPosition$", "$SetPositionHelp$", "set_position", [def, def.EvalAct_SetPosition], { Object={ Function="triggering_clonk" }, Position={ Function="position_constant_rel" } }, { Type="proplist", Display="({{Object}}->{{Position}})", EditorProps = {
150 Object = new Evaluator.Object { Name="$Object$", EditorHelp="$SetPositionObjectHelp$" },
151 Position = new Evaluator.Position { Name="$Position$", EditorHelp="$SetPositionPositionHelp$" }
152 } } );
153 AddEvaluator("Action", "$Object$", "$Fling$", "$FlingHelp$", "fling", [def, def.EvalAct_Fling], { Object={ Function="triggering_clonk" }, SpeedX={ Function="int_constant", Value=0 }, SpeedY={ Function="int_constant", Value=-20 }, AddSpeed={ Function="bool_constant", Value=false } }, { Type="proplist", Display="({{Object}}, {{SpeedX}}, {{SpeedY}})", EditorProps = {
154 Object = new Evaluator.Object { Name="$Object$", EditorHelp="$FlingObjectHelp$" },
155 SpeedX = new Evaluator.Integer { Name="$SpeedX$", EditorHelp="$FlingSpeedXHelp$" },
156 SpeedY = new Evaluator.Integer { Name="$SpeedY$", EditorHelp="$FlingSpeedYHelp$" },
157 AddSpeed = new Evaluator.Boolean { Name="$AddSpeedY$", EditorHelp="$FlingAddSpeedHelp$" },
158 } } );
159 AddEvaluator("Action", "$Object$", "$EnterObject$", "$EnterObjectHelp$", "enter_object", [def, def.EvalAct_EnterObject], { }, { Type="proplist", Display="{{Object}} -> {{Container}}", EditorProps = {
160 Object = new Evaluator.Object { EditorHelp="$EnterObjectObjectHelp$", Priority=90 },
161 Container = new Evaluator.Object { Name="$Container$", EditorHelp="$EnterObjectContainerHelp$", Priority=80 },
162 CollectionCheck = { Name="$CollectionCheck$", EditorHelp="$CollectionCheckHelp$", Type="enum", Options=[
163 { Name="$CollectionCheckIgnore$" },
164 { Name="$CollectionCheckCheck$", Value="check" },
165 { Name="$CollectionCheckExit$", Value="exit" }
166 ] }
167 } } );
168 AddEvaluator("Action", "$Object$", "$ExitObject$", "$ExitObjectHelp$", "exit_object", [def, def.EvalAct_ExitObject], { }, new Evaluator.Object { }, "Object");
169 AddEvaluator("Action", "$Object$", "$SetVisibility$", "$SetVisibilityHelp$", "set_visibility", [def, def.EvalAct_SetVisibility], { Object={ Function="triggering_clonk" }, Visibility=VIS_All }, { Type="proplist", Display="({{Object}}, {{Visibility}})", EditorProps = {
170 Object = new Evaluator.Object { Name="$Object$" },
171 Visibility = { Name="$Visibility$", Type="enum", Options = [{ Name="$Visible$", Value=VIS_All }, { Name="$Invisible$", Value=VIS_None }] }
172 } } );
173 AddEvaluator("Action", "$Object$", "$SetVincibility$", "$SetVincibilityHelp$", "set_vincibility", [def, def.EvalAct_SetVincibility], { Object={ Function="triggering_clonk" }, Vincibility=false }, { Type="proplist", Display="({{Object}}, {{Vincibility}})", EditorProps = {
174 Object = new Evaluator.Object { Name="$Object$" },
175 Vincibility = { Name="$Vincibility$", Type="enum", Options = [{ Name="$Invincible$", Value=false }, { Name="$Vincible$", Value=true }] }
176 } } );
177 AddEvaluator("Action", "Clonk", "$DoEnergy$", "$DoEnergyHelp$", "do_energy", [def, def.EvalAct_ObjectCallInt, Global.DoEnergy], { Object={ Function="triggering_clonk" } }, { Type="proplist", Display="({{Object}}, {{Value}})", EditorProps = {
178 Object = new Evaluator.Object { Name="$Object$", EditorHelp="$DoEnergyObjectHelp$" },
179 Value = new Evaluator.Integer { Name="$ValueChange$", EditorHelp="$DoEnergyValueChangeHelp$" }
180 } } );
181 AddEvaluator("Action", "Clonk", "$SetDirection$", "$SetDirectionHelp$", "set_direction", [def, def.EvalAct_SetDirection], { Object={ Function="triggering_clonk" }, Direction=DIR_Left }, { Type="proplist", Display="({{Object}}, {{Direction}})", EditorProps = {
182 Object = GetObjectEvaluator("IsClonk", "$Clonk$"),
183 Direction = { Name="$Direction$", Type="enum", Options=[{ Name="$Left$", Value=DIR_Left }, { Name="$Right$", Value=DIR_Right }] }
184 } } );
185 AddEvaluator("Action", "Ambience", "$CastParticles$", "$CastParticlesHelp$", "cast_particles", [def, def.EvalAct_CastParticles], {
186 Name="StarFlash",
187 Amount={Function="int_constant", Value=8},
188 Speed={Function="int_constant", Value=20},
189 Lifetime={Function="int_constant", Value=100},
190 Size={Function="int_constant", Value=10},
191 SizeEnd={Function="int_constant", Value=1},
192 Color={Function="color_constant", Value=0xffff},
193 BlitMode=0,
194 Gravity={Function="int_constant", Value=100},
195 FadeOut=true,
196 CollisionFunc="bounce"
197 }, { Type="proplist", Display="{{Amount}}x{{Name}}", EditorProps = {
198 Name = { Name="$ParticleName$", EditorHelp="$ParticleNameHelp$", Type="enum", Priority=50, Sorted=true, Options = [
199 { Name="$Dust$", Value="Dust" },
200 { Name="$Flash$", Value="Flash" },
201 { Name="$Magic$", Value="Magic" },
202 { Name="$Smoke$", Value="Smoke" },
203 { Name="$Sphere$", Value="Sphere" },
204 { Name="$StarFlash$", Value="StarFlash" },
205 { Name="$StarSpark$", Value="StarSpark" },
206 { Name="$Fire$", Value="MagicFire" },
207 { Name="$Ring$", Value="MagicRing" }
208 ] },
209 Position = new Evaluator.Position { EditorHelp="$CastObjectsPositionHelp$" },
210 Amount = new Evaluator.Integer { Name="$Amount$", EditorHelp="$CastParticlesAmountHelp$" },
211 Speed = new Evaluator.Integer { Name="$Speed$", EditorHelp="$CastParticlesSpeedHelp$" },
212 Lifetime = new Evaluator.Integer { Name="$Lifetime$", EditorHelp="$CastParticlesLifetimeHelp$" },
213 Size = new Evaluator.Integer { Name="$Size$", EditorHelp="$CastParticlesSizeHelp$" },
214 SizeEnd = new Evaluator.Integer { Name="$SizeEnd$", EditorHelp="$CastParticlesSizeEndHelp$" },
215 Color = new Evaluator.Color { Name="$Color$", EditorHelp="$CastParticlesColorHelp$" },
216 BlitMode = { Name="$BlitMode$", EditorHelp="$ParticleBlitModeHelp$", Type="enum", Options = [
217 { Name="$Normal$", Value=0 },
218 { Name="$Additive$", Value=GFX_BLIT_Additive },
219 { Name="$Mod2$", Value=GFX_BLIT_Mod2 }
220 ] },
221 Gravity = new Evaluator.Integer { Name="$Gravity$", EditorHelp="$ParticleGravityHelp$" },
222 FadeOut = { Name="$FadeOut$", EditorHelp="$ParticleFadeOutHelp$", Type="bool" },
223 CollisionFunc = { Name="$CollisionFunc$", EditorHelp="$ParticleCollisionFuncHelp$", Type="enum", Options = [
224 { Value="pass", Name="$Pass$" },
225 { Value="stop", Name="$Stop$" },
226 { Value="bounce", Name="$Bounce$" },
227 { Value="die", Name="$Die$" }
228 ] }
229 } } );
230 AddEvaluator("Action", "$Player$", "$DoWealth$", "$DoWealthHelp$", "do_wealth", [def, def.EvalAct_DoWealth], { Player={ Function="triggering_player" }, DoSound={ Function="bool_constant", Value=true } }, { Type="proplist", Display="({{Player}}, {{Change}})", EditorProps = {
231 Player = Evaluator.Player,
232 Change = new Evaluator.Integer { Name="$Change$", EditorHelp="$DoWealthChangeHelp$" },
233 DoSound = new Evaluator.Boolean { Name="$Sound$", EditorHelp="$DoWealthSoundHelp$", Priority=-1 }
234 } } );
235 AddEvaluator("Action", "$Player$", "$PlrKnowledge$", "$PlrKnowledgeHelp$", "plr_knowledge", [def, def.EvalAct_PlrKnowledge], { Players={ Function="triggering_player_list" }, ID={ Function="def_constant" } }, { Type="proplist", Display="({{Players}}, {{ID}})", EditorProps = {
236 Players = Evaluator.PlayerList,
237 ID = Evaluator.Definition
238 } } );
239 AddEvaluator("Action", "$Player$", "$SetPlrView$", "$SetPlrViewHelp$", "plr_view", [def, def.EvalAct_PlrView], { Players={ Function="triggering_player_list" }, Target={ Function="action_object" } }, { Type="proplist", Display="({{Players}}, {{Target}})", EditorProps = {
240 Players = Evaluator.PlayerList,
241 Target = new Evaluator.Object { Name="$Target$", EditorHelp="$PlrViewTargetHelp$" },
242 Immediate = { Name="$ScrollMode$", EditorHelp="$SetPlrViewScrollModeHelp$", Type="enum", Priority=-10, Options = [
243 { Name="$Smooth$" },
244 { Value=true, Name="$Immediate$" }
245 ] }
246 } } );
247 AddEvaluator("Action", "$Script$", "$ConditionalAction$", "$ConditionalActionHelp$", "if", [def, def.EvalAct_If], { }, { Type="proplist", Display="if({{Condition}}) {{TrueEvaluator}} else {{FalseEvaluator}}", EditorProps = {
248 Condition = new Evaluator.Boolean { Name="$Condition$", EditorHelp="$IfConditionHelp$", Priority=60 },
249 TrueEvaluator = new Evaluator.Action { Name="$TrueEvaluator$", EditorHelp="$TrueEvaluatorHelp$", Priority=50 },
250 FalseEvaluator = new Evaluator.Action { Name="$FalseEvaluator$", EditorHelp="$FalseEvaluatorHelp$", Priority=30 }
251 } } );
252 AddEvaluator("Action", "$Script$", "$SetVariable$", "$SetVariableHelp$", "set_variable", [def, def.EvalAct_SetVariable], { VariableName={ Function="string_constant", Value="" } }, { Type="proplist", Display="{{Context}}::{{VariableName}}={{Value}}", EditorProps = {
253 Context = new Evaluator.Object { Name="$Context$", EditorHelp="$VariableContextHelp$", EmptyName="$Global$" },
254 VariableName = new Evaluator.String { Name="$VariableName$", EditorHelp="$VariableNameHelp$" },
255 Value = new Evaluator.Any { Name="$Value$", EditorHelp="$SetVariableValueHelp$" }
256 } } );
257 AddEvaluator("Action", "$Script$", "$ForInteger$", "$ForIntegerHelp$", "for_int", [def, def.EvalAct_For, def.EvalAct_For_IntRange], { Start={ Function="int_constant", Value=1}, End={ Function="int_constant", Value=10}, Step={ Function="int_constant", Value=1} }, { Type="proplist", HideFullName=true, Display="for({{Start}}:{{Step}}:{{End}}) {{Action}}", EditorProps = {
258 Action = new Evaluator.Action { Name="$UserAction$", EditorHelp="$ForActionHelp$", Priority=100 },
259 Start = new Evaluator.Integer { Name="$Start$", EditorHelp="$ForStartHelp$", Priority=90 },
260 End = new Evaluator.Integer { Name="$End$", EditorHelp="$ForEndHelp$", Priority=80 },
261 Step = new Evaluator.Integer { Name="$Step$", EditorHelp="$ForStepHelp$", Priority=70 }
262 } } );
263 AddEvaluator("Action", "$Script$", "$ForPlayer$", "$ForPlayerHelp$", "for_player", [def, def.EvalAct_For, def.EvalAct_For_PlayerList], { Players={ Function="all_players" } }, { Type="proplist", HideFullName=true, Display="for({{Players}}) {{Action}}", EditorProps = {
264 Action = new Evaluator.Action { Name="$UserAction$", EditorHelp="$ForActionHelp$", Priority=100 },
265 Players = new Evaluator.PlayerList { EditorHelp="$ForPlayersHelp$" }
266 } } );
267 AddEvaluator("Action", "$Script$", "$ForObject$", "$ForObjectHelp$", "for_object", [def, def.EvalAct_For, def.EvalAct_For_ObjectList], { }, { Type="proplist", HideFullName=true, Display="for({{Objects}}) {{Action}}", EditorProps = {
268 Action = new Evaluator.Action { Name="$UserAction$", EditorHelp="$ForActionHelp$", Priority=100 },
269 Objects = new Evaluator.ObjectList { EditorHelp="$ForObjectsHelp$" }
270 } } );
271 AddEvaluator("Action", "$Script$", "$Log$", "$LogHelp$", "log", [def, def.EvalAct_Log], { }, new Evaluator.String { Name="$LogMessage$", EditorHelp="$LogMessageHelp$" }, "Message");
272 AddEvaluator("Action", "$Script$", "$Comment$", "$CommentHelp$", "comment", [def, def.EvalAct_Nop], { Comment="" }, { Name="$Comment$", EditorHelp="$CommentHelp$", Type="string" }, "Comment");
273 AddEvaluator("Action", "Game", "$GameOver$", "$GameOverHelp$", "game_over", [def, def.EvalAct_GameOver]);
274 // Object evaluators
275 AddEvaluator("Object", nil, "$ActionObject$", "$ActionObjectHelp$", "action_object", [def, def.EvalContextValue, "action_object"]);
276 AddEvaluator("Object", nil, "$TriggerClonk$", "$TriggerClonkHelp$", "triggering_clonk", [def, def.EvalContextValue, "triggering_clonk"]);
277 AddEvaluator("Object", nil, "$TriggerObject$", "$TriggerObjectHelp$", "triggering_object", [def, def.EvalContextValue, "triggering_object"]);
278 AddEvaluator("Object", nil, "$IteratedObject$", "$IteratedObjectHelp$", "iterated_object", [def, def.EvalContextValue, "for_object"]);
279 AddEvaluator("Object", nil, "$LastCreatedObject$", "$LastCreatedObjectHelp$", "last_created_object", [def, def.EvalContextValue, "last_created_object"]);
280 var find_object_in_area_delegate = { Type="proplist", Display="{{ID}}", EditorProps = {
281 ID = new Evaluator.Definition { Name="$ID$", EditorHelp="$FindObjectsIDHelp$", EmptyName="$Any$", Priority=51 },
282 Area = { Name="$SearchArea$", EditorHelp="$SearchAreaHelp$", Type="enum", OptionKey="Function", Priority=41, Options=[
283 { Name="$SearchAreaWholeMap$", EditorHelp="$SearchAreaWholeMapHelp$" },
284 { Name="$SearchAreaInRect$", EditorHelp="$SearchAreaInRectHelp$", Value={ Function="InRect" }, DefaultValueFunction=def.GetDefaultRect, ValueKey="Area", Delegate={ Type="rect", Name="$Rectangle$", Relative=false, Color=0xffff00 } },
285 { Name="$SearchAreaAtRect$", EditorHelp="$SearchAreaAtRectHelp$", Value={ Function="AtRect" }, DefaultValueFunction=def.GetDefaultRect, ValueKey="Area", Delegate={ Type="rect", Name="$Rectangle$", Relative=false, Color=0xffff80 } },
286 { Name="$SearchAreaCircle$", EditorHelp="$SearchAreaCircleHelp$", Value={ Function="Circle" }, DefaultValueFunction=def.GetDefaultCircle, ValueKey="Area", Delegate={ Type="circle", Name="$Circle$", Relative=false, CanMoveCenter=true, Color=0xff00ff } },
287 { Name="$SearchAreaNearPosition$", EditorHelp="$SearchAreaNearPositionHelp$", Value={ Function="NearPosition", Parameters={Radius=25} }, ValueKey="Parameters", Delegate={ Type="proplist", Display="({{Position}}, {{Radius}})", EditorProps = {
288 Position = new Evaluator.Position { EditorHelp="$SearchAreaNearPositionPositionHelp$"},
289 Radius = { Type="circle", Relative=true, Name="$Radius$", Color=0xff80ff }
290 } } }
291 ] },
292 AllowContained = { Name="$AllowContained$", EditorHelp="$AllowContainedHelp$", Type="bool", Priority=31 }
293 } };
294 var find_object_in_container_delegate = { Type="proplist", Display="{{ID}} in {{Container}}", EditorProps = {
295 ID = new Evaluator.Definition { Name="$ID$", EditorHelp="$FindObjectsIDHelp$", EmptyName="$Any$" },
296 Container = new Evaluator.Object { Name="$Container$", EditorHelp="FindObjectsContainerHelp" }
297 } };
298 AddEvaluator("Object", nil, "$FindObjectInArea$", "$FindObjectInAreaHelp$", "find_object_in_area", [def, def.EvalObjList_FindObjectsInArea, true], {}, find_object_in_area_delegate);
299 AddEvaluator("Object", nil, "$FindObjectInContainer$", "$FindObjectInContainerHelp$", "find_object_in_container", [def, def.EvalObjList_FindObjectInContainer], {}, find_object_in_container_delegate);
300 // Object list evaluators
301 AddEvaluator("ObjectList", nil, "$FindObjectsInArea$", "$FindObjectsInAreaHelp$", "find_objects_in_area", [def, def.EvalObjList_FindObjectsInArea], {}, find_object_in_area_delegate);
302 AddEvaluator("ObjectList", nil, "$FindObjectsInContainer$", "$FindObjectsInContainerHelp$", "find_objects_in_container", [def, def.EvalObjList_FindObjectsInContainer], {}, find_object_in_container_delegate);
303 // Definition evaluators
304 AddEvaluator("Definition", nil, ["$Constant$", ""], "$ConstantHelp$", "def_constant", [def, def.EvalConstant], { Value=nil }, { Type="def", Name="$Value$" });
305 AddEvaluator("Definition", nil, "$TypeOfObject$", "$TypeOfObjectHelp$", "type_of_object", [def, def.EvalObjProp, Global.GetID], { }, new Evaluator.Object { }, "Object");
306 // Player evaluators
307 AddEvaluator("Player", nil, "$TriggeringPlayer$", "$TriggeringPlayerHelp$", "triggering_player", [def, def.EvalContextValue, "triggering_player"]);
308 AddEvaluator("Player", nil, "$OwnerOfObject$", "$OwnerOfObjectHelp$", "owner", [def, def.EvalObjProp, Global.GetOwner], { }, new Evaluator.Object { }, "Object");
309 AddEvaluator("Player", nil, "$ControllerOfObject$", "$ControllerOfObjectHelp$", "owner", [def, def.EvalObjProp, Global.GetController], { }, new Evaluator.Object { }, "Object");
310 AddEvaluator("Player", nil, "$IteratedPlayer$", "$IteratedPlayerHelp$", "iterated_player", [def, def.EvalContextValue, "for_player"]);
311 // Player list evaluators
312 AddEvaluator("PlayerList", nil, "$TriggeringPlayer$", "$TriggeringPlayerHelp$", "triggering_player_list", [def, def.EvalPlrList_Single, def.EvalPlr_Trigger]);
313 AddEvaluator("PlayerList", nil, "$AllPlayers$", "$AllPlayersHelp$", "all_players", [def, def.EvalPlrList_All]);
314 // Boolean (condition) evaluators
315 AddEvaluator("Boolean", nil, ["$Constant$", ""], "$ConstantHelp$", "bool_constant", [def, def.EvalConstant], { Value=true }, { Type="bool", Name="$Value$" });
316 AddEvaluator("Boolean", "$Comparison$", "$CompareInteger$", "$ComparisonHelp$", "compare_int", [def, def.EvalComparison, "Integer"], { }, { Type="proplist", Display="{{LeftOperand}}{{Operator}}{{RightOperand}}", EditorProps = {
317 LeftOperand = new Evaluator.Integer { Name="$LeftOperand$", EditorHelp="$LeftOperandHelp$", Priority=44 },
318 Operator = { Type="enum", Name="$Operator$", EditorHelp="$OperatorHelp$", Priority=43, Options = [
319 { Name="==", EditorHelp="$EqualHelp$" },
320 { Name="!=", EditorHelp="$NotEqualHelp$", Value="ne" },
321 { Name="<", EditorHelp="$LessThanHelp$", Value="lt" },
322 { Name=">", EditorHelp="$GreaterThanHelp$", Value="gt" },
323 { Name="<=", EditorHelp="$LessOrEqualHelp$", Value="le" },
324 { Name=">=", EditorHelp="$GreaterOrEqualHelp$", Value="ge" }
325 ] },
326 RightOperand = new Evaluator.Integer { Name="$RightOperand$", EditorHelp="$RightOperandHelp$", Priority=42 }
327 } } );
328 AddEvaluator("Boolean", "$Comparison$", "$CompareBoolean$", "$ComparisonHelp$", "compare_bool", [def, def.EvalComparison, "Boolean"], { }, { Type="proplist", Display="{{LeftOperand}}{{Operator}}{{RightOperand}}", EditorProps = {
329 LeftOperand = new Evaluator.Object { Name="$LeftOperand$", EditorHelp="$LeftOperandHelp$", Priority=44 },
330 Operator = { Type="enum", Name="$Operator$", EditorHelp="$OperatorHelp$", Priority=43, Options = [
331 { Name="==", EditorHelp="$EqualHelp$" },
332 { Name="!=", EditorHelp="$NotEqualHelp$", Value="ne" },
333 ] },
334 RightOperand = new Evaluator.Object { Name="$RightOperand$", EditorHelp="$RightOperandHelp$", Priority=42 }
335 } } );
336 AddEvaluator("Boolean", "$Comparison$", "$CompareObject$", "$ComparisonHelp$", "compare_object", [def, def.EvalComparison, "Object"], { }, { Type="proplist", Display="{{LeftOperand}}{{Operator}}{{RightOperand}}", EditorProps = {
337 LeftOperand = new Evaluator.Object { Name="$LeftOperand$", EditorHelp="$LeftOperandHelp$", Priority=44 },
338 Operator = { Type="enum", Name="$Operator$", EditorHelp="$OperatorHelp$", Priority=43, Options = [
339 { Name="==", EditorHelp="$EqualHelp$" },
340 { Name="!=", EditorHelp="$NotEqualHelp$", Value="ne" },
341 ] },
342 RightOperand = new Evaluator.Object { Name="$RightOperand$", EditorHelp="$RightOperandHelp$", Priority=42 }
343 } } );
344 AddEvaluator("Boolean", "$Comparison$", "$CompareString$", "$ComparisonHelp$", "compare_string", [def, def.EvalComparison, "String"], { }, { Type="proplist", Display="{{LeftOperand}}{{Operator}}{{RightOperand}}", EditorProps = {
345 LeftOperand = new Evaluator.String { Name="$LeftOperand$", EditorHelp="$LeftOperandHelp$", Priority=44 },
346 Operator = { Type="enum", Name="$Operator$", EditorHelp="$OperatorHelp$", Priority=43, Options = [
347 { Name="==", EditorHelp="$EqualHelp$" },
348 { Name="!=", EditorHelp="$NotEqualHelp$", Value="ne" },
349 ] },
350 RightOperand = new Evaluator.String { Name="$RightOperand$", EditorHelp="$RightOperandHelp$", Priority=42 }
351 } } );
352 AddEvaluator("Boolean", "$Comparison$", "$CompareDefinition$", "$ComparisonHelp$", "compare_definition", [def, def.EvalComparison, "Definition"], { }, { Type="proplist", Display="{{LeftOperand}}{{Operator}}{{RightOperand}}", EditorProps = {
353 LeftOperand = new Evaluator.Definition { Name="$LeftOperand$", EditorHelp="$LeftOperandHelp$", Priority=44 },
354 Operator = { Type="enum", Name="$Operator$", EditorHelp="$OperatorHelp$", Priority=43, Options = [
355 { Name="==", EditorHelp="$EqualHelp$" },
356 { Name="!=", EditorHelp="$NotEqualHelp$", Value="ne" },
357 ] },
358 RightOperand = new Evaluator.Definition { Name="$RightOperand$", EditorHelp="$RightOperandHelp$", Priority=42 }
359 } } );
360 AddEvaluator("Boolean", "$Comparison$", "$ComparePlayer$", "$ComparisonHelp$", "compare_player", [def, def.EvalComparison, "Player"], { }, { Type="proplist", Display="{{LeftOperand}}{{Operator}}{{RightOperand}}", EditorProps = {
361 LeftOperand = new Evaluator.Player { Name="$LeftOperand$", EditorHelp="$LeftOperandHelp$", Priority=44 },
362 Operator = { Type="enum", Name="$Operator$", EditorHelp="$OperatorHelp$", Priority=43, Options = [
363 { Name="==", EditorHelp="$EqualHelp$" },
364 { Name="!=", EditorHelp="$NotEqualHelp$", Value="ne" },
365 ] },
366 RightOperand = new Evaluator.Player { Name="$RightOperand$", EditorHelp="$RightOperandHelp$", Priority=42 }
367 } } );
368 AddEvaluator("Boolean", "$Logic$", "$Not$", "$NotHelp$", "not", [def, def.EvalBool_Not], { }, new Evaluator.Boolean { }, "Operand");
369 AddEvaluator("Boolean", "$Logic$", "$And$", "$AndHelp$", "and", [def, def.EvalBool_And], { Operands=[] }, { Type="proplist", DescendPath="Operands", Display="{{Operands}}", EditorProps = {
370 Operands = { Name="$Operands$", Type="array", Elements=Evaluator.Boolean }
371 } } );
372 AddEvaluator("Boolean", "$Logic$", "$Or$", "$OrHelp$", "or", [def, def.EvalBool_Or], { Operands=[] }, { Type="proplist", DescendPath="Operands", Display="{{Operands}}", EditorProps = {
373 Operands = { Name="$Operands$", Type="array", Elements=Evaluator.Boolean }
374 } } );
375 AddEvaluator("Boolean", nil, "$ObjectExists$", "$ObjectExistsHelp$", "object_exists", [def, def.EvalBool_ObjectExists], { }, new Evaluator.Object { }, "Object");
376 AddEvaluator("Boolean", nil, "$ObjectAlive$", "$ObjectAliveHelp$", "object_alive", [def, def.EvalBool_ObjectAlive], { }, new Evaluator.Object { }, "Object");
377 // Integer evaluators
378 AddEvaluator("Integer", nil, ["$Constant$", ""], "$ConstantHelp$", "int_constant", [def, def.EvalConstant], { Value=0 }, { Type="int", Name="$Value$" });
379 var arithmetic_delegate = { Type="proplist", HideFullName=true, EditorProps = {
380 LeftOperand = new Evaluator.Integer { Name="$LeftOperand$", EditorHelp="$LeftArithmeticOperandHelp$", Priority=44 },
381 RightOperand = new Evaluator.Integer { Name="$RightOperand$", EditorHelp="$RightArithmeticOperandHelp$", Priority=42 }
382 } };
383 AddEvaluator("Integer", "$Arithmetic$", "$Sum$ (+)", "$SumHelp$", "add", [def, def.EvalInt_Add], { }, new arithmetic_delegate { Display="{{LeftOperand}}+{{RightOperand}}" });
384 AddEvaluator("Integer", "$Arithmetic$", "$Sub$ (-)", "$SumHelp$", "subtract", [def, def.EvalInt_Sub], { }, new arithmetic_delegate { Display="{{LeftOperand}}-{{RightOperand}}" });
385 AddEvaluator("Integer", "$Arithmetic$", "$Mul$ (*)", "$MulHelp$", "multiply", [def, def.EvalInt_Mul], { }, new arithmetic_delegate { Display="{{LeftOperand}}*{{RightOperand}}" });
386 AddEvaluator("Integer", "$Arithmetic$", "$Div$ (/)", "$DivHelp$", "divide", [def, def.EvalInt_Div], { }, new arithmetic_delegate { Display="{{LeftOperand}}/{{RightOperand}}" });
387 AddEvaluator("Integer", "$Arithmetic$", "$Mod$ (%)", "$ModHelp$", "modulo", [def, def.EvalInt_Mod], { }, new arithmetic_delegate { Display="{{LeftOperand}}%{{RightOperand}}" });
388 AddEvaluator("Integer", nil, "$Random$", "$RandomIntHelp$", "int_random", [def, def.EvalIntRandom], { Min={Function="int_constant", Value=0}, Max={Function="int_constant", Value=99} }, { Type="proplist", HideFullName=true, Display="Rnd({{Min}}..{{Max}})", EditorProps = {
389 Min = new Evaluator.Integer { Name="$Min$", EditorHelp="$RandomMinHelp$", Priority=51 },
390 Max = new Evaluator.Integer { Name="$Max$", EditorHelp="$RandomMaxHelp$", Priority=21 }
391 } } );
392 AddEvaluator("Integer", nil, "$Distance$", "$DistanceHelp$", "distance", [def, def.EvalInt_Distance], { }, { Type="proplist", HideFullName=true, Display="d({{PositionA}}..{{PositionB}})", EditorProps = {
393 PositionA = new Evaluator.Position { Name="$PositionA$", EditorHelp="$PositionAHelp$" },
394 PositionB = new Evaluator.Position { Name="$PositionB$", EditorHelp="$PositionBHelp$" }
395 } } );
396 AddEvaluator("Integer", nil, "$NumberOfObjects$", "$NumberOfObjectsHelp$", "object_count", [def, def.EvalCount, "ObjectList"], { }, new Evaluator.ObjectList { }, "Array");
397 AddEvaluator("Integer", nil, "$NumberOfPlayers$", "$NumberOfPlayersHelp$", "player_count", [def, def.EvalCount, "PlayerList"], { }, new Evaluator.PlayerList { }, "Array");
398 AddEvaluator("Integer", nil, "$PlayerWealth$", "$PlayerWealthHelp$", "player_wealth", [def, def.EvalInt_Wealth], { }, new Evaluator.Player { }, "Player");
399 AddEvaluator("Integer", nil, "$ClonkEnergy$", "$ClonkEnergyHelp$", "clonk_energy", [def, def.EvalObjProp, Global.GetEnergy], { }, GetObjectEvaluator("IsClonk", "$Clonk$"), "Object");
400 AddEvaluator("Integer", nil, "$ObjectMass$", "$ObjectMassHelp$", "object_mass", [def, def.EvalObjProp, Global.GetMass], { }, new Evaluator.Object { }, "Object");
401 AddEvaluator("Integer", nil, "$ObjectSpeed$", "$ObjectSpeedHelp$", "object_speed", [def, def.EvalObjProp, Global.GetSpeed], { }, new Evaluator.Object { }, "Object");
402 AddEvaluator("Integer", nil, "$PositionX$", "$PositionXHelp$", "position_x", [def, def.EvalInt_PosCoord, 0], { }, new Evaluator.Position { }, "Position");
403 AddEvaluator("Integer", nil, "$PositionY$", "$PositionYHelp$", "position_y", [def, def.EvalInt_PosCoord, 1], { }, new Evaluator.Position { }, "Position");
404 AddEvaluator("Integer", nil, "$IteratedInteger$", "$IteratedIntegerHelp$", "iterated_int", [def, def.EvalContextValue, "for_int"]);
405 // String evaluators
406 AddEvaluator("String", nil, ["$Constant$", ""], "$ConstantHelp$", "string_constant", [def, def.EvalStringConstant], { Value="" }, { Type="string", Name="$Value$", Translatable=true });
407 AddEvaluator("String", nil, ["$ValueToString$", ""], "$ValueToStringHelp$", "value_to_string", [def, def.EvalStr_ValueToString], { }, new Evaluator.Any { });
408 AddEvaluator("String", nil, "$Concat$", "$ConcatHelp$", "string_concat", [def, def.EvalStr_Concat], { Substrings=[] }, { Type="proplist", HideFullName=true, DescendPath="Substrings", Display="{{Substrings}}", EditorProps = {
409 Substrings = { Name="$Substrings$", Type="array", Elements=Evaluator.String }
410 } } );
411 // Color evaluators
412 AddEvaluator("Color", nil, ["$Constant$", ""], "$ConstantHelp$", "color_constant", [def, def.EvalConstant], { Value=0xffffff }, { Type="color", Name="$Value$" });
413 AddEvaluator("Color", nil, "$RandomColor$", "$RandomColorHelp$", "random_color", [def, def.EvalClr_Random], { ColorA={ Function="color_constant", Value=0 }, ColorB={ Function="color_constant", Value=0xffffff } }, { Type="proplist", Display="({{ColorA}}..{{ColorB}})", EditorProps = {
414 ColorA = new Evaluator.Color { Name="$ColorA$" },
415 ColorB = new Evaluator.Color { Name="$ColorB$" }
416 } } );
417 AddEvaluator("Color", nil, "$PlayerColor$", "$PlayerColorHelp$", "player_color", [def, def.EvalClr_PlayerColor], { Player={ Function="triggering_player" } }, new Evaluator.Player { }, "Player");
418 AddEvaluator("Color", nil, "$RGB$", "$RGBHelp$", "rgb_color", [def, def.EvalClr_RGB], { R={ Function="int_constant", Value=255 }, G={ Function="int_constant", Value=255 }, B={ Function="int_constant", Value=255 } }, { Type="proplist", Display="({{R}}, {{G}}, {{B}})", EditorProps = {
419 R = new Evaluator.Integer { Name="$Red$", Priority=51 },
420 G = new Evaluator.Integer { Name="$Green$", Priority=41 },
421 B = new Evaluator.Integer { Name="$Blue$", Priority=31 }
422 } } );
423 // Position evaluators
424 AddEvaluator("Position", nil, ["$ConstantPositionAbsolute$", ""], "$ConstantPositionAbsoluteHelp$", "position_constant", [def, def.EvalConstant], def.GetDefaultPosition, { Type="point", Name="$Position$", Relative=false, Color=0xff2000 });
425 AddEvaluator("Position", nil, ["$ConstantPositionRelative$", "+"], "$ConstantPositionRelativeHelp$", "position_constant_rel", [def, def.EvalPositionRelative], { Value=[0,0] }, { Type="point", Name="$Position$", Relative=true, Color=0xff0050 });
426 AddEvaluator("Position", nil, "$Coordinates$", "$CoordinatesHelp$", "position_coordinates", [def, def.EvalCoordinates], def.GetDefaultCoordinates, { Type="proplist", HideFullName=true, Display="({{X}},{{Y}})", EditorProps = {
427 X = new Evaluator.Integer { Name="X", EditorHelp="$PosXHelp$" },
428 Y = new Evaluator.Integer { Name="Y", EditorHelp="$PosYHelp$" }
429 } } );
430 AddEvaluator("Position", nil, "$PositionOffset$", "$PositionOffsetHelp$", "position_offset", [def, def.EvalPositionOffset], { }, { Type="proplist", HideFullName=true, Display="{{Position}}+{{Offset}}", EditorProps = {
431 Position = new Evaluator.Position { EditorHelp="$PositionOffsetPositionHelp$", Priority=51 },
432 Offset = new Evaluator.Offset { EditorHelp="$PositionOffsetOffsetHelp$", Priority=21 }
433 } } );
434 AddEvaluator("Position", nil, "$ObjectPosition$", "$ObjectPositionHelp$", "object_position", [def, def.EvalPositionObject], { Object={Function="triggering_object"} }, new Evaluator.Object { EditorHelp="$ObjectPositionObjectHelp$" }, "Object");
435 AddEvaluator("Position", nil, "$LastUsePosition$", "$LastUsePositionHelp$", "use_position", [def, def.EvalPos_LastUse]);
436 AddEvaluator("Position", "$RandomPosition$", "$RandomRectAbs$", "$RandomRectAbsHelp$", "random_pos_rect_abs", [def, def.EvalPos_RandomRect, false], def.GetDefaultRect, { Type="rect", Name="$Rectangle$", Relative=false, Color=0xffff00 }, "Area");
437 AddEvaluator("Position", "$RandomPosition$", "$RandomRectRel$", "$RandomRectRelHelp$", "random_pos_rect_rel", [def, def.EvalPos_RandomRect, true], { Area=[-30,-30,60,60] }, { Type="rect", Name="$Rectangle$", Relative=true, Color=0x00ffff }, "Area");
438 AddEvaluator("Position", "$RandomPosition$", "$RandomCircleAbs$", "$RandomCircleAbsHelp$", "random_pos_circle_abs", [def, def.EvalPos_RandomCircle, false], def.GetDefaultCircle, { Type="circle", Name="$Circle$", Relative=false, CanMoveCenter=true, Color=0xff00ff }, "Area");
439 AddEvaluator("Position", "$RandomPosition$", "$RandomCircleRel$", "$RandomCircleRelHelp$", "random_pos_circle_rel", [def, def.EvalPos_RandomCircle, true], { Area=[50,0,0] }, { Type="circle", Name="$Circle$", Relative=true, CanMoveCenter=true, Color=0xa000a0 }, "Area");
440 // Offset evaluators
441 AddEvaluator("Offset", nil, ["$ConstantOffsetRelative$", ""], "$ConstantOffsetRelativeHelp$", "offset_constant", [def, def.EvalConstant], { Value=[0,0] }, { Type="point", Name="$Position$", Relative=true, Color=0xff30ff });
442 AddEvaluator("Offset", nil, ["$Coordinates$", ""], "$CoordinatesHelp$", "offset_coordinates", [def, def.EvalCoordinates], { Value={X=0,Y=0} }, { Type="proplist", HideFullName=true, Display="({{X}},{{Y}})", EditorProps = {
443 X = new Evaluator.Integer { Name="X", EditorHelp="$OffXHelp$" },
444 Y = new Evaluator.Integer { Name="Y", EditorHelp="$OffYHelp$" },
445 } } );
446 AddEvaluator("Offset", nil, "$AddOffsets$", "$AddOffsetsHelp$", "add_offsets", [def, def.EvalOffsetAdd], { }, { Type="proplist", HideFullName=true, Display="{{Offset1}}+{{Offset2}}", EditorProps = {
447 Offset1 = new Evaluator.Offset { EditorHelp="$AddOffsetOffsetHelp$" },
448 Offset2 = new Evaluator.Offset { EditorHelp="$AddOffsetOffsetHelp$" }
449 } } );
450 AddEvaluator("Offset", nil, "$DiffPositions$", "$DiffPositionsHelp$", "diff_positions", [def, def.EvalOffsetDiff], { }, { Type="proplist", HideFullName=true, Display="{{PositionB}}-{{PositionA}}", EditorProps = {
451 PositionA = new Evaluator.Position { Name="$PositionA$", EditorHelp="$PositionAHelp$" },
452 PositionB = new Evaluator.Position { Name="$PositionB$", EditorHelp="$PositionBHelp$" }
453 } } );
454 AddEvaluator("Offset", nil, "$RandomOffRectRel$", "$RandomRectRelHelp$", "random_off_rect_rel", [def, def.EvalPos_RandomRect, "rect", false], { Area=[-30,-30,60,60] }, { Type="rect", Name="$Rectangle$", Relative=true, Color=0x00ffff }, "Area");
455 AddEvaluator("Offset", nil, "$RandomOffCircleRel$", "$RandomCircleRelHelp$", "random_off_circle_rel", [def, def.EvalPos_RandomCircle, "circle", false], { Area=[0,0,50] }, { Type="circle", Name="$Circle$", Relative=true, CanMoveCenter=true, Color=0xa000a0 }, "Area");
456 // Script evaluators
457 var variable_delegate = { Type="proplist", HideFullName=true, Display="{{Context}}::{{VariableName}}", EditorProps = {
458 Context = new Evaluator.Object { Name="$Context$", EditorHelp="$VariableContextHelp$", EmptyName="$Global$" },
459 VariableName = new Evaluator.String { Name="$VariableName$", EditorHelp="$VariableNameHelp$", Priority=50 } } };
460 for (var eval_type in EvaluatorTypes)
461 {
462 var data_type = EvaluatorReturnTypes[eval_type];
463 var group = nil;
464 if (eval_type != "Action")
465 {
466 AddEvaluator(eval_type, nil, "$Variable$", "$VariableHelp$", Format("%s_variable", eval_type), [def, def.EvalVariable, data_type], { VariableName={ Function="string_constant", Value="" } }, variable_delegate);
467 AddEvaluator(eval_type, nil, "$ConditionalValue$", "$ConditionalValueHelp$", Format("%s_conditional", eval_type), [def, def.EvalConditionalValue, eval_type], { }, { Type="proplist", HideFullName=true, Display="{{Condition}} ? {{TrueEvaluator}} : {{FalseEvaluator}}", EditorProps = {
468 Condition = new Evaluator.Boolean { Name="$Condition$", EditorHelp="$IfConditionValueHelp$", Priority=60 },
469 TrueEvaluator = new Evaluator[eval_type] { Name="$TrueEvaluatorValue$", EditorHelp="$TrueEvaluatorValueHelp$", Priority=50 },
470 FalseEvaluator = new Evaluator[eval_type] { Name="$FalseEvaluatorValue$", EditorHelp="$FalseEvaluatorValueHelp$", Priority=30 }
471 } } );
472 }
473 else
474 {
475 group = "$Script$";
476 }
477 AddEvaluator(eval_type, group, "$Script$", "$ScriptHelp$", Format("%s_script", eval_type), [def, def.EvalScript, data_type], { }, { Type="proplist", HideFullName=true, Display="{{Context}}::{{Script}}", EditorProps = {
478 Context = new Evaluator.Object { Name="$Context$", EditorHelp="$VariableContextHelp$", EmptyName="$Global$" },
479 Script = new Evaluator.String { Name="$ScriptCommand$", EditorHelp="$ScriptCommandHelp$" } } });
480 }
481 // User action editor props
482 Prop = Evaluator.Action;
483 PropProgressMode = { Name="$UserActionProgressMode$", EditorHelp="$UserActionProgressModeHelp$", Type="enum", Options = [ { Name="$Session$", Value="session" }, { Name="$PerPlayer$", Value="player" }, { Name="$Global$" } ] };
484 PropParallel = { Name="$ParallelAction$", EditorHelp="$ParallelActionHelp$", Type="bool" };
485 return true;
486 }
487
GetObjectEvaluator(filter_def,name,help)488 public func GetObjectEvaluator(filter_def, name, help)
489 {
490 // Create copy of the Evaluator.Object delegate, but with the object_constant proplist replaced by a version with filter_def
491 var object_options = Evaluator.Object.Options[:];
492 // Need to copy the option Value field to ensure it is owned by the correct parent.
493 // Otherwise it would be assigned by reference in the editor
494 var const_option = new EvaluatorDefs["object_constant"] { Value = { Function="object_constant", Value=nil } };
495 const_option.Delegate = new const_option.Delegate { Filter=filter_def };
496 object_options[const_option.OptionIndex] = const_option;
497 var new_evaluator = new Evaluator.Object { Name=name, Options=object_options, EditorHelp=help };
498 if (!object_evaluators) object_evaluators = [];
499 object_evaluators[GetLength(object_evaluators)] = new_evaluator;
500 return new_evaluator;
501 }
502
CopyProplist(p)503 private func CopyProplist(p)
504 {
505 // Create copy of p to ensure unique global reference
506 if (GetType(p) != C4V_PropList) return p;
507 var r = {};
508 for (var k in GetProperties(p)) r[k] = p[k];
509 return r;
510 }
511
AddEvaluator(string eval_type,string group,name,string help,string identifier,callback_data,default_val,proplist delegate,string delegate_storage_key)512 public func AddEvaluator(string eval_type, string group, name, string help, string identifier, callback_data, default_val, proplist delegate, string delegate_storage_key)
513 {
514 // Add an evaluator for one of the data types. Evaluators allow users to write small action sequences and scripts in the editor using dropdown lists.
515 // eval_type: Return type of the evaluator (Action, Object, Boolean, Player, etc. as defined in UserAction.Evaluator)
516 // group [optional] Localized name of sub-group for larger enums (i.e. actions)
517 // name: Localized name as it appears in the dropdown list of evaluators. May also be an array [name, short_name].
518 // identifier: Unique identifier that is used to store this action in savegames and look up the action def. Identifiers must be unique among all data types.
519 // callback_data: Array of [definition, definition.Function, parameter (optional)]. Function to be called when this evaluator is called
520 // default_val [optional]: Default value to be set when this evaluator is selected. Must be a proplist. Should contain values for all properties in the delegate
521 // delegate: Parameters for this evaluator
522 // Copy all value evaluations
523 if (eval_type != "Action" && eval_type != "Any" && callback_data[1] != UserAction.EvalVariable)
524 {
525 var any_group;
526 if (group) any_group = Format("%s/%s", EvaluatorTypeNames[eval_type], group); else any_group = EvaluatorTypeNames[eval_type];
527 AddEvaluator("Any", any_group, name, help, identifier, callback_data, CopyProplist(default_val), delegate, delegate_storage_key);
528 }
529 // Dissect parameters
530 if (group) group = GroupNames[group] ?? group; // resolve localized group name
531 var short_name;
532 if (GetType(name) == C4V_Array)
533 {
534 short_name = name[1];
535 name = name[0];
536 }
537 else if (delegate && delegate.HideFullName)
538 {
539 // Some proplist delegates provide their own display string and need not show the option name
540 short_name = "";
541 }
542 if (!default_val) default_val = {};
543 var default_get;
544 if (GetType(default_val) == C4V_Function)
545 {
546 default_get = default_val;
547 default_val = Call(default_get, nil, {Function=identifier});
548 }
549 default_val.Function = identifier;
550 var action_def = { Name=name, ShortName=short_name, EditorHelp=help, Group=group, Value=default_val, Delegate=delegate, DefaultValueFunction=default_get }, n;
551 if (delegate)
552 {
553 if (delegate.EditorProps || delegate.Elements)
554 {
555 // Proplist of array parameter for this evaluator: Descend path title should be name
556 delegate.Name = name;
557 var child_delegate = delegate;
558 if (delegate.DescendPath) child_delegate = delegate.EditorProps[delegate.DescendPath];
559 if (!child_delegate.EditorHelp) child_delegate.EditorHelp = help;
560 }
561 else
562 {
563 // Any other parameter type: Store in value
564 action_def.ValueKey = delegate_storage_key ?? "Value";
565 }
566 }
567 // Constant has higher priority
568 if (callback_data[1] == UserAction.EvalConstant)
569 {
570 action_def.Priority = 50;
571 }
572 Evaluator[eval_type].Options[n = GetLength(Evaluator[eval_type].Options)] = action_def;
573 action_def.OptionIndex = n;
574 // Remember lookup table through identifier (ignore duplicates)
575 if (eval_type != "Any" || !group)
576 {
577 EvaluatorCallbacks[identifier] = callback_data;
578 EvaluatorDefs[identifier] = action_def;
579 }
580 // Copy any object evaluators to existing evaluator lists
581 if (eval_type == "Object" && object_evaluators)
582 for (var obj_eval in object_evaluators)
583 obj_eval.Options[GetLength(obj_eval.Options)] = action_def;
584 return action_def;
585 }
586
EvaluateValue(string eval_type,proplist props,proplist context)587 public func EvaluateValue(string eval_type, proplist props, proplist context)
588 {
589 //Log("EvaluateValue %v %v %v", eval_type, props, context);
590 if (!props) return nil;
591 // Finish any hold-action
592 if (context.hold == props)
593 {
594 context.hold = nil;
595 return context.hold_result;
596 }
597 // Not on hold: Perform evaluation
598 var cb = EvaluatorCallbacks[props.Function];
599 /*var rval = cb[0]->Call(cb[1], props, context, cb[2]);
600 Log("%v <- EvaluateValue %v %v %v", rval, eval_type, props, context);
601 return rval;*/
602 return cb[0]->Call(cb[1], props, context, cb[2]);
603 }
604
EvaluateAction(proplist props,object action_object,object triggering_object,int triggering_player,string progress_mode,bool allow_parallel,finish_callback,array position)605 public func EvaluateAction(proplist props, object action_object, object triggering_object, int triggering_player, string progress_mode, bool allow_parallel, finish_callback, array position)
606 {
607 // No action
608 if (!props) if (finish_callback) return action_object->Call(finish_callback); else return;
609 // Determine context
610 var context;
611 if (!progress_mode)
612 {
613 if (!(context = props._context))
614 props._context = context = CreateObject(UserAction);
615 }
616 else if (progress_mode == "player")
617 {
618 if (!props._contexts) props._contexts = [];
619 var plr_id = GetPlayerID(triggering_player);
620 if (!(context = props._contexts[plr_id]))
621 props._contexts[plr_id] = context = CreateObject(UserAction);
622 }
623 else // if (progress_mode == "session")
624 {
625 // Temporary context
626 context = CreateObject(UserAction);
627 context.temp = true;
628 }
629 // Prevent duplicate parallel execution
630 if (!allow_parallel && (context.hold && !context.suspended)) return false;
631 // Init context settings
632 context->InitContext(action_object, triggering_player, triggering_object, props, finish_callback, position);
633 // Execute action
634 EvaluateValue("Action", props, context);
635 FinishAction(context);
636 return true;
637 }
638
EvaluateCondition(proplist props,object action_object,object triggering_object,int triggering_player)639 public func EvaluateCondition(proplist props, object action_object, object triggering_object, int triggering_player)
640 {
641 // Build temp context
642 var context = CreateObject(UserAction);
643 context.temp = true;
644 // Init context settings
645 context->InitContext(action_object, triggering_player, triggering_object, props);
646 // Execute condition evaluator
647 var result = EvaluateValue("Boolean", props, context);
648 // Cleanup
649 if (context) context->RemoveObject();
650 // Done
651 return result;
652 }
653
EvaluateString(proplist props,object context)654 public func EvaluateString(proplist props, object context)
655 {
656 return EvaluateValue("String", props, context) ?? "";
657 }
658
EvaluatePosition(proplist props,object context)659 private func EvaluatePosition(proplist props, object context)
660 {
661 // Execute position evaluator; fall back to position of action object
662 var position = EvaluateValue("Position", props, context);
663 if (!position)
664 {
665 if (context.action_object) position = [context.action_object->GetX(), context.action_object->GetY()];
666 else position = [0,0];
667 }
668 return position;
669 }
670
EvaluateOffset(proplist props,object context)671 private func EvaluateOffset(proplist props, object context)
672 {
673 // Execute offset evaluator; fall back to [0, 0]
674 return EvaluateValue("Offset", props, context) ?? [0,0];
675 }
676
EvaluatePlayer(proplist props,object context)677 private func EvaluatePlayer(proplist props, object context)
678 {
679 // Execute player evaluator; nil means NO_OWNER
680 var plr = EvaluateValue("Player", props, context);
681 if (!GetType(plr)) plr = NO_OWNER;
682 return plr;
683 }
684
ResumeAction(proplist context,proplist resume_props)685 private func ResumeAction(proplist context, proplist resume_props)
686 {
687 //Log("ResumeAction %v %v", context, resume_props);
688 // Resume only if on hold for the same entry
689 if (context.hold != resume_props) return;
690 // Not if owning object is dead
691 if (!context.action_object) return;
692 // Resume action
693 EvaluateValue("Action", context.root_action, context);
694 // Cleanup action object (unless it ran into another hold)
695 FinishAction(context);
696 }
697
FinishAction(proplist context)698 private func FinishAction(proplist context)
699 {
700 // Cleanup action object (unless it's kept around for callbacks or to store sequence progress)
701 // Note that context.root_action.contexts is checked to kill session-contexts that try to suspend
702 // There would be no way to resume so just kill the context
703 if (!context.hold || context.temp)
704 {
705 if (context.action_object && context.finish_callback) context.action_object->Call(context.finish_callback, context);
706 context->RemoveObject();
707 }
708 }
709
EvalConstant(proplist props,proplist context)710 private func EvalConstant(proplist props, proplist context) { return props.Value; }
EvalStringConstant(proplist props,proplist context)711 private func EvalStringConstant(proplist props, proplist context) { return GetTranslatedString(props.Value); }
EvalContextValue(proplist props,proplist context,string name)712 private func EvalContextValue(proplist props, proplist context, string name) { return context[name]; }
EvalObj_ActionObject(proplist props,proplist context)713 private func EvalObj_ActionObject(proplist props, proplist context) { return context.action_object; }
EvalObj_TriggeringObject(proplist props,proplist context)714 private func EvalObj_TriggeringObject(proplist props, proplist context) { return context.triggering_object; }
EvalObj_TriggeringClonk(proplist props,proplist context)715 private func EvalObj_TriggeringClonk(proplist props, proplist context) { return context.triggering_clonk; }
EvalObj_LastCreatedObject(proplist props,proplist context)716 private func EvalObj_LastCreatedObject(proplist props, proplist context) { return context.last_created_object; }
EvalPlr_Trigger(proplist props,proplist context)717 private func EvalPlr_Trigger(proplist props, proplist context) { return context.triggering_player; }
EvalPlrList_Single(proplist props,proplist context,fn)718 private func EvalPlrList_Single(proplist props, proplist context, fn) { return [Call(fn, props, context)]; }
719
EvalCount(proplist props,proplist context,data_type)720 private func EvalCount(proplist props, proplist context, data_type)
721 {
722 var list = EvaluateValue(data_type, props.Array, context);
723 if (list) return GetLength(list); else return 0;
724 }
725
EvalObjList_FindObjectsInArea(proplist props,proplist context,bool find_one)726 private func EvalObjList_FindObjectsInArea(proplist props, proplist context, bool find_one)
727 {
728 var params = Find_And(), np=1;
729 // Resolve area parameter
730 if (props.Area)
731 {
732 var area = props.Area.Area;
733 var fn = props.Area.Function;
734 var area_criterion;
735 if (fn == "InRect")
736 area_criterion = Find_InRect(area[0], area[1], area[2], area[3]);
737 else if (fn == "AtRect")
738 area_criterion = Find_AtRect(area[0], area[1], area[2], area[3]);
739 else if (fn == "Circle")
740 area_criterion = Find_Distance(area[0], area[1], area[2]);
741 else if (fn == "NearPosition")
742 {
743 var pos_params = props.Area.Parameters;
744 var pos = EvaluatePosition(pos_params.Position, context);
745 area_criterion = Find_Distance(pos_params.Radius, pos[0], pos[1]);
746 }
747 if (area_criterion) params[np++] = area_criterion;
748 }
749 // Other parameters
750 var idobj = EvaluateValue("Definition", props.ID, context);
751 if (idobj) params[np++] = Find_ID(idobj);
752 if (!props.AllowContained) params[np++] = Find_NoContainer();
753 // Find objects
754 var result = FindObjects(params);
755 if (find_one) return result[0]; else return result;
756 }
757
EvalObjList_FindObjectInContainer(proplist props,proplist context)758 private func EvalObjList_FindObjectInContainer(proplist props, proplist context)
759 {
760 var container = EvaluateValue("Object", props.Container, context);
761 var idobj = EvaluateValue("Definition", props.ID, context);
762 if (!container) return;
763 if (idobj)
764 return container->FindContents(idobj);
765 else
766 return container->Contents();
767 }
768
EvalObjList_FindObjectsInContainer(proplist props,proplist context)769 private func EvalObjList_FindObjectsInContainer(proplist props, proplist context)
770 {
771 // Return array of all objects contained in container
772 var container = EvaluateValue("Object", props.Container, context);
773 var idobj = EvaluateValue("Definition", props.ID, context);
774 if (!container) return; // nil is treated as empty list
775 var i, n = container->ContentsCount(idobj), j;
776 if (!n) return;
777 var result = CreateArray(n), obj;
778 while ((obj = container->Contents(i++)))
779 if (!idobj || obj->GetID() == idobj)
780 result[j++] = obj;
781 return result;
782 }
783
EvalPlrList_All(proplist props,proplist context,fn)784 private func EvalPlrList_All(proplist props, proplist context, fn)
785 {
786 var n = GetPlayerCount(C4PT_User);
787 var result = CreateArray(n);
788 for (var i=0; i<n; ++i) result[i] = GetPlayerByIndex(i);
789 return result;
790 }
791
EvalComparison(proplist props,proplist context,data_type)792 private func EvalComparison(proplist props, proplist context, data_type)
793 {
794 var left = EvaluateValue(data_type, props.LeftOperand, context);
795 var right = EvaluateValue(data_type, props.RightOperand, context);
796 if (!left) left = nil; // 0 ==nil
797 if (!right) right = nil; // 0 == nil
798 var op = props.Operator;
799 if (!op)
800 return left == right;
801 else if (op == "ne")
802 return left != right;
803 else if (op == "lt")
804 return left < right;
805 else if (op == "gt")
806 return left > right;
807 else if (op == "le")
808 return left <= right;
809 else if (op == "ge")
810 return left >= right;
811 }
812
EvalBool_Not(proplist props,proplist context)813 private func EvalBool_Not(proplist props, proplist context) { return !EvaluateValue("Boolean", props.Operand, context); }
814
EvalBool_And(proplist props,proplist context)815 private func EvalBool_And(proplist props, proplist context)
816 {
817 for (var cond in props.Operands)
818 if (!EvaluateValue("Boolean", cond, context))
819 return false;
820 return true;
821 }
822
EvalBool_Or(proplist props,proplist context)823 private func EvalBool_Or(proplist props, proplist context)
824 {
825 for (var cond in props.Operands)
826 if (EvaluateValue("Boolean", cond, context))
827 return true;
828 return false;
829 }
830
EvalBool_ObjectExists(proplist props,proplist context)831 private func EvalBool_ObjectExists(proplist props, proplist context) { return !!EvaluateValue("Object", props.Object, context); }
832
EvalBool_ObjectAlive(proplist props,proplist context)833 private func EvalBool_ObjectAlive(proplist props, proplist context)
834 {
835 var obj = EvaluateValue("Object", props.Object, context);
836 return obj && obj->GetAlive();
837 }
838
EvalAct_Sequence(proplist props,proplist context)839 private func EvalAct_Sequence(proplist props, proplist context)
840 {
841 // Sequence execution: Iterate over actions until one action puts the context on hold
842 var n = GetLength(props.Actions), sid = props._sequence_id;
843 if (!sid) sid = props._sequence_id = Format("%d", ++UserAction_SequenceIDs);
844 for (var progress = context.action_data[sid] ?? 0; progress < n; ++progress)
845 {
846 //Log("Sequence progress exec %v %v", progress, context.hold);
847 // goto preparations
848 context.sequence_had_goto[sid] = false;
849 context.last_sequence = props;
850 // Evaluate next sequence step
851 EvaluateValue("Action", props.Actions[progress], context);
852 if (context.hold || context.suspended)
853 {
854 // Execution on hold (i.e. wait action). Stop execution for now
855 if (!context.hold) progress = 0; // No hold specified: Stop with sequence reset
856 context.action_data[sid] = progress;
857 return;
858 }
859 // Apply jump in the sequence
860 if (context.sequence_had_goto[sid]) progress = context.action_data[sid] - 1;
861 }
862 // Sequence finished
863 context.last_sequence = nil;
864 // Reset for next execution.
865 context.action_data[sid] = 0;
866 }
867
EvalAct_Goto(proplist props,proplist context)868 private func EvalAct_Goto(proplist props, proplist context)
869 {
870 // Apply goto by jumping in most recently executed sequence
871 if (context.last_sequence)
872 {
873 var index = props.Index;
874 if (GetType(index) != C4V_Int) index = EvaluateValue("Integer", index, context); // compatibility
875 context.action_data[context.last_sequence._sequence_id] = index;
876 context.sequence_had_goto[context.last_sequence._sequence_id] = true;
877 }
878 }
879
EvalAct_StopSequence(proplist props,proplist context)880 private func EvalAct_StopSequence(proplist props, proplist context)
881 {
882 // Stop: Suspend without hold props, which causes all sequences to reset
883 context.hold = nil;
884 context.suspended = true;
885 }
886
EvalAct_SuspendSequence(proplist props,proplist context)887 private func EvalAct_SuspendSequence(proplist props, proplist context)
888 {
889 // Suspend: Remember hold position and stop action execution
890 context.hold = props;
891 context.suspended = true;
892 }
893
EvalAct_For_IntRange(proplist props,proplist context)894 private func EvalAct_For_IntRange(proplist props, proplist context)
895 {
896 // Create list with range of integers
897 // Both start and end inclusive
898 var start = EvaluateValue("Integer", props.Start, context);
899 var end = EvaluateValue("Integer", props.End, context);
900 var step = EvaluateValue("Integer", props.Step, context);
901 if (!step) return [];
902 var d = end - start;
903 if (d * step < 0) return []; // wrong direction
904 var n = (end - start) / step + 1;
905 var list = CreateArray(n), i;
906 for (var v = start; v <= end; v += step) list[i++] = v;
907 return list;
908 }
909
EvalAct_For_ObjectList(proplist props,proplist context)910 private func EvalAct_For_ObjectList(proplist props, proplist context)
911 {
912 return EvaluateValue("ObjectList", props.Objects, context) ?? [];
913 }
914
EvalAct_For_PlayerList(proplist props,proplist context)915 private func EvalAct_For_PlayerList(proplist props, proplist context)
916 {
917 return EvaluateValue("PlayerList", props.Players, context) ?? [];
918 }
919
EvalAct_For(proplist props,proplist context,list_function)920 private func EvalAct_For(proplist props, proplist context, list_function)
921 {
922 // For: Iterate over elements until one action puts the context on hold
923 // list_info: [list_function, current_item_field]
924 var sid = props._sequence_id;
925 if (!sid) sid = props._sequence_id = Format("%d", ++UserAction_SequenceIDs);
926 // Get list data
927 var list, progress;
928 if (!context.action_data[sid])
929 {
930 list = Call(list_function, props, context);
931 }
932 else
933 {
934 list = context.action_data[sid][0];
935 progress = context.action_data[sid][1];
936 context.action_data[sid] = nil;
937 }
938 var n = GetLength(list);
939 for (; progress < n; ++progress)
940 {
941 // Get iterated item
942 var curr_value = list[progress];
943 if (!GetType(curr_value)) continue; // Ignore nil (deleted objects)
944 context[props.Function] = curr_value;
945 // Evaluate next sequence step
946 EvaluateValue("Action", props.Action, context);
947 if (context.hold || context.suspended)
948 {
949 // Execution on hold (i.e. wait action). Stop execution for now
950 if (context.hold) context.action_data[sid] = [list, progress];
951 return;
952 }
953 }
954 }
955
EvalAct_Wait(proplist props,proplist context)956 private func EvalAct_Wait(proplist props, proplist context)
957 {
958 // Wait for specified number of frames
959 context.hold = props;
960 ScheduleCall(context, UserAction.ResumeAction, props.Time, 1, context, props);
961 }
962
EvalAct_WaitCondition(proplist props,proplist context)963 private func EvalAct_WaitCondition(proplist props, proplist context)
964 {
965 // Poll condition and resume when it's met.
966 var cond = props.Condition;
967 // Invalid condition?
968 if (!cond) return;
969 // Condition fulfilled?
970 if (EvaluateValue("Boolean", cond, context)) return;
971 // Re-check condition periodically
972 context.hold = props;
973 ScheduleCall(context, UserAction.EvalAct_WaitConditionRe, props.Interval, 0x7fffffff, props, context);
974 }
975
EvalAct_WaitConditionRe(proplist props,proplist context)976 private func EvalAct_WaitConditionRe(proplist props, proplist context)
977 {
978 if (UserAction->EvaluateValue("Boolean", props.Condition, context))
979 {
980 ClearScheduleCall(context, UserAction.EvalAct_WaitConditionRe);
981 UserAction->ResumeAction(context, props);
982 }
983 }
984
EvalAct_Sound(proplist props,proplist context)985 private func EvalAct_Sound(proplist props, proplist context)
986 {
987 if (!props.Sound) return;
988 var sound_context;
989 if (props.SourceObject)
990 {
991 sound_context = EvaluateValue("Object", props.SourceObject, context);
992 if (!sound_context) return;
993 }
994 else
995 {
996 sound_context = Global;
997 }
998 var volume = EvaluateValue("Integer", props.Volume, context);
999 var pitch = EvaluateValue("Integer", props.Pitch, context);
1000 if (props.TargetPlayers == "all_players")
1001 {
1002 sound_context->Sound(props.Sound, true, volume, nil, props.Loop, nil, pitch);
1003 }
1004 else
1005 {
1006 for (var plr in EvaluateValue("PlayerList", props.TargetPlayers, context))
1007 {
1008 sound_context->Sound(props.Sound, false, volume, plr, props.Loop, nil, pitch);
1009 }
1010 }
1011 }
1012
EvalAct_CreateObject(proplist props,proplist context)1013 private func EvalAct_CreateObject(proplist props, proplist context)
1014 {
1015 // Create a new object
1016 var create_id = EvaluateValue("Definition", props.ID, context);
1017 if (!create_id) return;
1018 var owner = EvaluatePlayer(props.Owner, context);
1019 var container = EvaluateValue("Object", props.Container, context);
1020 var obj;
1021 if (container)
1022 {
1023 // Contained object
1024 obj = container->CreateContents(create_id);
1025 if (obj) obj->SetOwner(owner);
1026 }
1027 else
1028 {
1029 // Uncontained object
1030 var position = EvaluatePosition(props.Position, context);
1031 var speed_x = EvaluateValue("Integer", props.SpeedX, context);
1032 var speed_y = EvaluateValue("Integer", props.SpeedY, context);
1033 var rotation = EvaluateValue("Integer", props.Rotation, context);
1034 var speed_r = EvaluateValue("Integer", props.SpeedR, context);
1035 if (props.CreateAbove)
1036 obj = Global->CreateObjectAbove(create_id, position[0], position[1], owner);
1037 else
1038 obj = Global->CreateObject(create_id, position[0], position[1], owner);
1039 // Default speed and rotation
1040 if (obj) obj->SetXDir(speed_x);
1041 if (obj) obj->SetYDir(speed_y);
1042 if (obj) obj->SetR(rotation);
1043 if (obj) obj->SetRDir(speed_r, 5); // Internal rdir value is in five degrees frame
1044 }
1045 // Remember object for later access
1046 context.last_created_object = obj;
1047 }
1048
EvalAct_CastObjects(proplist props,proplist context)1049 private func EvalAct_CastObjects(proplist props, proplist context)
1050 {
1051 // Cast objects in multiple directions
1052 var create_id = EvaluateValue("Definition", props.ID, context);
1053 if (!create_id) return;
1054 var owner = EvaluatePlayer(props.Owner, context);
1055 var amount = EvaluateValue("Integer", props.Amount, context);
1056 var speed = EvaluateValue("Integer", props.Speed, context);
1057 var mean_angle = EvaluateValue("Integer", props.MeanAngle, context);
1058 var angle_deviation = EvaluateValue("Integer", props.AngleDeviation, context);
1059 var position = EvaluatePosition(props.Position, context);
1060 context.last_casted_objects = CastObjects(create_id, amount, speed, position[0], position[1], mean_angle, angle_deviation);
1061 }
1062
EvalAct_CastParticles(proplist props,proplist context)1063 private func EvalAct_CastParticles(proplist props, proplist context)
1064 {
1065 var particle_name = props.Name;
1066 var amount = EvaluateValue("Integer", props.Amount, context);
1067 var speed = EvaluateValue("Integer", props.Speed, context);
1068 var size_start = EvaluateValue("Integer", props.Size, context);
1069 var size_end = EvaluateValue("Integer", props.SizeEnd, context);
1070 var lifetime = EvaluateValue("Integer", props.Lifetime, context);
1071 if (lifetime <= 0) return;
1072 var position = EvaluatePosition(props.Position, context);
1073 var color = (EvaluateValue("Color", props.Color, context) ?? 0xffffff) | 0xff000000;
1074 var blit_mode = props.BlitMode;
1075 var fadeout = props.FadeOut;
1076 var gravity = EvaluateValue("Integer", props.Gravity, context);
1077 var collision_func = props.CollisionFunc;
1078 var prototype =
1079 {
1080 BlitMode = props.BlitMode,
1081 Size = PV_Linear(size_start, size_end),
1082 Rotation = PV_Direction(),
1083 R = (color >> 16) & 0xff,
1084 G = (color >> 8) & 0xff,
1085 B = (color >> 0) & 0xff,
1086 CollisionVertex = 500
1087 };
1088 if (fadeout) prototype.Alpha = PV_Linear(255, 0); else prototype.Alpha=255;
1089 if (gravity) prototype.ForceY = PV_Gravity(gravity);
1090 if (collision_func == "pass")
1091 {
1092 prototype.CollisionDensity = 9999;
1093 }
1094 else if (collision_func == "bounce")
1095 {
1096 prototype.OnCollision = PC_Bounce(500);
1097 }
1098 else if (collision_func == "die")
1099 {
1100 prototype.OnCollision = PC_Die();
1101 }
1102 else if (collision_func == "stop")
1103 {
1104 prototype.OnCollision = PC_Stop();
1105 }
1106 CreateParticle(particle_name, position[0], position[1], PV_Random(-speed, speed), PV_Random(-speed, speed), lifetime, prototype, amount);
1107 }
1108
EvalAct_RemoveObject(proplist props,proplist context)1109 private func EvalAct_RemoveObject(proplist props, proplist context)
1110 {
1111 var obj = EvaluateValue("Object", props.Object, context);
1112 if (!obj) return;
1113 obj->RemoveObject(props.EjectContents);
1114 }
1115
EvalAct_SetPosition(proplist props,proplist context)1116 private func EvalAct_SetPosition(proplist props, proplist context)
1117 {
1118 var obj = EvaluateValue("Object", props.Object, context);
1119 if (!obj) return;
1120 var pos = EvaluatePosition(props.Position, context);
1121 obj->SetPosition(pos[0], pos[1]);
1122 }
1123
EvalAct_Fling(proplist props,proplist context)1124 private func EvalAct_Fling(proplist props, proplist context)
1125 {
1126 var obj = EvaluateValue("Object", props.Object, context);
1127 if (!obj) return;
1128 var vx = EvaluateValue("Integer", props.SpeedX, context);
1129 var vy = EvaluateValue("Integer", props.SpeedY, context);
1130 var add_speed = EvaluateValue("Boolean", props.AddSpeed, context);
1131 obj->Fling(vx, vy, 10, add_speed);
1132 }
1133
EvalAct_EnterObject(proplist props,proplist context)1134 private func EvalAct_EnterObject(proplist props, proplist context)
1135 {
1136 var object = EvaluateValue("Object", props.Object, context);
1137 var container = EvaluateValue("Object", props.Container, context);
1138 if (!container || !object) return;
1139 // Enter either with a check (Collect) or just force-Enter
1140 if (!props.CollectionCheck)
1141 {
1142 object->Enter(container);
1143 }
1144 else
1145 {
1146 if (!container->Collect(object, true))
1147 {
1148 if (props.CollectionCheck == "exit")
1149 {
1150 object->SetPosition(container->GetX(), container->GetY());
1151 }
1152 }
1153 }
1154 }
1155
EvalAct_ExitObject(proplist props,proplist context)1156 private func EvalAct_ExitObject(proplist props, proplist context)
1157 {
1158 var object = EvaluateValue("Object", props.Object, context);
1159 if (object) object->Exit();
1160 }
1161
EvalAct_SetDirection(proplist props,proplist context)1162 private func EvalAct_SetDirection(proplist props, proplist context)
1163 {
1164 var object = EvaluateValue("Object", props.Object, context);
1165 if (object) object->SetDir(props.Direction);
1166 }
1167
EvalAct_SetVisibility(proplist props,proplist context)1168 private func EvalAct_SetVisibility(proplist props, proplist context)
1169 {
1170 var object = EvaluateValue("Object", props.Object, context);
1171 if (object) object.Visibility = props.Visibility;
1172 }
1173
EvalAct_SetVincibility(proplist props,proplist context)1174 private func EvalAct_SetVincibility(proplist props, proplist context)
1175 {
1176 var object = EvaluateValue("Object", props.Object, context);
1177 if (object) object->SetInvincibility(!props.Vincibility);
1178 }
1179
EvalAct_DoWealth(proplist props,proplist context)1180 private func EvalAct_DoWealth(proplist props, proplist context)
1181 {
1182 var player = EvaluatePlayer(props.Player, context);
1183 var change = EvaluateValue("Integer", props.Change, context);
1184 if (player != NO_OWNER && change)
1185 {
1186 SetWealth(player, GetWealth(player) + change);
1187 var do_sound = EvaluateValue("Boolean", props.DoSound, context);
1188 if (do_sound)
1189 {
1190 if (change < 0) Sound("UI::Cash*", true, nil, player); else Sound("UI::UnCash*", true, nil, player);
1191 }
1192 }
1193 }
1194
EvalAct_PlrKnowledge(proplist props,proplist context)1195 private func EvalAct_PlrKnowledge(proplist props, proplist context)
1196 {
1197 var players = EvaluateValue("PlayerList", props.Players, context) ?? [];
1198 var def = EvaluateValue("Definition", props.ID, context);
1199 if (!def) return;
1200 for (var plr in players) SetPlrKnowledge(plr, def);
1201 }
1202
EvalAct_PlrView(proplist props,proplist context)1203 private func EvalAct_PlrView(proplist props, proplist context)
1204 {
1205 var players = EvaluateValue("PlayerList", props.Players, context) ?? [];
1206 var target = EvaluateValue("Object", props.Target, context);
1207 var immediate = props.Immediate;
1208 if (!target) return;
1209 for (var plr in players) SetPlrView(plr, target, immediate);
1210 }
1211
EvalAct_ObjectCallInt(proplist props,proplist context,func call_fn)1212 private func EvalAct_ObjectCallInt(proplist props, proplist context, func call_fn)
1213 {
1214 var obj = EvaluateValue("Object", props.Object, context);
1215 if (!obj) return;
1216 var parameter = EvaluateValue("Integer", props.Value, context);
1217 obj->Call(call_fn, parameter);
1218 }
1219
EvalAct_If(proplist props,proplist context)1220 private func EvalAct_If(proplist props, proplist context)
1221 {
1222 // Do evaluation on first pass. After that, take context value.
1223 var sid = props._sequence_id;
1224 if (!sid) sid = props._sequence_id = Format("%d", ++UserAction_SequenceIDs);
1225 var cond = context.action_data[sid] ?? !!EvaluateValue("Boolean", props.Condition, context);
1226 if (cond)
1227 EvaluateValue("Action", props.TrueEvaluator, context);
1228 else
1229 EvaluateValue("Action", props.FalseEvaluator, context);
1230 // Only keep conditional value within a held action
1231 if (context.hold) context.action_data[sid] = cond;
1232 }
1233
EvalConditionalValue(proplist props,proplist context,eval_type)1234 private func EvalConditionalValue(proplist props, proplist context, eval_type)
1235 {
1236 // Return value by condition
1237 if (EvaluateValue("Boolean", props.Condition, context))
1238 return EvaluateValue(eval_type, props.TrueEvaluator, context);
1239 else
1240 return EvaluateValue(eval_type, props.FalseEvaluator, context);
1241 }
1242
EvalAct_Log(proplist props,proplist context)1243 private func EvalAct_Log(proplist props, proplist context)
1244 {
1245 Log(EvaluateString(props.Message, context));
1246 }
1247
EvalAct_Nop(proplist props,proplist context)1248 private func EvalAct_Nop(proplist props, proplist context) {}
1249
GetVariableContext(proplist props,proplist context)1250 private func GetVariableContext(proplist props, proplist context)
1251 {
1252 // Resolve context for variable. Global or object context.
1253 var var_context = EvaluateValue("Object", props, context);
1254 if (!var_context)
1255 {
1256 if (!g_UserAction_global_vars) g_UserAction_global_vars = {};
1257 var_context = g_UserAction_global_vars;
1258 }
1259 else
1260 {
1261 if (!var_context.user_variables) var_context.user_variables = {};
1262 var_context = var_context.user_variables;
1263 }
1264 return var_context;
1265 }
1266
EvalAct_SetVariable(proplist props,proplist context)1267 private func EvalAct_SetVariable(proplist props, proplist context)
1268 {
1269 // Assign variable
1270 var var_context = GetVariableContext(props.Context, context);
1271 var var_name = StringToIdentifier(EvaluateString(props.VariableName, context));
1272 var value = EvaluateValue("Any", props.Value, context);
1273 var_context[var_name] = value;
1274 }
1275
EvalVariable(proplist props,proplist context,expected_type)1276 private func EvalVariable(proplist props, proplist context, expected_type)
1277 {
1278 // Get variable value
1279 var var_context = GetVariableContext(props.Context, context);
1280 var var_name = StringToIdentifier(EvaluateString(props.VariableName, context));
1281 var value = var_context[var_name];
1282 // Check type (except for C4V_Nil which means "Any" here)
1283 var val_type = GetType(value);
1284 if (val_type != expected_type && expected_type)
1285 {
1286 // Array types have special checking
1287 if (GetType(expected_type) == C4V_Array)
1288 {
1289 var expected_len = expected_type[1];
1290 var valid;
1291 if (val_type == C4V_Array)
1292 {
1293 // Check required length
1294 if (!expected_len || GetLength(value) == expected_len)
1295 {
1296 // Check data type of contents
1297 var subtype = expected_type[0];
1298 valid = true;
1299 for (var v in value) if (v && GetType(v) != subtype) valid = false;
1300 }
1301 }
1302 // Invalid value for expected array: Return default array.
1303 // All nil is OK because there is no string list type and nil converts to 0.
1304 if (!valid) return CreateArray(expected_len);
1305 }
1306 else
1307 {
1308 // Type not matching. Construct default.
1309 if (expected_type == C4V_Int) return 0;
1310 if (expected_type == C4V_Bool) return !!value; // This conversion is OK
1311 if (expected_type == C4V_String) return "";
1312 return nil;
1313 }
1314 }
1315 // Value OK. Return it.
1316 return value;
1317 }
1318
EvalScript(proplist props,proplist context)1319 private func EvalScript(proplist props, proplist context)
1320 {
1321 var script_context;
1322 if (props.Context)
1323 {
1324 if (!(script_context = EvaluateValue("Object", props.Context, context))) return;
1325 }
1326 else
1327 {
1328 script_context = Global;
1329 }
1330 var script = EvaluateString(props.Script, context);
1331 return script_context->eval(script, true);
1332 }
1333
EvalAct_GameOver(proplist props,proplist context)1334 private func EvalAct_GameOver(proplist props, proplist context) { GameOver(); }
1335
GetDefaultPosition(object target_object)1336 private func GetDefaultPosition(object target_object)
1337 {
1338 // Default position for constant absolute position evaluator: Use selected object position
1339 var value;
1340 if (target_object)
1341 value = [target_object->GetX(), target_object->GetY()];
1342 else
1343 value = [0,0];
1344 return { Function="position_constant", Value=value };
1345 }
1346
GetDefaultCoordinates(object target_object)1347 private func GetDefaultCoordinates(object target_object)
1348 {
1349 // Default position for constant absolute position evaluator: Use selected object position
1350 var value;
1351 if (target_object)
1352 value = {X={Function="int_constant", Value=target_object->GetX()}, Y={Function="int_constant", Value=target_object->GetY()}};
1353 else
1354 value = {X=0, Y=0};
1355 value.Function="position_coordinates";
1356 return value;
1357 }
1358
GetDefaultRect(object target_object,proplist props)1359 private func GetDefaultRect(object target_object, proplist props)
1360 {
1361 // Default rectangle around target object
1362 var r;
1363 if (target_object) r = [target_object->GetX()-30, target_object->GetY()-30, 60, 60]; else r = [100,100,100,100];
1364 return { Function=props.Function, Area=r };
1365 }
1366
GetDefaultCircle(object target_object,proplist props)1367 private func GetDefaultCircle(object target_object, proplist props)
1368 {
1369 // Default circle around target object
1370 var r;
1371 if (target_object) r = [50, target_object->GetX(), target_object->GetY()]; else r = [50,100,100];
1372 return { Function=props.Function, Area=r };
1373 }
1374
GetDefaultSearchRect(object target_object)1375 private func GetDefaultSearchRect(object target_object)
1376 {
1377 // Default rectangle around target object
1378 var r;
1379 if (target_object) r = [target_object->GetX()-30, target_object->GetY()-30, 60, 60]; else r = [100,100,100,100];
1380 return { Function="Rect", Area=r };
1381 }
1382
GetDefaultSearchCircle(object target_object)1383 private func GetDefaultSearchCircle(object target_object)
1384 {
1385 // Default circle around target object
1386 var r;
1387 if (target_object) r = [50, target_object->GetX(), target_object->GetY()]; else r = [50,100,100];
1388 return { Function="Circle", Area=r };
1389 }
1390
EvalIntRandom(proplist props,proplist context)1391 private func EvalIntRandom(proplist props, proplist context)
1392 {
1393 // Random value between min and max. Also allow them to be swapped.
1394 var min = EvaluateValue("Integer", props.Min, context);
1395 var max = EvaluateValue("Integer", props.Max, context);
1396 var rmin = Min(min,max);
1397 return Random(Max(max,min)-rmin) + rmin;
1398 }
1399
EvalPositionRelative(proplist props,proplist context)1400 private func EvalPositionRelative(proplist props, proplist context)
1401 {
1402 // Return position relative to action_object
1403 if (context.action_object)
1404 return [props.Value[0] + context.action_object->GetX(), props.Value[1] + context.action_object->GetY()];
1405 else
1406 return props.Value;
1407 }
1408
EvalCoordinates(proplist props,proplist context)1409 private func EvalCoordinates(proplist props, proplist context)
1410 {
1411 // Coordinate evaluation: Make array [X, Y]
1412 return [EvaluateValue("Integer", props.X, context), EvaluateValue("Integer", props.Y, context)];
1413 }
1414
EvalPositionOffset(proplist props,proplist context)1415 private func EvalPositionOffset(proplist props, proplist context)
1416 {
1417 var p = EvaluatePosition(props.Position, context);
1418 var o = EvaluateOffset(props.Offset, context);
1419 return [p[0]+o[0], p[1]+o[1]];
1420 }
1421
EvalPositionObject(proplist props,proplist context)1422 private func EvalPositionObject(proplist props, proplist context)
1423 {
1424 var obj = EvaluateValue("Object", props.Object, context);
1425 if (obj) return [obj->GetX(), obj->GetY()];
1426 return [0,0]; // undefined object: Position is 0/0 default
1427 }
1428
EvalPos_LastUse(proplist props,proplist context)1429 private func EvalPos_LastUse(proplist props, proplist context) { return context.position; }
1430
EvalPos_RandomRect(proplist props,proplist context,bool relative)1431 private func EvalPos_RandomRect(proplist props, proplist context, bool relative)
1432 {
1433 // Constant random distribution in rectangle
1434 var a = props.Area;
1435 var rval = [a[0] + Random(a[2]), a[1] + Random(a[3])];
1436 // Apply relative offset
1437 if (relative && context.action_object)
1438 {
1439 rval[0] += context.action_object->GetX();
1440 rval[1] += context.action_object->GetY();
1441 }
1442 return rval;
1443 }
1444
EvalPos_RandomCircle(proplist props,proplist context,bool relative)1445 private func EvalPos_RandomCircle(proplist props, proplist context, bool relative)
1446 {
1447 // Constant random distribution in circle
1448 var a = props.Area;
1449 var r = a[0];
1450 r = Sqrt(Random(r*r));
1451 var ang = Random(360);
1452 var x = Sin(ang, r), y = Cos(ang, r);
1453 var rval = [a[1]+x, a[2]+y];
1454 // Apply relative offset
1455 if (relative && context.action_object)
1456 {
1457 rval[0] += context.action_object->GetX();
1458 rval[1] += context.action_object->GetY();
1459 }
1460 return rval;
1461 }
1462
EvalOffsetAdd(proplist props,proplist context)1463 private func EvalOffsetAdd(proplist props, proplist context)
1464 {
1465 var o1 = EvaluateOffset(props.Offset1, context);
1466 var o2 = EvaluateOffset(props.Offset2, context);
1467 return [o1[0]+o2[0], o1[1]+o2[1]];
1468 }
1469
EvalOffsetDiff(proplist props,proplist context)1470 private func EvalOffsetDiff(proplist props, proplist context)
1471 {
1472 var pA = EvaluatePosition(props.PositionA, context);
1473 var pB = EvaluatePosition(props.PositionB, context);
1474 return [pB[0]-pA[0], pB[1]-pA[1]];
1475 }
1476
EvalInt_Add(proplist props,proplist context)1477 private func EvalInt_Add(proplist props, proplist context) { return EvaluateValue("Integer", props.LeftOperand, context) + EvaluateValue("Integer", props.RightOperand, context); }
EvalInt_Sub(proplist props,proplist context)1478 private func EvalInt_Sub(proplist props, proplist context) { return EvaluateValue("Integer", props.LeftOperand, context) - EvaluateValue("Integer", props.RightOperand, context); }
EvalInt_Mul(proplist props,proplist context)1479 private func EvalInt_Mul(proplist props, proplist context) { return EvaluateValue("Integer", props.LeftOperand, context) * EvaluateValue("Integer", props.RightOperand, context); }
1480
EvalInt_Div(proplist props,proplist context)1481 private func EvalInt_Div(proplist props, proplist context)
1482 {
1483 var a = EvaluateValue("Integer", props.LeftOperand, context), b = EvaluateValue("Integer", props.RightOperand, context);
1484 return a/(b+!b);
1485 }
1486
EvalInt_Mod(proplist props,proplist context)1487 private func EvalInt_Mod(proplist props, proplist context)
1488 {
1489 var a = EvaluateValue("Integer", props.LeftOperand, context), b = EvaluateValue("Integer", props.RightOperand, context);
1490 return a%(b+!b);
1491 }
1492
EvalInt_Distance(proplist props,proplist context)1493 private func EvalInt_Distance(proplist props, proplist context)
1494 {
1495 var pA = EvaluatePosition(props.PositionA, context);
1496 var pB = EvaluatePosition(props.PositionB, context);
1497 return Distance(pA[0], pA[1], pB[0], pB[1]);
1498 }
1499
EvalInt_Wealth(proplist props,proplist context)1500 private func EvalInt_Wealth(proplist props, proplist context) { return GetWealth(EvaluatePlayer(props.Player, context)); }
1501
EvalInt_PosCoord(proplist props,proplist context,int idx)1502 private func EvalInt_PosCoord(proplist props, proplist context, int idx) { return EvaluatePosition(props.Position, context)[idx]; }
1503
EvalClr_PlayerColor(proplist props,proplist context)1504 private func EvalClr_PlayerColor(proplist props, proplist context) { return GetPlayerColor(EvaluatePlayer(props.Player, context)); }
1505
EvalClr_RGB(proplist props,proplist context)1506 private func EvalClr_RGB(proplist props, proplist context)
1507 {
1508 var R = EvaluateValue("Integer", props.R, context);
1509 var G = EvaluateValue("Integer", props.G, context);
1510 var B = EvaluateValue("Integer", props.B, context);
1511 return RGB(R, G, B);
1512 }
1513
EvalClr_Random(proplist props,proplist context)1514 private func EvalClr_Random(proplist props, proplist context)
1515 {
1516 var a = EvaluateValue("Color", props.ColorA, context);
1517 var b = EvaluateValue("Color", props.ColorB, context);
1518 var result;
1519 for (var i=0; i<3; ++i)
1520 {
1521 var ca = (a>>(i*8)) & 0xff;
1522 var cb = (b>>(i*8)) & 0xff;
1523 result |= ((Random(Abs(ca-cb+1)) + Min(ca, cb)) << (i*8));
1524 }
1525 return result;
1526 }
1527
EvalStr_ValueToString(proplist props,proplist context)1528 private func EvalStr_ValueToString(proplist props, proplist context)
1529 {
1530 return Format("%v", EvaluateValue("Any", props.Value, context));
1531 }
1532
EvalStr_Concat(proplist props,proplist context)1533 private func EvalStr_Concat(proplist props, proplist context)
1534 {
1535 var result="";
1536 for (var s in props.Substrings) result = Format("%s%s", result, EvaluateString(s, context));
1537 return result;
1538 }
1539
EvalObjProp(proplist props,proplist context,prop_fn)1540 private func EvalObjProp(proplist props, proplist context, prop_fn)
1541 {
1542 var obj = EvaluateValue("Object", props.Object, context);
1543 if (!obj) return nil;
1544 return obj->Call(prop_fn);
1545 }
1546
1547
1548
1549 /* Context instance */
1550
1551 // Proplist holding progress in each sequence
1552 local action_data, sequence_had_goto;
1553 static UserAction_SequenceIDs;
1554
1555 // Set to innermost sequence (for goto)
1556 local last_sequence;
1557
1558 // If this action is paused and will be resumed by a callback or by re-execution of the action, this property is set to the props of the holding action
1559 local hold;
1560
1561 // Set to true if action is on hold but waiting for re-execution
1562 local suspended;
1563
1564 // Return value if a value-providing evaluator is held
1565 local hold_result;
1566
Initialize()1567 public func Initialize()
1568 {
1569 action_data = {};
1570 sequence_had_goto = {};
1571 return true;
1572 }
1573
InitContext(object action_object,int triggering_player,object triggering_object,proplist props,finish_callback,position)1574 public func InitContext(object action_object, int triggering_player, object triggering_object, proplist props, finish_callback, position)
1575 {
1576 // Determine triggering player+objects
1577 var triggering_clonk;
1578 // Triggering player unknown? Try fallback to the controller of the triggering object
1579 if (!GetType(triggering_player) && triggering_object)
1580 {
1581 triggering_player = triggering_object->GetController();
1582 }
1583 // Triggering clonk is the selected clonk of the triggering player
1584 if (GetType(triggering_player))
1585 {
1586 triggering_clonk = GetCursor(triggering_player);;
1587 if (!triggering_clonk) triggering_clonk = GetCrew(triggering_player);
1588 }
1589 // Triggering object / Triggering player clonk fallbacks
1590 if (!triggering_object)
1591 triggering_object = triggering_clonk;
1592 else if (triggering_object->~IsClonk())
1593 triggering_clonk = triggering_object;
1594 // Position default
1595 if (!position && triggering_object)
1596 position = [triggering_object->GetX(), triggering_object->GetY()];
1597 // Init context settings
1598 this.action_object = action_object;
1599 this.triggering_object = triggering_object;
1600 this.triggering_clonk = triggering_clonk;
1601 this.triggering_player = triggering_player;
1602 this.position = position;
1603 this.root_action = props;
1604 this.suspended = false;
1605 this.finish_callback = finish_callback;
1606 return true;
1607 }
1608
MenuOK(proplist menu_id,object clonk)1609 public func MenuOK(proplist menu_id, object clonk)
1610 {
1611 // Pressed 'Next' in dialogue: Resume in user action
1612 UserAction->ResumeAction(this, this.hold);
1613 }
1614
MenuSelectOption(int index)1615 public func MenuSelectOption(int index)
1616 {
1617 // Selected an option in dialogue: Resume at specified position in innermost sequence
1618 if (!hold || !hold.Options) return;
1619 var opt = this.hold.Options[index];
1620 if (opt && last_sequence)
1621 {
1622 action_data[last_sequence._sequence_id] = opt._goto;
1623 hold = nil;
1624 }
1625 UserAction->ResumeAction(this, hold);
1626 }
1627
SaveScenarioObject(props)1628 public func SaveScenarioObject(props) { return false; } // temp. don't save.
1629