]> git.lizzy.rs Git - metalua.git/blob - src/tests/reweave/schema.lua
Merge remote branch 'origin/master'
[metalua.git] / src / tests / reweave / schema.lua
1 local print, verb, dbg, errr, print_table, printt = make_module_loggers("schema", "SCM")
2
3 local CT, GMF,
4       game_const
5       = import 'game/const.lua'
6       {
7         'chipTypes',
8         'gameModeFlags'
9       }
10
11 local MTF,
12       cast_type
13       = import (game_const.abilities)
14       {
15         'manualTargetFlags',
16         'castType'
17       }
18
19 local AP, abiprob_mapping = import (game_const.abilities.property)
20       {
21         'mappingInv', -- Note order (inverted goes first)
22         'mapping'
23       }
24
25 local PO, CM, CST, SO,
26       abie_const
27       = import 'abie/const.lua'
28       {
29         'propObjects',
30         'customMessages',
31         'clientStat',
32         'storeObjects'
33       }
34
35 local non_empty_list,
36       no_check,
37       not_implemented,
38       get_children,
39       get_children_concat_newline,
40       get_children_concat_str,
41       get_children_concat_table,
42       get_value,
43       get_value_quoted,
44       get_value_tonumber,
45       check_mapping_tonumber,
46       get_value_mapped_tonumber_quoted,
47       node_children_placeholders_filler,
48       check_tonumber
49       = import 'jsle/schema/util.lua'
50       {
51         'non_empty_list',
52         'no_check',
53         'not_implemented',
54         'get_children',
55         'get_children_concat_newline',
56         'get_children_concat_str',
57         'get_children_concat_table',
58         'get_value',
59         'get_value_quoted',
60         'get_value_tonumber',
61         'check_mapping_tonumber',
62         'get_value_mapped_tonumber_quoted',
63         'node_children_placeholders_filler',
64         'check_tonumber'
65       }
66
67 local declare_common = import 'jsle/schema/common.lua' { 'declare_common' }
68
69 -- Optional TODOs:
70
71 -- TODO: Must be able to fetch back data from lang file to this schema.
72 -- TODO: Write effect validation with human readable answers. Make it available via jobman's job.
73 -- TODO: Write auto-conversion function for old abilities (v.1.01->current)
74 -- TODO: Embed limitations on number of simultanious identical active OT effects
75 -- TODO: Write checkers for numeric fields
76 -- TODO: Adapt game/ctrl.lua to abie
77
78 local define_schema = function(jsle)
79   assert_is_table(jsle)
80
81 -- WARNING: Return nil on error from handlers, do not return false -- it is a legitimate value.
82 -- WARNING: Reordering of schema elements would result in INCOMPATIBLE format change!
83
84   local propwrite_values =
85   {
86     { ["health"] = [[жизнь]] };
87     { ["health_max"] = [[здоровье]] };
88     { ["mana1"] = [[красную ману]] };
89     { ["mana2"] = [[зелёную ману]] };
90     { ["mana3"] = [[синюю ману]] };
91     -- Note mana4 is reserved for health
92     { ["mana5"] = [[ману 5]] };
93     { ["mana6"] = [[ману 6]] };
94     { ["mana7"] = [[ману 7]] };
95     { ["mana8"] = [[ману 8]] };
96     { ["armor"] = [[броню]] };
97     { ["fury"] = [[ярость]] };
98     { ["block"] = [[блок]] };
99     { ["fortune"] = [[удачу]] };
100     { ["stun"] = [[оглушение]] };
101     { ["armour_piercing"] = [[бронебойность]] };
102     { ["agility"] = [[ловкость]] };
103     { ["counterattack"] = [[контрудар]] };
104     { ["damage"] = [[базовый урон]] };
105     { ["damage_min"] = [[минимальный урон]] };
106     { ["damage_max"] = [[максимальный урон]] };
107     { ["damage_mult"] = [[множитель урона]] };
108     { ["vampiric"] = [[вампиризм]] };
109     { ["stun_count"] = [[оглушённость]] };
110   }
111
112   local propread_values = tiappend(
113       tclone(propwrite_values),
114       {
115         { ["race_id"] = [[расу]] },
116         { ["level"] = [[уровень]] },
117         { ["grade"] = [[степень]] }, -- TODO: clan_rank?!
118         { ["rank"] = [[ранг]] },
119         { ["glory"] = [[доблесть]] },
120         { ["scalps"] = [[скальпы]] },
121         { ["kills"] = [[убийства]] },
122       }
123     )
124
125   -- TODO: Be more specific. Should be at least "abie-1.03".
126   jsle:version("1.03") -- WARNING: Do an ordering cleanup when this changes
127
128   jsle:record "ROOT"
129   {
130     children =
131     {
132       [1] = "TARGET_LIST";
133       [2] = "IMMEDIATE_EFFECT_LIST";
134       [3] = "OVERTIME_EFFECT";
135       [4] = { "BOOLEAN", default = 0 }; -- Warning! Do not use BOOLOP_VARIANT, nothing of it would work at this point.
136       [5] = { "CUSTOM_OVERTIME_EFFECTS", default = empty_table };
137     };
138     html = [[<h2>Цели</h2>%C(1)%<h2>Мгновенные эффекты</h2><b>Игнорировать активацию в статистике:</b>%C(4)%<br><br><b>Действия:</b>%C(2)%<h2>Овертайм-эффекты</h2>%C(3)%<hr>%C(5)%]];
139     checker = no_check;
140     handler = function(self, node)
141       return self:effect_from_string(
142         node.value[1], -- Target list
143         node.value[4], -- Ignore usage stats flag
144         self:fill_placeholders(
145             node.value,
146 [[
147 function(self)
148   self:set_custom_ot_effects($(5))
149
150   do
151     $(2)
152   end
153
154   do
155     $(3)
156   end
157 end
158 ]]
159           )
160        )
161     end;
162   }
163
164   jsle:list "TARGET_LIST"
165   {
166     type = "TARGET_VALUE";
167     html = [[%LIST(", ")%]];
168     checker = non_empty_list;
169     handler = function(self, node)
170       local result = 0
171       for i, v in ipairs(node.value) do
172         result = result + v
173       end
174       return result
175     end;
176   }
177
178   jsle:enum "TARGET_VALUE"
179   {
180     values =
181     {
182       { [MTF.AUTO_ONLY]       = [[неинтерактивно]] };
183       { [MTF.SELF_HUMAN]      = [[на себя]] };
184       { [MTF.SELF_TEAM_HUMAN] = [[на человека в своей команде]] };
185       { [MTF.OPP_HUMAN]       = [[на противника]] };
186       { [MTF.OPP_TEAM_HUMAN]  = [[на человека в команде противника]] };
187       { [MTF.FIELD_CHIP]      = [[на фишку]] };
188     };
189     html = [[%VALUE()%]];
190     checker = no_check;
191     handler = get_value_tonumber;
192     numeric_keys = true;
193   }
194
195   jsle:list "IMMEDIATE_EFFECT_LIST"
196   {
197     type = "ACTION_VARIANT";
198     html = [[%LE("<i>Нет</i>")%%LNE("<ol><li>")%%LIST("<li>")%%LNE("</ol>")%]];
199     checker = no_check;
200     handler = get_children_concat_newline;
201   }
202
203   jsle:record "OVERTIME_EFFECT"
204   {
205     children =
206     {
207       [1] = "OT_EFFECT_TARGET";
208       [2] = "NUMOP_VARIANT";
209       [3] = "NUMOP_VARIANT";
210       [4] = "BOOLOP_VARIANT";
211       [5] = "OVERTIME_EFFECT_LIST";
212       [6] = "OVERTIME_EFFECT_LIST";
213       [7] = "OVERTIME_EFFECT_LIST";
214       [8] = "OT_MODIFIER_LIST";
215       [9] = "NUMOP_VARIANT"; -- TODO: Must be higher in the list. Straighten numbers on next version change (do not forget to fix texts)
216       [10] = "NUMOP_VARIANT"; -- TODO: Must be higher in the list. Straighten numbers on next version change (do not forget to fix texts)
217       [11] = { "GAME_MODES", default = GMF.ALL }; -- TODO: Must be higher in the list. Straighten numbers on next version change (do not forget to fix texts)
218       [12] = { "BOOLEAN", default = 0 };
219     };
220     html = [[<br><b>Цель:</b> %C(1)%<br><b>Время жизни:</b> %C(2)% <i>(&ge;255 &mdash; бессрочно)</i><br><b>Период:</b> %C(3)%<br><b>Изначальный кулдаун:</b> %C(10)%<br><b>Сброс в конце боя:</b> %C(4)%<br><b>Остается при снятии всех эффектов вручную:</b> %C(12)%<br><b>Максимальное число одновременно активных эффектов:</b> %C(9)% <i>(0 &mdash; не ограничено)</i><br><b>Игровые режимы:</b> %C(11)%<h3>При изменении набора характеристик</h3>%C(5)%<h3>В конце хода цели</h3>%C(7)%<h3>Временные модификаторы <i>(кроме жизни)</i></h3>%C(8)%]];
221     checker = no_check;
222     handler = function(self, node)
223       if
224         node.value[5] ~= "" or
225         node.value[6] ~= "" or
226         node.value[7] ~= "" or
227         node.value[8] ~= "{}"
228       then
229         -- Spawning OT effect only if have any actions in it.
230         return node_children_placeholders_filler
231           [[
232             self:spawn_overtime_effect(
233                 $(1),
234                 $(2),
235                 $(3),
236                 $(10),
237                 $(4),
238                 $(9),
239                 function(self)
240                   $(5)
241                 end,
242                 function(self)
243                   $(6)
244                 end,
245                 function(self)
246                   $(7)
247                 end,
248                 $(8),
249                 $(11),
250                 $(12)
251               )
252           ]] (self, node)
253       else
254         return [[-- No OT effects]]
255       end
256    end;
257   }
258
259   jsle:list "OT_MODIFIER_LIST"
260   {
261     type = "OT_MODIFIER_VARIANT";
262     html = [[%LE("<i>Нет</i>")%%LNE("<ol><li>")%%LIST("<li>")%%LNE("</ol>")%]];
263     checker = no_check;
264     handler = get_children_concat_table;
265   }
266
267   jsle:variant "OT_MODIFIER_VARIANT"
268   {
269     values =
270     {
271       { ["MOD_SET"]  = [[Установить]] };
272       { ["MOD_INC"]  = [[Увеличить]] };
273       { ["MOD_DEC"]  = [[Уменьшить]] };
274       { ["MOD_MULT"] = [[Умножить]] };
275     };
276     label = [["<i title=\"Модификатор\">M</i>"]];
277     html = [[%VALUE()%]];
278     checker = no_check;
279     handler = get_value;
280   }
281
282   jsle:record "MOD_SET"
283   {
284     children =
285     {
286       [1] = "PROPWRITE";
287       [2] = "NUMOP_VARIANT";
288     };
289     html = [[Установить %C(1)% цели в %C(2)%]];
290     checker = no_check;
291     handler = node_children_placeholders_filler [[{ name = $(1), fn = function(self, value) return ($(2)) end; }]];
292   }
293
294   jsle:record "MOD_INC"
295   {
296     children =
297     {
298       [1] = "PROPWRITE";
299       [2] = "NUMOP_VARIANT";
300     };
301     html = [[Увеличить %C(1)% цели на %C(2)%]];
302     checker = no_check;
303     handler = node_children_placeholders_filler [[{ name = $(1), fn = function(self, value) return value + ($(2)) end; }]];
304   }
305
306   jsle:record "MOD_DEC"
307   {
308     children =
309     {
310       [1] = "PROPWRITE";
311       [2] = "NUMOP_VARIANT";
312     };
313     html = [[Уменьшить %C(1)% цели на %C(2)%]];
314     checker = no_check;
315     handler = node_children_placeholders_filler [[{ name = $(1), fn = function(self, value) return value - ($(2)) end; }]];
316   }
317
318   jsle:record "MOD_MULT"
319   {
320     children =
321     {
322       [1] = "PROPWRITE";
323       [2] = "NUMOP_VARIANT";
324     };
325     html = [[Умножить %C(1)% цели на %C(2)%]];
326     checker = no_check;
327     handler = node_children_placeholders_filler [[{ name = $(1), fn = function(self, value) return value * ($(2)) end; }]];
328   }
329
330   jsle:list "OVERTIME_EFFECT_LIST"
331   {
332     type = "ACTION_VARIANT";
333     html = [[%LE("<i>Нет</i>")%%LNE("<ol><li>")%%LIST("<li>")%%LNE("</ol>")%]];
334     checker = no_check;
335     handler = get_children_concat_newline;
336   }
337
338   jsle:list "ACTION_LIST"
339   {
340     type = "ACTION_VARIANT";
341     html = [[<ol><li>%LIST("<li>")%</ol>]];
342     checker = non_empty_list;
343     handler = get_children_concat_newline;
344   }
345
346   jsle:variant "ACTION_VARIANT"
347   {
348     values =
349     {
350       { ["ACT_SET"]               = [[Установить]] };
351       { ["ACT_INC"]               = [[Увеличить]] };
352       { ["ACT_DEC"]               = [[Уменьшить]] };
353       { ["ACT_MULT"]              = [[Умножить]] };
354       { ["ACT_DIRECTSET"]         = [[Установить напрямую]] };
355       { ["ACT_DIRECTINC"]         = [[Увеличить напрямую]] };
356       { ["ACT_DIRECTDEC"]         = [[Уменьшить напрямую]] };
357       { ["ACT_DIRECTMULT"]        = [[Умножить напрямую]] };
358       { ["ACT_FLDEXPLODE"]        = [[Взорвать фишки]] };
359       { ["ACT_FLDLEVELDELTA"]     = [[Поднять уровень фишек]] };
360       { ["ACT_FLDCOLLECT_COORDS"] = [[Собрать фишки по координатам]] };
361       { ["ACT_FLDREPLACE_COORDS"] = [[Заменить фишки по координатам]] };
362       { ["ACT_ONEMOREACTION"]     = [[Дать ещё одно действие]] };
363       { ["ACT_KEEPTIMEOUT"]       = [[Не сбрасывать таймер]] };
364       { ["ACT_SETVAR"]            = [[Запомнить]] };
365       { ["ACT_SETOBJVAR_LOCAL"]   = [[Запомнить в объекте локально]] };
366       { ["ACT_SETOBJVAR_GLOBAL"]  = [[Запомнить в объекте глобально]] };
367       { ["ACT_SETOBJVAR_OT"]      = [[Запомнить в текущем овертайме]] };
368       { ["ACT_DOIF"]              = [[Если]] };
369       { ["ACT_DOIFELSE"]          = [[Если ... иначе]] };
370       { ["ACT_PLAYABIANIM"]       = [[Играть эффект абилки]] };
371       { ["ACT_SENDCUSTOMMSG"]     = [[Отправить данные клиентам]] };
372       { ["ACT_INCSTAT"]           = [[Увеличить статистику клиента]] };
373       { ["ACT_ACTIVATEOT"]        = [[Активировать ОТ-эффект]] };
374       { ["ACT_REMOVE_OVERTIMES"]  = [[Снять ОТ-эффекты]] };
375       -- Keep these below --
376       { ["ACT_FLDREPLACE"]        = [[Заменить фишки <b><i>(устарело)</i></b>]] };
377       { ["ACT_CRASH_GAME"]        = [[УРОНИТЬ игру <b><i>(только для тестов)</i></b>]] };
378       -- { ["PLAINLUA"]           = [[Lua]] };
379     };
380     label = [["<i title=\"Действие\">A</i>"]];
381     html = [[%VALUE()%]];
382     checker = no_check;
383     handler = get_value;
384   }
385
386   declare_common(jsle, "ACT_DOIF", "ACT_DOIFELSE")
387
388   jsle:record "ACT_SET"
389   {
390     children =
391     {
392       [1] = "PROPPATH_WRITE";
393       [2] = "NUMOP_VARIANT";
394     };
395     html = [[Установить %C(1)% в %C(2)%]];
396     checker = no_check;
397     handler = node_children_placeholders_filler [[self:propset($(1), $(2))]];
398   }
399
400   jsle:record "ACT_INC"
401   {
402     children =
403     {
404       [1] = "PROPPATH_WRITE";
405       [2] = "NUMOP_VARIANT";
406     };
407     html = [[Увеличить %C(1)% на %C(2)%]];
408     checker = no_check;
409     handler = node_children_placeholders_filler [[self:propinc($(1), $(2))]];
410   }
411
412   jsle:record "ACT_DEC"
413   {
414     children =
415     {
416       [1] = "PROPPATH_WRITE";
417       [2] = "NUMOP_VARIANT";
418     };
419     html = [[Уменьшить %C(1)% на %C(2)%]];
420     checker = no_check;
421     handler = node_children_placeholders_filler [[self:propdec($(1), $(2))]];
422   }
423
424   jsle:record "ACT_MULT"
425   {
426     children =
427     {
428       [1] = "PROPPATH_WRITE";
429       [2] = "NUMOP_VARIANT";
430     };
431     html = [[Умножить %C(1)% на %C(2)%]];
432     checker = no_check;
433     handler = node_children_placeholders_filler [[self:propmult($(1), $(2))]];
434   }
435
436   jsle:record "ACT_DIRECTSET"
437   {
438     children =
439     {
440       [1] = "PROPPATH_WRITE";
441       [2] = "NUMOP_VARIANT";
442     };
443     html = [[Установить напрямую %C(1)% в %C(2)%]];
444     checker = no_check;
445     handler = node_children_placeholders_filler [[self:propset_direct($(1), $(2))]];
446   }
447
448   jsle:record "ACT_DIRECTINC"
449   {
450     children =
451     {
452       [1] = "PROPPATH_WRITE";
453       [2] = "NUMOP_VARIANT";
454     };
455     html = [[Увеличить напрямую %C(1)% на %C(2)%]];
456     checker = no_check;
457     handler = node_children_placeholders_filler [[self:propinc_direct($(1), $(2))]];
458   }
459
460   jsle:record "ACT_DIRECTDEC"
461   {
462     children =
463     {
464       [1] = "PROPPATH_WRITE";
465       [2] = "NUMOP_VARIANT";
466     };
467     html = [[Уменьшить напрямую %C(1)% на %C(2)%]];
468     checker = no_check;
469     handler = node_children_placeholders_filler [[self:propdec_direct($(1), $(2))]];
470   }
471
472   jsle:record "ACT_DIRECTMULT"
473   {
474     children =
475     {
476       [1] = "PROPPATH_WRITE";
477       [2] = "NUMOP_VARIANT";
478     };
479     html = [[Умножить напрямую %C(1)% на %C(2)%]];
480     checker = no_check;
481     handler = node_children_placeholders_filler [[self:propmult_direct($(1), $(2))]];
482   }
483
484   jsle:record "ACT_FLDEXPLODE"
485   {
486     children =
487     {
488       [1] = "NUMOP_VARIANT";
489       [2] = "CHIPCOORD";
490     };
491     html = [[Взорвать бомбу радиусом %C(1)% в координатах %C(2)%]];
492     checker = no_check;
493     handler = node_children_placeholders_filler [[self:fld_explode($(1), $(2))]];
494   }
495
496   jsle:record "ACT_FLDREPLACE"
497   {
498     children =
499     {
500       [1] = "CHIPTYPE";
501       [2] = "NUMOP_VARIANT";
502       [3] = "CHIPTYPE";
503       [4] = "NUMOP_VARIANT";
504     };
505     html = [[Заменить %C(1)% уровня %C(2)% на %C(3)% уровня %C(4)%]];
506     checker = no_check;
507     handler = node_children_placeholders_filler [[self:fld_replace($(1), $(2), $(3), $(4))]];
508     doc = [[Deprecated, use other replace actions]];
509   }
510
511   jsle:record "ACT_FLDLEVELDELTA"
512   {
513     children =
514     {
515       [1] = "NUMOP_VARIANT";
516       [2] = "CHIPTYPE";
517       [3] = "NUMOP_VARIANT";
518       [4] = "NUMOP_VARIANT";
519     };
520     html = [[Поднять уровень %C(2)% на %C(1)% в диапазоне от %C(3)% до %C(4)%]];
521     checker = no_check;
522     handler = node_children_placeholders_filler [[self:fld_level_delta($(1), $(2), $(3), $(4))]];
523   }
524
525   jsle:record "ACT_FLDCOLLECT_COORDS"
526   {
527     children =
528     {
529       [1] = "COORDLISTOP_VARIANT";
530     };
531     html = [[Собрать %C(1)%]];
532     checker = no_check;
533     handler = node_children_placeholders_filler [[self:fld_collect_coords($(1))]];
534   }
535
536   jsle:record "ACT_FLDREPLACE_COORDS"
537   {
538     children =
539     {
540       [1] = "COORDLISTOP_VARIANT";
541       [2] = "CHIPTYPE_LIST";
542       [3] = "NUMOP_VARIANT";
543     };
544     html = [[Заменить %C(1)% на %C(2)% уровня %C(3)%]];
545     checker = no_check;
546     handler = node_children_placeholders_filler [[self:fld_replace_coords($(1),$(2),$(3))]];
547   }
548
549   jsle:literal "ACT_ONEMOREACTION"
550   {
551     html = [[Дать ещё одно действие <i>(только мгновенный эффект)</i>]];
552     checker = no_check;
553     handler = invariant [[self:one_more_action()]];
554   }
555
556   jsle:literal "ACT_KEEPTIMEOUT"
557   {
558     html = [[Не сбрасывать таймер <i>(только мгновенный эффект)</i>]];
559     checker = no_check;
560     handler = invariant [[self:keep_timeout()]];
561   }
562
563   jsle:record "ACT_SETVAR"
564   {
565     children =
566     {
567       [1] = "NUMOP_VARIANT";
568       [2] = "NUMOP_VARIANT";
569     };
570     html = [[Запомнить в №%C(1)% значение %C(2)%]];
571     checker = no_check;
572     handler = node_children_placeholders_filler [[self:setvar($(1), $(2))]];
573   }
574
575   jsle:enum "OT_EFFECT_TARGET"
576   {
577     values =
578     {
579       { [PO.SELF] = [[на себя]] };
580       { [PO.OPP] = [[на противника]] };
581       { [PO.TARGET] = [[на цель]] };
582     };
583     html = [[%VALUE()%]];
584     checker = no_check;
585     handler = get_value_quoted;
586   }
587
588   jsle:variant "BOOLOP_VARIANT"
589   {
590     values =
591     {
592       { ["BOOLEAN"] = [[Логическое значение]] };
593       { ["BOOLOP_LT"] = [[&lt;]] };
594       { ["BOOLOP_LTE"] = [[&le;]] };
595       { ["BOOLOP_GT"] = [[&gt;]] };
596       { ["BOOLOP_GTE"] = [[&ge;]] };
597       { ["BOOLOP_EQ"] = [[==]] };
598       { ["BOOLOP_NEQ"] = [[!=]] };
599       { ["BOOLOP_AND_MANY"] = [[И (Список)]] };
600       { ["BOOLOP_OR_MANY"] = [[ИЛИ (Список)]] };
601       { ["BOOLOP_NOT"] = [[НЕ]] };
602       { ["BOOLOP_HAVEMEDAL"] = [[МЕДАЛЬ]] };
603       { ["BOOLOP_ISACTIVE"] = [[Изменения инициированы целью овертайм-эффекта]] };
604       { ["BOOLOP_IS_GAME_IN_MODE"] = [[Текущий игровой режим]] };
605       -- Deprecated, keep below --
606       { ["BOOLOP_AND"] = [[И]] };
607       { ["BOOLOP_OR"] = [[ИЛИ]] };
608       --{ ["PLAINLUA"] = [[Lua]] };
609     };
610     label = [["<i title=\"Логическая операция\">B</i>"]];
611     html = [[%VALUE()%]];
612     checker = no_check;
613     handler = get_value;
614   }
615
616   jsle:record "BOOLOP_HAVEMEDAL"
617   {
618     children =
619     {
620       [1] = "PROPOBJECT";
621       [2] = "NUMOP_VARIANT";
622     };
623     html = [[есть медаль №%C(2)% %C(1)%]];
624     checker = no_check;
625     handler = node_children_placeholders_filler [[self:have_medal($(1), $(2))]];
626   }
627
628   jsle:literal "BOOLOP_ISACTIVE"
629   {
630     html = [[изменения инициированы целью овертайм-эффекта]];
631     checker = no_check; -- Only for on_changeset event.
632     handler = invariant [[self:is_overtime_target_active()]];
633   }
634
635   declare_common(
636       jsle,
637       "BOOLOP_LT",
638       "BOOLOP_LTE",
639       "BOOLOP_GT",
640       "BOOLOP_GTE",
641       "BOOLOP_EQ",
642       "BOOLOP_NEQ",
643       "BOOLOP_AND",
644       "BOOLOP_OR",
645       "BOOLOP_NOT"
646     )
647
648   jsle:variant "NUMOP_VARIANT"
649   {
650     values =
651     {
652       { ["NUMBER"] = [[Число]] };
653       { ["NUMOP_ADD_MANY"] = [[+ (Список)]] };
654       { ["NUMOP_DEC_MANY"] = [[- (Список)]] };
655       { ["NUMOP_MUL_MANY"] = [[* (Список)]] };
656       { ["NUMOP_DIV_MANY"] = [[/ (Список)]] };
657       { ["NUMOP_POV"] = [[POW]] }; -- TODO: POW, not POV! Fix by search and replace
658       { ["NUMOP_MOD"] = [[MOD]] };
659       { ["NUMOP_MIN"] = [[MIN]] };
660       { ["NUMOP_MAX"] = [[MAX]] };
661       { ["NUMOP_UNM"] = [[Знак]] };
662       { ["NUMOP_GET"] = [[Характеристика]] };
663       { ["NUMOP_GET_RAW"] = [[Базовое значение характеристики]] };
664       { ["NUMOP_GET_ABIPROP"] = [[Характеристика абилки]] };
665       { ["NUMOP_PERCENT_ROLL"] = [[Cлучайный процент]] };
666       { ["NUMOP_TEAMSIZE"] = [[Размер команды]] };
667       { ["NUMOP_GETVAR"] = [[Вспомнить]] };
668       { ["NUMOP_GETOBJVAR_LOCAL"]  = [[Вспомнить из объекта локально]] };
669       { ["NUMOP_GETOBJVAR_GLOBAL"] = [[Вспомнить из объекта глобально]] };
670       { ["NUMOP_GETOBJVAR_OT"]     = [[Вспомнить из текущего овертайма]] };
671       { ["NUMOP_OTLIFETIMELEFT"] = [[Оставшееся время жизни]] };
672       { ["NUMOP_OTLIFETIMETOTAL"] = [[Общее время жизни]] };
673       { ["NUMOP_FLDGETQUANTITYOFCHIPS"] = [[Число фишек по цвету и уровню]] };
674       { ["NUMOP_TARGETX"] = [[Координата X выбранной фишки]] };
675       { ["NUMOP_TARGETY"] = [[Координата Y выбранной фишки]] };
676       { ["NUMOP_OTEFFECTCOUNT"] = [[Число активных овертайм-эффектов]] };
677       { ["NUMOP_IFF"] = [[Если]] };
678       { ["NUMOP_GETUID"] = [[Идентификатор игрока]] };
679       -- Keep these below --
680       { ["NUMOP_FLDCOUNTCHIPS"] = [[Число фишек на поле <b><i>(устарело)</i></b>]] };
681       { ["NUMOP_ADD"] = [[+]] };
682       { ["NUMOP_DEC"] = [[-]] };
683       { ["NUMOP_MUL"] = [[*]] };
684       { ["NUMOP_DIV"] = [[/]] };
685       { ["NUMOP_CRASH_GAME"] = [[УРОНИТЬ игру <b><i>(только для тестов)</i></b>]] };
686       --{ ["PLAINLUA"] = [[Lua]] };
687     };
688     label = [["<i title=\"Численная операция\">I</i>"]];
689     html = [[%VALUE()%]];
690     checker = no_check;
691     handler = get_value;
692   }
693
694   declare_common(
695       jsle,
696       "NUMOP_ADD",
697       "NUMOP_DEC",
698       "NUMOP_MUL",
699       "NUMOP_DIV",
700       "NUMOP_POV",
701       "NUMOP_MOD",
702       "NUMOP_MIN",
703       "NUMOP_MAX",
704       "NUMOP_UNM"
705     )
706
707   jsle:record "NUMOP_GET"
708   {
709     children =
710     {
711       [1] = "PROPPATH_READ";
712     };
713     html = [[%C(1)%]];
714     checker = no_check;
715     handler = node_children_placeholders_filler [[self:propget($(1), false)]];
716   }
717
718   declare_common(jsle, "NUMOP_PERCENT_ROLL")
719
720   jsle:record "NUMOP_FLDCOUNTCHIPS"
721   {
722     children =
723     {
724       [1] = "CHIPTYPE";
725       [2] = "BOOLOP_VARIANT";
726     };
727     html = [[число %C(1)% на поле (учитывая уровни: %C(2)%)]];
728     checker = no_check;
729     handler = node_children_placeholders_filler [[self:fld_count_chips($(1), $(2))]];
730     doc = [[Deprecated, use other chip count operations]];
731   }
732
733   jsle:record "NUMOP_TEAMSIZE"
734   {
735     children =
736     {
737       [1] = "PROPOBJECT";
738     };
739     html = [[размер команды %C(1)%]];
740     checker = no_check;
741     handler = node_children_placeholders_filler [[self:team_size($(1))]];
742   }
743
744   jsle:record "NUMOP_GETVAR"
745   {
746     children =
747     {
748       [1] = "NUMOP_VARIANT";
749     };
750     html = [[вспомнить из №%C(1)%]];
751     checker = no_check;
752     handler = node_children_placeholders_filler [[self:getvar($(1))]];
753   }
754
755   jsle:literal "NUMOP_OTLIFETIMELEFT"
756   {
757     html = [[оставшееся время жизни]];
758     checker = no_check;
759     handler = invariant [[self:ot_lifetime_left()]];
760   }
761
762   jsle:literal "NUMOP_OTLIFETIMETOTAL"
763   {
764     html = [[общее время жизни]];
765     checker = no_check;
766     handler = invariant [[self:ot_lifetime_total()]];
767   }
768
769   jsle:literal "NUMOP_TARGETX"
770   {
771     html = [[X выбранной фишки]];
772     checker = no_check;
773     handler = invariant [[self:target_x()]];
774   }
775
776   jsle:literal "NUMOP_TARGETY"
777   {
778     html = [[Y выбранной фишки]];
779     checker = no_check;
780     handler = invariant [[self:target_y()]];
781   }
782
783   jsle:record "PROPPATH_WRITE"
784   {
785     children =
786     {
787       [1] = "PROPOBJECT";
788       [2] = "PROPWRITE";
789     };
790     html = [[%C(2)% %C(1)%]];
791     checker = no_check;
792     handler = node_children_placeholders_filler [[self:make_proppath($(1), $(2))]];
793   }
794
795   jsle:record "PROPPATH_READ"
796   {
797     children =
798     {
799       [1] = "PROPOBJECT";
800       [2] = "PROPREAD";
801     };
802     html = [[%C(2)% %C(1)%]];
803     checker = no_check;
804     handler = node_children_placeholders_filler [[self:make_proppath($(1), $(2))]];
805   }
806
807   jsle:enum "PROPOBJECT"
808   {
809     values =
810     {
811       { [PO.SELF] = [[у себя]] };
812       { [PO.OPP] = [[у противника]] };
813       { [PO.TARGET] = [[у цели]] };
814       { [PO.OWN_CHANGESET] = [[в своём наборе изменений]] };
815       { [PO.OPP_CHANGESET] = [[в наборе изменений противника]] };
816     };
817     html = [[%VALUE()%]];
818     checker = no_check; -- Check value is valid for current action list subtype
819     handler = get_value_quoted;
820   }
821
822   jsle:enum "PROPWRITE"
823   {
824     values = propwrite_values;
825     html = [[%VALUE()%]];
826     checker = no_check;
827     handler = get_value_quoted;
828   }
829
830   jsle:enum "PROPREAD"
831   {
832     values = propread_values;
833     html = [[%VALUE()%]];
834     checker = no_check;
835     handler = get_value_quoted;
836   }
837
838   jsle:enum "CHIPTYPE"
839   {
840     values =
841     {
842       { [CT.EMERALD] = [[зелёных фишек]] };
843       { [CT.RUBY] = [[красных фишек]] };
844       { [CT.AQUA] = [[синих фишек]] };
845       { [CT.DMG] = [[черепов]] };
846       { [CT.CHIP5] = [[фишек-5]] };
847       { [CT.CHIP6] = [[фишек-6]] };
848       { [CT.CHIP7] = [[фишек-7]] };
849       { [CT.CHIP8] = [[фишек-8]] };
850       { [CT.EMPTY] = [[пустых фишек]] };
851     };
852     html = [[%VALUE()%]];
853     checker = no_check;
854     handler = get_value_tonumber;
855     numeric_keys = true;
856   }
857
858   jsle:edit "NUMBER"
859   {
860     size = 4;
861     numeric = true;
862     checker = check_tonumber;
863     handler = get_value_tonumber;
864   }
865
866   declare_common(
867       jsle,
868       "BOOLEAN",
869       "PLAINLUA"
870     )
871
872   jsle:list "COORDLISTOP_STD"
873   {
874     type = "CHIPCOORD";
875     html = [[фишки с координатами %LIST(", ")%]];
876     checker = non_empty_list;
877     handler = get_children_concat_table;
878   }
879
880   jsle:record "CHIPCOORD"
881   {
882     children =
883     {
884       [1] = "NUMOP_VARIANT";
885       [2] = "NUMOP_VARIANT";
886     };
887     html = [[(x: %C(1)%, y: %C(2)%)]];
888     checker = no_check;
889     handler = node_children_placeholders_filler [[{x=$(1), y=$(2)}]];
890   }
891
892   -- TODO: UNUSED. Remove or use.
893   jsle:record "BOOLOP_SELECTEDTARGET"
894   {
895     children =
896     {
897       [1] = "TARGET_VALUE";
898     };
899     html = [[выбрана цель %C(1)%]];
900     checker = no_check;
901     handler = node_children_placeholders_filler [[self:is_target_selected($(1))]];
902     doc = [[Currently not used]];
903   }
904
905   jsle:record "NUMOP_OTEFFECTCOUNT"
906   {
907     children =
908     {
909       [1] = "PROPOBJECT";
910       [2] = "NUMOP_VARIANT";
911       [3] = "NUMOP_VARIANT";
912     };
913     html = [[число овертайм-эффектов абилки ID %C(2)% <i>(0 &mdash; этот эффект)</i> № эффекта %C(3)% <i>(0 &mdash; по умолчанию)</i>, активных %C(1)%]];
914     checker = no_check;
915     handler = node_children_placeholders_filler [[self:active_ot_effect_count($(1), $(2), $(3))]];
916   }
917
918   declare_common(jsle, "NUMOP_IFF")
919
920   jsle:record "NUMOP_GET_RAW"
921   {
922     children =
923     {
924       [1] = "PROPPATH_READ";
925     };
926     html = [[базовое значение %C(1)%]];
927     checker = no_check;
928     handler = node_children_placeholders_filler [[self:propget($(1), true)]];
929   }
930
931   -- TODO: Get rid of non-list versions!
932
933   declare_common(
934       jsle,
935       "NUMOP_ADD_MANY",
936       "NUMOP_DEC_MANY",
937       "NUMOP_MUL_MANY",
938       "NUMOP_DIV_MANY"
939     )
940
941   declare_common(
942       jsle,
943       "BOOLOP_AND_MANY",
944       "BOOLOP_OR_MANY"
945     )
946
947   jsle:list "CHIPTYPE_LIST"
948   {
949     type = "CHIPTYPE";
950     html = [[%LIST(", ")%]];
951     checker = non_empty_list;
952     handler = get_children_concat_table;
953   }
954
955   jsle:record "NUMOP_GET_ABIPROP"
956   {
957     children =
958     {
959       [1] = "ABIPROP_NAME";
960     };
961     html = [[%C(1)% абилки]];
962     checker = no_check;
963     handler = node_children_placeholders_filler [[self:abipropget($(1))]];
964   }
965
966   jsle:enum "ABIPROP_NAME"
967   {
968     values =
969     {
970       { [AP.prob] = [[вероятность активации]] };
971     };
972     html = [[%VALUE()%]];
973     checker = check_mapping_tonumber;
974     handler = get_value_mapped_tonumber_quoted(abiprob_mapping);
975   }
976
977   jsle:record "ACT_SENDCUSTOMMSG"
978   {
979     children =
980     {
981       [1] = "NUMOP_LIST";
982     };
983     html = [[Отправить участникам боя данные: %C(1)%]];
984     checker = no_check;
985     handler = node_children_placeholders_filler [[self:send_custom_msg($(1))]];
986   }
987
988   declare_common(jsle, "NUMOP_LIST")
989
990   jsle:record "ACT_PLAYABIANIM"
991   {
992     children =
993     {
994       [1] = "NUMOP_VARIANT";
995     };
996     html = [[Играть эффект абилки ID: %C(1)%]];
997     checker = no_check;
998     -- Hack. Should format be hardcoded here or below?
999     handler = node_children_placeholders_filler(
1000         [[self:send_custom_msg({]]..assert_is_number(CM.PLAYABIANIM)
1001         ..[[, $(1), self:get_uid("]]..PO.SELF..[[")})]]
1002       );
1003   }
1004
1005   jsle:variant "COORDLISTOP_VARIANT"
1006   {
1007     values =
1008     {
1009       { ["COORDLISTOP_STD"] = [[Обычный список коордтнат]] };
1010       { ["COORDLISTOP_GETLEVEL"] = [[Фишки цвета <i>цв1</i> с уровнями от <i>ур1</i> до <i>ур2</i>]] };
1011     };
1012     label = [["<i title=\"Список координат\">C</i>"]];
1013     html = [[%VALUE()%]];
1014     checker = no_check;
1015     handler = get_value;
1016   }
1017
1018   jsle:record "COORDLISTOP_GETLEVEL"
1019   {
1020     children =
1021     {
1022       [1] = "CHIPTYPE";
1023       [2] = "NUMOP_VARIANT";
1024       [3] = "NUMOP_VARIANT";
1025     };
1026     html = [[%C(1)% с уровнями от %C(2)% до %C(3)%]];
1027     checker = no_check;
1028     handler = node_children_placeholders_filler [[self:fld_get_coordlist_from_levels_and_type($(1), $(2), $(3))]];
1029   }
1030
1031   jsle:record "NUMOP_FLDGETQUANTITYOFCHIPS"
1032   {
1033     children =
1034     {
1035       [1] = "CHIPTYPE";
1036       [2] = "NUMOP_VARIANT";
1037       [3] = "NUMOP_VARIANT";
1038       [4] = "BOOLOP_VARIANT";
1039     };
1040     html = [[число %C(1)% на поле уровней с %C(2)% до %C(3)% (учитывая уровень в счетчике: %C(4)%)]];
1041     checker = no_check;
1042     handler = node_children_placeholders_filler [[self:fld_get_quantity_of_chips($(1), $(2), $(3), $(4))]];
1043   }
1044
1045   jsle:enum "CLIENTSTAT"
1046   {
1047     values =
1048     {
1049       -- TODO: Support commented out variants?
1050       { [CST.SPELL_USE]        = [[исп. спеллов]] };
1051       --{ [CST.SPELL_FRAG]       = [[фраги от спеллов]] };
1052       { [CST.CONSUMABLE_USE]   = [[исп. расходников]] };
1053       --{ [CST.CONSUMABLE_FRAG]  = [[фраги от расходников]] };
1054       { [CST.AUTOABILITY_USE]  = [[исп. автоабилок]] };
1055       --{ [CST.AUTOABILITY_FRAG] = [[фраги от автоабилок]] };
1056       --{ [CST.RATING]           = [[рейтинг]] };
1057       --{ [CST.CUSTOM]           = [[пользовательская]] };
1058     };
1059     html = [[%VALUE()%]];
1060     checker = check_mapping_tonumber;
1061     handler = get_value_tonumber;
1062   }
1063
1064   jsle:record "ACT_INCSTAT"
1065   {
1066     children =
1067     {
1068       [1] = "PROPOBJECT";
1069       [2] = "CLIENTSTAT";
1070       [3] = "NUMOP_VARIANT";
1071       [4] = "NUMOP_VARIANT";
1072     };
1073     html = [[Увеличить %C(1)% статистику &laquo;%C(2)%&raquo; эффекта №%C(3)% <i>(0 &mdash; текущий)</i> на %C(4)%]];
1074     checker = no_check;
1075     handler = node_children_placeholders_filler [[self:inc_client_stat($(1), $(2), $(3), $(4))]];
1076   }
1077
1078   jsle:record "ACT_ACTIVATEOT"
1079   {
1080     children =
1081     {
1082       [1] = "NUMOP_VARIANT";
1083       [2] = { "KEYVALUE_LIST", default = empty_table };
1084     };
1085     html = [[Активировать ОТ-эффект №%C(1)%, передав %C(2)%]];
1086     checker = no_check;
1087     handler = node_children_placeholders_filler [[self:activate_custom_ot_effect($(1),$(2))]];
1088   }
1089
1090   jsle:list "CUSTOM_OVERTIME_EFFECTS"
1091   {
1092     type = "OVERTIME_EFFECT";
1093     html = [[%LE("<i>(Нет дополнительных ОТ-эффектов)</i>")%%LNE("<ol><li><h2>Дополнительный OT-эффект</h2>")%%LIST("<hr><li><h2>Дополнительный OT-эффект</h2>")%%LNE("</ol>")%]];
1094     checker = no_check;
1095     handler = function(self, node)
1096       local buf = {[[{]]}
1097       local _ = function(v) buf[#buf + 1] = tostring(v) end
1098       for i, child in ipairs(node.value) do
1099         _ [[
1100 []] _(i) _[[] = function(self)
1101 ]] _(child) _ [[
1102 end;
1103 ]]
1104       end
1105       _ [[}]]
1106       return table.concat(buf)
1107     end;
1108   }
1109
1110   jsle:record "NUMOP_GETUID"
1111   {
1112     children =
1113     {
1114       [1] = "PROPOBJECT";
1115     };
1116     html = [[идентификатор игрока %C(1)%]];
1117     checker = no_check;
1118     handler = node_children_placeholders_filler [[self:get_uid($(1))]];
1119   }
1120
1121   jsle:enum "STORE_OBJ"
1122   {
1123     values =
1124     {
1125       { [SO.CLIENT_SELF]   = [[на себе]] };
1126       { [SO.CLIENT_OPP]    = [[на противнике]] };
1127       { [SO.CLIENT_TARGET] = [[на цели]] };
1128       { [SO.FIGHT]         = [[на бою]] };
1129       { [SO.GAME]          = [[на игре]] };
1130     };
1131     html = [[%VALUE()%]];
1132     checker = no_check;
1133     handler = get_value_tonumber;
1134   }
1135
1136   jsle:record "ACT_SETOBJVAR_LOCAL"
1137   {
1138     children =
1139     {
1140       [1] = "STORE_OBJ";
1141       [2] = "NUMOP_VARIANT";
1142       [3] = "NUMOP_VARIANT";
1143     };
1144     html = [[Запомнить в объекте &laquo;%C(1)%&raquo; в слот №%C(2)% <b>приватное</b> значение %C(3)%]];
1145     checker = no_check;
1146     handler = node_children_placeholders_filler [[self:setobjvar_local($(1), $(2), $(3))]];
1147   }
1148
1149   jsle:record "NUMOP_GETOBJVAR_LOCAL"
1150   {
1151     children =
1152     {
1153       [1] = "STORE_OBJ";
1154       [2] = "NUMOP_VARIANT";
1155     };
1156     html = [[вспомнить из объекта &laquo;%C(1)%&raquo; из слота №%C(2)% <b>приватное</b> значение]];
1157     checker = no_check;
1158     handler = node_children_placeholders_filler [[self:getobjvar_local($(1), $(2))]];
1159   }
1160
1161   jsle:record "ACT_SETOBJVAR_GLOBAL"
1162   {
1163     children =
1164     {
1165       [1] = "STORE_OBJ";
1166       [2] = "NUMOP_VARIANT";
1167       [3] = "NUMOP_VARIANT";
1168     };
1169     html = [[Запомнить в объекте %C(1)% в слот №%C(2)% <b>публичное</b> значение %C(3)%]];
1170     checker = no_check;
1171     handler = node_children_placeholders_filler [[self:setobjvar_global($(1), $(2), $(3))]];
1172   }
1173
1174   jsle:record "NUMOP_GETOBJVAR_GLOBAL"
1175   {
1176     children =
1177     {
1178       [1] = "STORE_OBJ";
1179       [2] = "NUMOP_VARIANT";
1180     };
1181     html = [[вспомнить из объекта %C(1)% из слота №%C(2)% <b>публичное</b> значение]];
1182     checker = no_check;
1183     handler = node_children_placeholders_filler [[self:getobjvar_global($(1), $(2))]];
1184   }
1185
1186   jsle:record "ACT_REMOVE_OVERTIMES"
1187   {
1188     children =
1189     {
1190       [1] = "OT_EFFECT_TARGET";
1191     };
1192     html = [[Снять все эффекты, наложенные %C(1)%]];
1193     checker = no_check;
1194     handler = node_children_placeholders_filler [[self:remove_overtime_effects($(1))]];
1195   }
1196
1197   jsle:enum "GAME_MODES"
1198   {
1199     values =
1200     {
1201       { [GMF.ALL] = [[любой]] };
1202       { [GMF.DUEL] = [[дуэль]] };
1203       { [GMF.SINGLE] = [[одиночная игра]] };
1204     };
1205     html = [[%VALUE()%]];
1206     checker = no_check;
1207     handler = get_value_tonumber;
1208   }
1209
1210   jsle:record "BOOLOP_IS_GAME_IN_MODE"
1211   {
1212     children =
1213     {
1214       [1] = "GAME_MODES";
1215     };
1216     html = [[игровой режим &laquo;%C(1)%&raquo; включён]];
1217     checker = no_check;
1218     handler = node_children_placeholders_filler [[self:is_game_in_mode($(1))]];
1219   }
1220
1221   jsle:record "ACT_SETOBJVAR_OT"
1222   {
1223     children =
1224     {
1225       [1] = "NUMOP_VARIANT";
1226       [2] = "NUMOP_VARIANT";
1227     };
1228     html = [[Запомнить в текущем овертайме в слот №%C(1)% значение %C(2)%]];
1229     checker = no_check;
1230     handler = node_children_placeholders_filler [[self:setobjvar_ot($(1), $(2))]];
1231   }
1232
1233   jsle:record "NUMOP_GETOBJVAR_OT"
1234   {
1235     children =
1236     {
1237       [1] = "NUMOP_VARIANT";
1238     };
1239     html = [[Вспомнить из текущего овертайма из слота №%C(1)%]];
1240     checker = no_check;
1241     handler = node_children_placeholders_filler [[self:getobjvar_ot($(1))]];
1242   }
1243
1244   declare_common(
1245       jsle,
1246       "KEYVALUE_LIST",
1247       "KEYVALUE"
1248     )
1249
1250   jsle:literal "ACT_CRASH_GAME"
1251   {
1252     html = [[<span style="color:red"><b>УРОНИТЬ</b> игру (только для теста)<span>]];
1253     checker = function(self, node)
1254       if common_get_config().crashers_enabled == true then
1255         errr("WARNING: ACT_CRASH_GAME CRASHER IS ON")
1256         return true
1257       end
1258
1259       errr("DETECTED ATTEMPT TO UPLOAD CRASHERS (SCHEMA)")
1260       return false, "crashers are disabled in config"
1261     end;
1262     handler = invariant [[self:crash_game()]];
1263   }
1264
1265   jsle:literal "NUMOP_CRASH_GAME"
1266   {
1267     html = [[<span style="color:red"><b>УРОНИТЬ</b> игру (только для теста)<span>]];
1268     checker = function(self, node)
1269       if common_get_config().crashers_enabled == true then
1270         errr("WARNING: NUMOP_CRASH_GAME CRASHER IS ON")
1271         return true
1272       end
1273
1274       errr("DETECTED ATTEMPT TO UPLOAD CRASHERS (SCHEMA)")
1275       return false, "crashers are disabled in config"
1276     end;
1277     handler = invariant [[(self:crash_game() or 0)]];
1278   }
1279
1280   return jsle
1281 end
1282
1283 return
1284 {
1285   define_schema = define_schema;
1286 }