1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name script_unittype.cpp - The unit-type ccl functions. */
12 //
13 // (c) Copyright 1999-2015 by Lutz Sammer, Jimmy Salmon and Andrettin
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 /*----------------------------------------------------------------------------
33 -- Includes
34 ----------------------------------------------------------------------------*/
35
36 #include "stratagus.h"
37
38 #include "unittype.h"
39
40 #include "actions.h"
41 #include "animation.h"
42 #include "construct.h"
43 #include "editor.h"
44 #include "font.h"
45 #include "luacallback.h"
46 #include "map.h"
47 #include "player.h"
48 #include "script.h"
49 #include "sound.h"
50 #include "spells.h"
51 #include "ui.h"
52 #include "unit.h"
53 #include "unitsound.h"
54 #include "unit_manager.h"
55 #include "video.h"
56
57 /*----------------------------------------------------------------------------
58 -- Variables
59 ----------------------------------------------------------------------------*/
60
61 CUnitTypeVar UnitTypeVar; /// Variables for UnitType and unit.
62
63 // names of boolflags
64 static const char COWARD_KEY[] = "Coward";
65 static const char BUILDING_KEY[] = "Building";
66 static const char FLIP_KEY[] = "Flip";
67 static const char REVEALER_KEY[] = "Revealer";
68 static const char LANDUNIT_KEY[] = "LandUnit";
69 static const char AIRUNIT_KEY[] = "AirUnit";
70 static const char SEAUNIT_KEY[] = "SeaUnit";
71 static const char EXPLODEWHENKILLED_KEY[] = "ExplodeWhenKilled";
72 static const char VISIBLEUNDERFOG_KEY[] = "VisibleUnderFog";
73 static const char PERMANENTCLOAK_KEY[] = "PermanentCloak";
74 static const char DETECTCLOAK_KEY[] = "DetectCloak";
75 static const char ATTACKFROMTRANSPORTER_KEY[] = "AttackFromTransporter";
76 static const char VANISHES_KEY[] = "Vanishes";
77 static const char GROUNDATTACK_KEY[] = "GroundAttack";
78 static const char SHOREBUILDING_KEY[] = "ShoreBuilding";
79 static const char CANATTACK_KEY[] = "CanAttack";
80 static const char BUILDEROUTSIDE_KEY[] = "BuilderOutside";
81 static const char BUILDERLOST_KEY[] = "BuilderLost";
82 static const char CANHARVEST_KEY[] = "CanHarvest";
83 static const char HARVESTER_KEY[] = "Harvester";
84 static const char SELECTABLEBYRECTANGLE_KEY[] = "SelectableByRectangle";
85 static const char ISNOTSELECTABLE_KEY[] = "IsNotSelectable";
86 static const char DECORATION_KEY[] = "Decoration";
87 static const char INDESTRUCTIBLE_KEY[] = "Indestructible";
88 static const char TELEPORTER_KEY[] = "Teleporter";
89 static const char SHIELDPIERCE_KEY[] = "ShieldPiercing";
90 static const char SAVECARGO_KEY[] = "SaveCargo";
91 static const char NONSOLID_KEY[] = "NonSolid";
92 static const char WALL_KEY[] = "Wall";
93 static const char NORANDOMPLACING_KEY[] = "NoRandomPlacing";
94 static const char ORGANIC_KEY[] = "organic";
95 static const char SIDEATTACK_KEY[] = "SideAttack";
96 static const char SKIRMISHER_KEY[] = "Skirmisher";
97 static const char ALWAYSTHREAT_KEY[] = "AlwaysThreat";
98 static const char ELEVATED_KEY[] = "Elevated";
99 static const char NOFRIENDLYFIRE_KEY[] = "NoFriendlyFire";
100 static const char MAINFACILITY_KEY[] = "MainFacility";
101
102 // names of the variable.
103 static const char HITPOINTS_KEY[] = "HitPoints";
104 static const char BUILD_KEY[] = "Build";
105 static const char MANA_KEY[] = "Mana";
106 static const char TRANSPORT_KEY[] = "Transport";
107 static const char RESEARCH_KEY[] = "Research";
108 static const char TRAINING_KEY[] = "Training";
109 static const char UPGRADETO_KEY[] = "UpgradeTo";
110 static const char GIVERESOURCE_KEY[] = "GiveResource";
111 static const char CARRYRESOURCE_KEY[] = "CarryResource";
112 static const char XP_KEY[] = "Xp";
113 static const char KILL_KEY[] = "Kill";
114 static const char SUPPLY_KEY[] = "Supply";
115 static const char DEMAND_KEY[] = "Demand";
116 static const char ARMOR_KEY[] = "Armor";
117 static const char SIGHTRANGE_KEY[] = "SightRange";
118 static const char ATTACKRANGE_KEY[] = "AttackRange";
119 static const char PIERCINGDAMAGE_KEY[] = "PiercingDamage";
120 static const char BASICDAMAGE_KEY[] = "BasicDamage";
121 static const char POSX_KEY[] = "PosX";
122 static const char POSY_KEY[] = "PosY";
123 static const char TARGETPOSX_KEY[] = "TargetPosX";
124 static const char TARGETPOSY_KEY[] = "TargetPosY";
125 static const char RADARRANGE_KEY[] = "RadarRange";
126 static const char RADARJAMMERRANGE_KEY[] = "RadarJammerRange";
127 static const char AUTOREPAIRRANGE_KEY[] = "AutoRepairRange";
128 static const char BLOODLUST_KEY[] = "Bloodlust";
129 static const char HASTE_KEY[] = "Haste";
130 static const char SLOW_KEY[] = "Slow";
131 static const char INVISIBLE_KEY[] = "Invisible";
132 static const char UNHOLYARMOR_KEY[] = "UnholyArmor";
133 static const char SLOT_KEY[] = "Slot";
134 static const char SHIELD_KEY[] = "ShieldPoints";
135 static const char POINTS_KEY[] = "Points";
136 static const char MAXHARVESTERS_KEY[] = "MaxHarvesters";
137 static const char POISON_KEY[] = "Poison";
138 static const char SHIELDPERMEABILITY_KEY[] = "ShieldPermeability";
139 static const char SHIELDPIERCING_KEY[] = "ShieldPiercing";
140 static const char ISALIVE_KEY[] = "IsAlive";
141 static const char PLAYER_KEY[] = "Player";
142 static const char PRIORITY_KEY[] = "Priority";
143
144 /*----------------------------------------------------------------------------
145 -- Functions
146 ----------------------------------------------------------------------------*/
147
CBoolKeys()148 CUnitTypeVar::CBoolKeys::CBoolKeys()
149 {
150
151 const char *const tmp[] = {COWARD_KEY, BUILDING_KEY, FLIP_KEY, REVEALER_KEY,
152 LANDUNIT_KEY, AIRUNIT_KEY, SEAUNIT_KEY, EXPLODEWHENKILLED_KEY,
153 VISIBLEUNDERFOG_KEY, PERMANENTCLOAK_KEY, DETECTCLOAK_KEY,
154 ATTACKFROMTRANSPORTER_KEY, VANISHES_KEY, GROUNDATTACK_KEY,
155 SHOREBUILDING_KEY, CANATTACK_KEY, BUILDEROUTSIDE_KEY,
156 BUILDERLOST_KEY, CANHARVEST_KEY, HARVESTER_KEY, SELECTABLEBYRECTANGLE_KEY,
157 ISNOTSELECTABLE_KEY, DECORATION_KEY, INDESTRUCTIBLE_KEY, TELEPORTER_KEY, SHIELDPIERCE_KEY,
158 SAVECARGO_KEY, NONSOLID_KEY, WALL_KEY, NORANDOMPLACING_KEY, ORGANIC_KEY, SIDEATTACK_KEY, SKIRMISHER_KEY,
159 ALWAYSTHREAT_KEY, ELEVATED_KEY, NOFRIENDLYFIRE_KEY, MAINFACILITY_KEY
160 };
161
162 for (int i = 0; i < NBARALREADYDEFINED; ++i) {
163 buildin[i].offset = i;
164 buildin[i].keylen = strlen(tmp[i]);
165 buildin[i].key = tmp[i];
166 }
167 Init();
168 }
169
CVariableKeys()170 CUnitTypeVar::CVariableKeys::CVariableKeys()
171 {
172
173 const char *const tmp[] = {HITPOINTS_KEY, BUILD_KEY, MANA_KEY, TRANSPORT_KEY,
174 RESEARCH_KEY, TRAINING_KEY, UPGRADETO_KEY, GIVERESOURCE_KEY,
175 CARRYRESOURCE_KEY, XP_KEY, KILL_KEY, SUPPLY_KEY, DEMAND_KEY, ARMOR_KEY,
176 SIGHTRANGE_KEY, ATTACKRANGE_KEY, PIERCINGDAMAGE_KEY,
177 BASICDAMAGE_KEY, POSX_KEY, POSY_KEY, TARGETPOSX_KEY, TARGETPOSY_KEY, RADARRANGE_KEY,
178 RADARJAMMERRANGE_KEY, AUTOREPAIRRANGE_KEY, BLOODLUST_KEY, HASTE_KEY,
179 SLOW_KEY, INVISIBLE_KEY, UNHOLYARMOR_KEY, SLOT_KEY, SHIELD_KEY, POINTS_KEY,
180 MAXHARVESTERS_KEY, POISON_KEY, SHIELDPERMEABILITY_KEY, SHIELDPIERCING_KEY, ISALIVE_KEY, PLAYER_KEY,
181 PRIORITY_KEY
182 };
183
184 for (int i = 0; i < NVARALREADYDEFINED; ++i) {
185 buildin[i].offset = i;
186 buildin[i].keylen = strlen(tmp[i]);
187 buildin[i].key = tmp[i];
188 }
189 Init();
190 }
191
192 int GetSpriteIndex(const char *SpriteName);
193
194 /**
195 ** Get the resource ID from a SCM object.
196 **
197 ** @param l Lua state.
198 **
199 ** @return the resource id
200 */
CclGetResourceByName(lua_State * l)201 unsigned CclGetResourceByName(lua_State *l)
202 {
203 const char *const tmp = LuaToString(l, -1);
204 const std::string value = tmp ? tmp : "";
205 const int resId = GetResourceIdByName(l, value.c_str());
206
207 return resId;
208 }
209
210
211 /**
212 ** Find the index of a extra death type
213 */
ExtraDeathIndex(const char * death)214 int ExtraDeathIndex(const char *death)
215 {
216 for (unsigned int det = 0; det < ANIMATIONS_DEATHTYPES; ++det) {
217 if (!strcmp(death, ExtraDeathTypes[det].c_str())) {
218 return det;
219 }
220 }
221 return ANIMATIONS_DEATHTYPES;
222 }
223
224 /**
225 ** Parse BuildingRules
226 **
227 ** @param l Lua state.
228 ** @param blist BuildingRestriction to fill in
229 */
ParseBuildingRules(lua_State * l,std::vector<CBuildRestriction * > & blist)230 static void ParseBuildingRules(lua_State *l, std::vector<CBuildRestriction *> &blist)
231 {
232 CBuildRestrictionAnd *andlist = new CBuildRestrictionAnd();
233
234 const int args = lua_rawlen(l, -1);
235 Assert(!(args & 1)); // must be even
236
237 for (int i = 0; i < args; ++i) {
238 const char *value = LuaToString(l, -1, i + 1);
239 ++i;
240 lua_rawgeti(l, -1, i + 1);
241 if (!lua_istable(l, -1)) {
242 LuaError(l, "incorrect argument");
243 }
244 if (!strcmp(value, "distance")) {
245 CBuildRestrictionDistance *b = new CBuildRestrictionDistance;
246
247 for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
248 value = LuaToString(l, -2);
249 if (!strcmp(value, "Distance")) {
250 b->Distance = LuaToNumber(l, -1);
251 } else if (!strcmp(value, "DistanceType")) {
252 value = LuaToString(l, -1);
253 if (value[0] == '=') {
254 if ((value[1] == '=' && value[2] == '\0') || (value[1] == '\0')) {
255 b->DistanceType = Equal;
256 }
257 } else if (value[0] == '>') {
258 if (value[1] == '=' && value[2] == '\0') {
259 b->DistanceType = GreaterThanEqual;
260 } else if (value[1] == '\0') {
261 b->DistanceType = GreaterThan;
262 }
263 } else if (value[0] == '<') {
264 if (value[1] == '=' && value[2] == '\0') {
265 b->DistanceType = LessThanEqual;
266 } else if (value[1] == '\0') {
267 b->DistanceType = LessThan;
268 }
269 } else if (value[0] == '!' && value[1] == '=' && value[2] == '\0') {
270 b->DistanceType = NotEqual;
271 }
272 } else if (!strcmp(value, "Type")) {
273 b->RestrictTypeName = LuaToString(l, -1);
274 } else if (!strcmp(value, "Owner")) {
275 b->RestrictTypeOwner = LuaToString(l, -1);
276 } else if (!strcmp(value, "CheckBuilder")) {
277 b->CheckBuilder = LuaToBoolean(l, -1);
278 } else if (!strcmp(value, "Diagonal")) {
279 b->Diagonal = LuaToBoolean(l, -1);
280 } else {
281 LuaError(l, "Unsupported BuildingRules distance tag: %s" _C_ value);
282 }
283 }
284 andlist->push_back(b);
285 } else if (!strcmp(value, "addon")) {
286 CBuildRestrictionAddOn *b = new CBuildRestrictionAddOn;
287
288 for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
289 value = LuaToString(l, -2);
290 if (!strcmp(value, "OffsetX")) {
291 b->Offset.x = LuaToNumber(l, -1);
292 } else if (!strcmp(value, "OffsetY")) {
293 b->Offset.y = LuaToNumber(l, -1);
294 } else if (!strcmp(value, "Type")) {
295 b->ParentName = LuaToString(l, -1);
296 } else {
297 LuaError(l, "Unsupported BuildingRules addon tag: %s" _C_ value);
298 }
299 }
300 andlist->push_back(b);
301 } else if (!strcmp(value, "ontop")) {
302 CBuildRestrictionOnTop *b = new CBuildRestrictionOnTop;
303
304 for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
305 value = LuaToString(l, -2);
306 if (!strcmp(value, "Type")) {
307 b->ParentName = LuaToString(l, -1);
308 } else if (!strcmp(value, "ReplaceOnDie")) {
309 b->ReplaceOnDie = LuaToBoolean(l, -1);
310 } else if (!strcmp(value, "ReplaceOnBuild")) {
311 b->ReplaceOnBuild = LuaToBoolean(l, -1);
312 } else {
313 LuaError(l, "Unsupported BuildingRules ontop tag: %s" _C_ value);
314 }
315 }
316 andlist->push_back(b);
317 } else if (!strcmp(value, "has-unit")) {
318 CBuildRestrictionHasUnit *b = new CBuildRestrictionHasUnit;
319
320 for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
321 value = LuaToString(l, -2);
322 if (!strcmp(value, "Type")) {
323 b->RestrictTypeName = LuaToString(l, -1);
324 } else if (!strcmp(value, "Owner")) {
325 b->RestrictTypeOwner = LuaToString(l, -1);
326 } else if (!strcmp(value, "Count")) {
327 b->Count = LuaToNumber(l, -1);
328 } else if (!strcmp(value, "CountType")) {
329 value = LuaToString(l, -1);
330 if (value[0] == '=') {
331 if ((value[1] == '=' && value[2] == '\0') || (value[1] == '\0')) {
332 b->CountType = Equal;
333 }
334 } else if (value[0] == '>') {
335 if (value[1] == '=' && value[2] == '\0') {
336 b->CountType = GreaterThanEqual;
337 } else if (value[1] == '\0') {
338 b->CountType = GreaterThan;
339 }
340 } else if (value[0] == '<') {
341 if (value[1] == '=' && value[2] == '\0') {
342 b->CountType = LessThanEqual;
343 } else if (value[1] == '\0') {
344 b->CountType = LessThan;
345 }
346 } else if (value[0] == '!' && value[1] == '=' && value[2] == '\0') {
347 b->CountType = NotEqual;
348 }
349 } else {
350 LuaError(l, "Unsupported BuildingRules has-unit tag: %s" _C_ value);
351 }
352 }
353 andlist->push_back(b);
354 }
355 else if (!strcmp(value, "surrounded-by")) {
356 CBuildRestrictionSurroundedBy *b = new CBuildRestrictionSurroundedBy;
357
358 for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
359 value = LuaToString(l, -2);
360 if (!strcmp(value, "Type")) {
361 b->RestrictTypeName = LuaToString(l, -1);
362 } else if (!strcmp(value, "Count")) {
363 b->Count = LuaToNumber(l, -1);
364 } else if (!strcmp(value, "CountType")) {
365 value = LuaToString(l, -1);
366 if (value[0] == '=') {
367 if ((value[1] == '=' && value[2] == '\0') || (value[1] == '\0')) {
368 b->CountType = Equal;
369 }
370 } else if (value[0] == '>') {
371 if (value[1] == '=' && value[2] == '\0') {
372 b->CountType = GreaterThanEqual;
373 } else if (value[1] == '\0') {
374 b->CountType = GreaterThan;
375 }
376 } else if (value[0] == '<') {
377 if (value[1] == '=' && value[2] == '\0') {
378 b->CountType = LessThanEqual;
379 } else if (value[1] == '\0') {
380 b->CountType = LessThan;
381 }
382 } else if (value[0] == '!' && value[1] == '=' && value[2] == '\0') {
383 b->CountType = NotEqual;
384 }
385 } else if (!strcmp(value, "Distance")) {
386 b->Distance = LuaToNumber(l, -1);
387 } else if (!strcmp(value, "DistanceType")) {
388 value = LuaToString(l, -1);
389 if (value[0] == '=') {
390 if ((value[1] == '=' && value[2] == '\0') || (value[1] == '\0')) {
391 b->DistanceType = Equal;
392 }
393 } else if (value[0] == '>') {
394 if (value[1] == '=' && value[2] == '\0') {
395 b->DistanceType = GreaterThanEqual;
396 } else if (value[1] == '\0') {
397 b->DistanceType = GreaterThan;
398 }
399 } else if (value[0] == '<') {
400 if (value[1] == '=' && value[2] == '\0') {
401 b->DistanceType = LessThanEqual;
402 } else if (value[1] == '\0') {
403 b->DistanceType = LessThan;
404 }
405 } else if (value[0] == '!' && value[1] == '=' && value[2] == '\0') {
406 b->DistanceType = NotEqual;
407 }
408 } else if (!strcmp(value, "Owner")) {
409 b->RestrictTypeOwner = LuaToString(l, -1);
410 } else if (!strcmp(value, "CheckBuilder")) {
411 b->CheckBuilder = LuaToBoolean(l, -1);
412 } else {
413 LuaError(l, "Unsupported BuildingRules surrounded-by tag: %s" _C_ value);
414 }
415 }
416 andlist->push_back(b);
417 } else {
418 LuaError(l, "Unsupported BuildingRules tag: %s" _C_ value);
419 }
420 lua_pop(l, 1);
421 }
422 blist.push_back(andlist);
423 }
424
UpdateDefaultBoolFlags(CUnitType & type)425 static void UpdateDefaultBoolFlags(CUnitType &type)
426 {
427 // BoolFlag
428 type.BoolFlag[BUILDING_INDEX].value = type.Building;
429 type.BoolFlag[FLIP_INDEX].value = type.Flip;
430 type.BoolFlag[LANDUNIT_INDEX].value = type.LandUnit;
431 type.BoolFlag[AIRUNIT_INDEX].value = type.AirUnit;
432 type.BoolFlag[SEAUNIT_INDEX].value = type.SeaUnit;
433 type.BoolFlag[EXPLODEWHENKILLED_INDEX].value = type.ExplodeWhenKilled;
434 type.BoolFlag[CANATTACK_INDEX].value = type.CanAttack;
435 }
436
437 static const std::string shadowMarker = std::string("MARKER");
438 /**
439 ** <b>Description</b>
440 **
441 ** Parse unit-type.
442 **
443 ** @param l Lua state.
444 **
445 ** Example:
446 **
447 ** <div class="example"><code><strong>DefineUnitType</strong>("unit-silvermoon-archer", { Name = _("Silvermoon Archer"),
448 ** Image = {"file", "human/units/elven_archer.png", "size", {72, 72}},
449 ** Animations = "animations-archer", Icon = "icon-archer",
450 ** Costs = {"time", 70, "gold", 500, "wood", 50},
451 ** Speed = 10,
452 ** HitPoints = 45,
453 ** DrawLevel = 40,
454 ** TileSize = {1, 1}, BoxSize = {33, 33},
455 ** SightRange = 6, ComputerReactionRange = 7, PersonReactionRange = 6,
456 ** BasicDamage = 4, PiercingDamage = 6, Missile = "missile-arrow",
457 ** MaxAttackRange = 4,
458 ** Priority = 75,
459 ** Points = 60,
460 ** Demand = 1,
461 ** Corpse = "unit-human-dead-body",
462 ** Type = "land",
463 ** RightMouseAction = "attack",
464 ** CanAttack = true,
465 ** CanTargetLand = true, CanTargetSea = true, CanTargetAir = true,
466 ** LandUnit = true,
467 ** organic = true,
468 ** SelectableByRectangle = true,
469 ** Sounds = {
470 ** "selected", "archer-selected",
471 ** "acknowledge", "archer-acknowledge",
472 ** "ready", "archer-ready",
473 ** "help", "basic human voices help 1",
474 ** "dead", "basic human voices dead"} } )</code></div>
475 */
CclDefineUnitType(lua_State * l)476 static int CclDefineUnitType(lua_State *l)
477 {
478 LuaCheckArgs(l, 2);
479 if (!lua_istable(l, 2)) {
480 LuaError(l, "incorrect argument");
481 }
482
483 // Slot identifier
484 const char *str = LuaToString(l, 1);
485 CUnitType *type = UnitTypeByIdent(str);
486 int redefine;
487 if (type) {
488 redefine = 1;
489 DebugPrint("Redefining unit-type '%s'\n" _C_ str);
490 } else {
491 type = NewUnitTypeSlot(str);
492 redefine = 0;
493 }
494
495 type->NumDirections = 0;
496 type->Flip = 1;
497
498 // Parse the list: (still everything could be changed!)
499 for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
500 const char *value = LuaToString(l, -2);
501 if (!strcmp(value, "Name")) {
502 type->Name = LuaToString(l, -1);
503 } else if (!strcmp(value, "Image")) {
504 if (!lua_istable(l, -1)) {
505 LuaError(l, "incorrect argument");
506 }
507 int subargs = lua_rawlen(l, -1);
508 for (int k = 0; k < subargs; ++k) {
509 value = LuaToString(l, -1, k + 1);
510 ++k;
511
512 if (!strcmp(value, "file")) {
513 type->File = LuaToString(l, -1, k + 1);
514 } else if (!strcmp(value, "size")) {
515 lua_rawgeti(l, -1, k + 1);
516 CclGetPos(l, &type->Width, &type->Height);
517 lua_pop(l, 1);
518 } else {
519 LuaError(l, "Unsupported image tag: %s" _C_ value);
520 }
521 }
522 if (redefine && type->Sprite) {
523 CGraphic::Free(type->Sprite);
524 type->Sprite = NULL;
525 }
526 if (type->ShadowFile == shadowMarker) {
527 type->ShadowFile = type->File;
528 if (type->ShadowWidth == 0 && type->ShadowHeight == 0) {
529 type->ShadowWidth = type->Width;
530 type->ShadowHeight = type->Height;
531 }
532 }
533 } else if (!strcmp(value, "Shadow")) {
534 // default to same spritemap as unit
535 if (type->File.length() > 0) {
536 type->ShadowFile = type->File;
537 type->ShadowWidth = type->Width;
538 type->ShadowHeight = type->Height;
539 } else {
540 type->ShadowFile = shadowMarker;
541 }
542 if (!lua_istable(l, -1)) {
543 LuaError(l, "incorrect argument");
544 }
545 const int subargs = lua_rawlen(l, -1);
546 for (int k = 0; k < subargs; ++k) {
547 value = LuaToString(l, -1, k + 1);
548 ++k;
549
550 if (!strcmp(value, "file")) {
551 type->ShadowFile = LuaToString(l, -1, k + 1);
552 } else if (!strcmp(value, "size")) {
553 lua_rawgeti(l, -1, k + 1);
554 CclGetPos(l, &type->ShadowWidth, &type->ShadowHeight);
555 lua_pop(l, 1);
556 } else if (!strcmp(value, "offset")) {
557 lua_rawgeti(l, -1, k + 1);
558 CclGetPos(l, &type->ShadowOffsetX, &type->ShadowOffsetY);
559 lua_pop(l, 1);
560 } else if (!strcmp(value, "sprite-frame")) {
561 type->ShadowSpriteFrame = LuaToNumber(l, -1, k + 1);
562 } else if (!strcmp(value, "scale")) {
563 type->ShadowScale = LuaToNumber(l, -1, k + 1);
564 } else {
565 LuaError(l, "Unsupported shadow tag: %s" _C_ value);
566 }
567 }
568 if (redefine && type->ShadowSprite) {
569 CGraphic::Free(type->ShadowSprite);
570 type->ShadowSprite = NULL;
571 }
572 } else if (!strcmp(value, "Offset")) {
573 CclGetPos(l, &type->OffsetX, &type->OffsetY);
574 } else if (!strcmp(value, "Flip")) {
575 type->Flip = LuaToBoolean(l, -1);
576 } else if (!strcmp(value, "Animations")) {
577 type->Animations = AnimationsByIdent(LuaToString(l, -1));
578 if (!type->Animations) {
579 DebugPrint("Warning animation '%s' not found\n" _C_ LuaToString(l, -1));
580 }
581 } else if (!strcmp(value, "Icon")) {
582 type->Icon.Name = LuaToString(l, -1);
583 type->Icon.Icon = NULL;
584 #ifdef USE_MNG
585 } else if (!strcmp(value, "Portrait")) {
586 if (!lua_istable(l, -1)) {
587 LuaError(l, "incorrect argument");
588 }
589 const int subargs = lua_rawlen(l, -1);
590 type->Portrait.Num = subargs;
591 type->Portrait.Files = new std::string[type->Portrait.Num];
592 type->Portrait.Mngs = new Mng *[type->Portrait.Num];
593 memset(type->Portrait.Mngs, 0, type->Portrait.Num * sizeof(Mng *));
594 for (int k = 0; k < subargs; ++k) {
595 type->Portrait.Files[k] = LuaToString(l, -1, k + 1);
596 }
597 #endif
598 } else if (!strcmp(value, "Costs")) {
599 if (!lua_istable(l, -1)) {
600 LuaError(l, "incorrect argument");
601 }
602 const int subargs = lua_rawlen(l, -1);
603 for (int k = 0; k < subargs; ++k) {
604 lua_rawgeti(l, -1, k + 1);
605 const int res = CclGetResourceByName(l);
606 lua_pop(l, 1);
607 ++k;
608 type->DefaultStat.Costs[res] = LuaToNumber(l, -1, k + 1);
609 }
610 } else if (!strcmp(value, "Storing")) {
611 if (!lua_istable(l, -1)) {
612 LuaError(l, "incorrect argument");
613 }
614 const int subargs = lua_rawlen(l, -1);
615 for (int k = 0; k < subargs; ++k) {
616 lua_rawgeti(l, -1, k + 1);
617 const int res = CclGetResourceByName(l);
618 lua_pop(l, 1);
619 ++k;
620 type->DefaultStat.Storing[res] = LuaToNumber(l, -1, k + 1);
621 }
622 } else if (!strcmp(value, "ImproveProduction")) {
623 if (!lua_istable(l, -1)) {
624 LuaError(l, "incorrect argument");
625 }
626 const int subargs = lua_rawlen(l, -1);
627 for (int k = 0; k < subargs; ++k) {
628 lua_rawgeti(l, -1, k + 1);
629 const int res = CclGetResourceByName(l);
630 lua_pop(l, 1);
631 ++k;
632 type->DefaultStat.ImproveIncomes[res] = DefaultIncomes[res] + LuaToNumber(l, -1, k + 1);
633 }
634 } else if (!strcmp(value, "Construction")) {
635 // FIXME: What if constructions aren't yet loaded?
636 type->Construction = ConstructionByIdent(LuaToString(l, -1));
637 } else if (!strcmp(value, "DrawLevel")) {
638 type->DrawLevel = LuaToNumber(l, -1);
639 } else if (!strcmp(value, "MaxOnBoard")) {
640 type->MaxOnBoard = LuaToNumber(l, -1);
641 } else if (!strcmp(value, "BoardSize")) {
642 type->BoardSize = LuaToNumber(l, -1);
643 } else if (!strcmp(value, "ButtonLevelForTransporter")) {
644 type->ButtonLevelForTransporter = LuaToNumber(l, -1);
645 } else if (!strcmp(value, "StartingResources")) {
646 type->StartingResources = LuaToNumber(l, -1);
647 } else if (!strcmp(value, "RegenerationRate")) {
648 type->DefaultStat.Variables[HP_INDEX].Increase = LuaToNumber(l, -1);
649 } else if (!strcmp(value, "RegenerationFrequency")) {
650 int value = LuaToNumber(l, -1);
651 type->DefaultStat.Variables[HP_INDEX].IncreaseFrequency = value;
652 if (type->DefaultStat.Variables[HP_INDEX].IncreaseFrequency != value) {
653 LuaError(l, "RegenerationFrequency out of range!");
654 }
655 } else if (!strcmp(value, "BurnPercent")) {
656 type->BurnPercent = LuaToNumber(l, -1);
657 } else if (!strcmp(value, "BurnDamageRate")) {
658 type->BurnDamageRate = LuaToNumber(l, -1);
659 } else if (!strcmp(value, "PoisonDrain")) {
660 type->PoisonDrain = LuaToNumber(l, -1);
661 } else if (!strcmp(value, "ShieldPoints")) {
662 if (lua_istable(l, -1)) {
663 DefineVariableField(l, type->DefaultStat.Variables + SHIELD_INDEX, -1);
664 } else if (lua_isnumber(l, -1)) {
665 type->DefaultStat.Variables[SHIELD_INDEX].Max = LuaToNumber(l, -1);
666 type->DefaultStat.Variables[SHIELD_INDEX].Value = 0;
667 type->DefaultStat.Variables[SHIELD_INDEX].Increase = 1;
668 type->DefaultStat.Variables[SHIELD_INDEX].Enable = 1;
669 }
670 } else if (!strcmp(value, "TileSize")) {
671 CclGetPos(l, &type->TileWidth, &type->TileHeight);
672 } else if (!strcmp(value, "NeutralMinimapColor")) {
673 type->NeutralMinimapColorRGB.Parse(l);
674 } else if (!strcmp(value, "Neutral")) {
675 type->Neutral = LuaToBoolean(l, -1);
676 } else if (!strcmp(value, "BoxSize")) {
677 CclGetPos(l, &type->BoxWidth, &type->BoxHeight);
678 } else if (!strcmp(value, "BoxOffset")) {
679 CclGetPos(l, &type->BoxOffsetX, &type->BoxOffsetY);
680 } else if (!strcmp(value, "NumDirections")) {
681 type->NumDirections = LuaToNumber(l, -1);
682 } else if (!strcmp(value, "ComputerReactionRange")) {
683 type->ReactRangeComputer = LuaToNumber(l, -1);
684 } else if (!strcmp(value, "PersonReactionRange")) {
685 type->ReactRangePerson = LuaToNumber(l, -1);
686 } else if (!strcmp(value, "Missile")) {
687 type->Missile.Name = LuaToString(l, -1);
688 type->Missile.Missile = NULL;
689 } else if (!strcmp(value, "MinAttackRange")) {
690 type->MinAttackRange = LuaToNumber(l, -1);
691 } else if (!strcmp(value, "MaxAttackRange")) {
692 type->DefaultStat.Variables[ATTACKRANGE_INDEX].Value = LuaToNumber(l, -1);
693 type->DefaultStat.Variables[ATTACKRANGE_INDEX].Max = LuaToNumber(l, -1);
694 type->DefaultStat.Variables[ATTACKRANGE_INDEX].Enable = 1;
695 } else if (!strcmp(value, "MaxHarvesters")) {
696 type->DefaultStat.Variables[MAXHARVESTERS_INDEX].Value = LuaToNumber(l, -1);
697 type->DefaultStat.Variables[MAXHARVESTERS_INDEX].Max = LuaToNumber(l, -1);
698 } else if (!strcmp(value, "Priority")) {
699 type->DefaultStat.Variables[PRIORITY_INDEX].Value = LuaToNumber(l, -1);
700 type->DefaultStat.Variables[PRIORITY_INDEX].Max = LuaToNumber(l, -1);
701 } else if (!strcmp(value, "AnnoyComputerFactor")) {
702 type->AnnoyComputerFactor = LuaToNumber(l, -1);
703 } else if (!strcmp(value, "AiAdjacentRange")) {
704 type->AiAdjacentRange = LuaToNumber(l, -1);
705 } else if (!strcmp(value, "DecayRate")) {
706 type->DecayRate = LuaToNumber(l, -1);
707 } else if (!strcmp(value, "Corpse")) {
708 type->CorpseName = LuaToString(l, -1);
709 type->CorpseType = NULL;
710 } else if (!strcmp(value, "DamageType")) {
711 value = LuaToString(l, -1);
712 //int check = ExtraDeathIndex(value);
713 type->DamageType = value;
714 } else if (!strcmp(value, "ExplodeWhenKilled")) {
715 type->ExplodeWhenKilled = 1;
716 type->Explosion.Name = LuaToString(l, -1);
717 type->Explosion.Missile = NULL;
718 } else if (!strcmp(value, "TeleportCost")) {
719 type->TeleportCost = LuaToNumber(l, -1);
720 } else if (!strcmp(value, "TeleportEffectIn")) {
721 type->TeleportEffectIn = new LuaCallback(l, -1);
722 } else if (!strcmp(value, "TeleportEffectOut")) {
723 type->TeleportEffectOut = new LuaCallback(l, -1);
724 } else if (!strcmp(value, "OnDeath")) {
725 type->OnDeath = new LuaCallback(l, -1);
726 } else if (!strcmp(value, "OnHit")) {
727 type->OnHit = new LuaCallback(l, -1);
728 } else if (!strcmp(value, "OnEachCycle")) {
729 type->OnEachCycle = new LuaCallback(l, -1);
730 } else if (!strcmp(value, "OnEachSecond")) {
731 type->OnEachSecond = new LuaCallback(l, -1);
732 } else if (!strcmp(value, "OnInit")) {
733 type->OnInit = new LuaCallback(l, -1);
734 } else if (!strcmp(value, "OnReady")) {
735 type->OnReady = new LuaCallback(l, -1);
736 } else if (!strcmp(value, "Type")) {
737 value = LuaToString(l, -1);
738 if (!strcmp(value, "land")) {
739 type->UnitType = UnitTypeLand;
740 } else if (!strcmp(value, "fly")) {
741 type->UnitType = UnitTypeFly;
742 } else if (!strcmp(value, "naval")) {
743 type->UnitType = UnitTypeNaval;
744 } else {
745 LuaError(l, "Unsupported Type: %s" _C_ value);
746 }
747 } else if (!strcmp(value, "MissileOffsets")) {
748 if (!lua_istable(l, -1)) {
749 LuaError(l, "incorrect argument");
750 }
751 const int subargs = lua_rawlen(l, -1);
752 for (int k = 0; k < subargs; ++k) {
753 lua_rawgeti(l, -1, k + 1);
754 if (!lua_istable(l, -1) || lua_rawlen(l, -1) != UnitSides) {
755 LuaError(l, "incorrect argument");
756 }
757 for (int m = 0; m < UnitSides; ++m) {
758 lua_rawgeti(l, -1, m + 1);
759 CclGetPos(l, &type->MissileOffsets[m][k].x, &type->MissileOffsets[m][k].y);
760 lua_pop(l, 1);
761 }
762 lua_pop(l, 1);
763 }
764 } else if (!strcmp(value, "Impact")) {
765 if (!lua_istable(l, -1)) {
766 LuaError(l, "incorrect argument");
767 }
768 const int subargs = lua_rawlen(l, -1);
769 for (int k = 0; k < subargs; ++k) {
770 const char *dtype = LuaToString(l, -1, k + 1);
771 ++k;
772
773 if (!strcmp(dtype, "general")) {
774 type->Impact[ANIMATIONS_DEATHTYPES].Name = LuaToString(l, -1, k + 1);
775 type->Impact[ANIMATIONS_DEATHTYPES].Missile = NULL;
776 } else if (!strcmp(dtype, "shield")) {
777 type->Impact[ANIMATIONS_DEATHTYPES + 1].Name = LuaToString(l, -1, k + 1);
778 type->Impact[ANIMATIONS_DEATHTYPES + 1].Missile = NULL;
779 } else {
780 int num = 0;
781 for (; num < ANIMATIONS_DEATHTYPES; ++num) {
782 if (dtype == ExtraDeathTypes[num]) {
783 break;
784 }
785 }
786 if (num == ANIMATIONS_DEATHTYPES) {
787 LuaError(l, "Death type not found: %s" _C_ dtype);
788 } else {
789 type->Impact[num].Name = LuaToString(l, -1, k + 1);
790 type->Impact[num].Missile = NULL;
791 }
792 }
793 }
794 } else if (!strcmp(value, "RightMouseAction")) {
795 value = LuaToString(l, -1);
796 if (!strcmp(value, "none")) {
797 type->MouseAction = MouseActionNone;
798 } else if (!strcmp(value, "attack")) {
799 type->MouseAction = MouseActionAttack;
800 } else if (!strcmp(value, "move")) {
801 type->MouseAction = MouseActionMove;
802 } else if (!strcmp(value, "harvest")) {
803 type->MouseAction = MouseActionHarvest;
804 } else if (!strcmp(value, "spell-cast")) {
805 type->MouseAction = MouseActionSpellCast;
806 } else if (!strcmp(value, "sail")) {
807 type->MouseAction = MouseActionSail;
808 } else {
809 LuaError(l, "Unsupported RightMouseAction: %s" _C_ value);
810 }
811 } else if (!strcmp(value, "CanAttack")) {
812 type->CanAttack = LuaToBoolean(l, -1);
813 } else if (!strcmp(value, "RepairRange")) {
814 type->RepairRange = LuaToNumber(l, -1);
815 } else if (!strcmp(value, "RepairHp")) {
816 type->RepairHP = LuaToNumber(l, -1);
817 } else if (!strcmp(value, "RepairCosts")) {
818 if (!lua_istable(l, -1)) {
819 LuaError(l, "incorrect argument");
820 }
821 const int subargs = lua_rawlen(l, -1);
822 for (int k = 0; k < subargs; ++k) {
823 lua_rawgeti(l, -1, k + 1);
824 const int res = CclGetResourceByName(l);
825 lua_pop(l, 1);
826 ++k;
827 type->RepairCosts[res] = LuaToNumber(l, -1, k + 1);
828 }
829 } else if (!strcmp(value, "CanTargetLand")) {
830 if (LuaToBoolean(l, -1)) {
831 type->CanTarget |= CanTargetLand;
832 } else {
833 type->CanTarget &= ~CanTargetLand;
834 }
835 } else if (!strcmp(value, "CanTargetSea")) {
836 if (LuaToBoolean(l, -1)) {
837 type->CanTarget |= CanTargetSea;
838 } else {
839 type->CanTarget &= ~CanTargetSea;
840 }
841 } else if (!strcmp(value, "CanTargetAir")) {
842 if (LuaToBoolean(l, -1)) {
843 type->CanTarget |= CanTargetAir;
844 } else {
845 type->CanTarget &= ~CanTargetAir;
846 }
847 } else if (!strcmp(value, "Building")) {
848 type->Building = LuaToBoolean(l, -1);
849 } else if (!strcmp(value, "BuildingRules")) {
850 if (!lua_istable(l, -1)) {
851 LuaError(l, "incorrect argument");
852 }
853 const int subargs = lua_rawlen(l, -1);
854 // Free any old restrictions if they are redefined
855 for (std::vector<CBuildRestriction *>::iterator b = type->BuildingRules.begin();
856 b != type->BuildingRules.end(); ++b) {
857 delete *b;
858 }
859 type->BuildingRules.clear();
860 for (int k = 0; k < subargs; ++k) {
861 lua_rawgeti(l, -1, k + 1);
862 if (!lua_istable(l, -1)) {
863 LuaError(l, "incorrect argument");
864 }
865 ParseBuildingRules(l, type->BuildingRules);
866 lua_pop(l, 1);
867 }
868 } else if (!strcmp(value, "AiBuildingRules")) {
869 if (!lua_istable(l, -1)) {
870 LuaError(l, "incorrect argument");
871 }
872 const int subargs = lua_rawlen(l, -1);
873 // Free any old restrictions if they are redefined
874 for (std::vector<CBuildRestriction *>::iterator b = type->AiBuildingRules.begin();
875 b != type->AiBuildingRules.end(); ++b) {
876 delete *b;
877 }
878 type->AiBuildingRules.clear();
879 for (int k = 0; k < subargs; ++k) {
880 lua_rawgeti(l, -1, k + 1);
881 if (!lua_istable(l, -1)) {
882 LuaError(l, "incorrect argument");
883 }
884 ParseBuildingRules(l, type->AiBuildingRules);
885 lua_pop(l, 1);
886 }
887 } else if (!strcmp(value, "AutoBuildRate")) {
888 type->AutoBuildRate = LuaToNumber(l, -1);
889 } else if (!strcmp(value, "LandUnit")) {
890 type->LandUnit = LuaToBoolean(l, -1);
891 } else if (!strcmp(value, "AirUnit")) {
892 type->AirUnit = LuaToBoolean(l, -1);
893 } else if (!strcmp(value, "SeaUnit")) {
894 type->SeaUnit = LuaToBoolean(l, -1);
895 } else if (!strcmp(value, "RandomMovementProbability")) {
896 type->RandomMovementProbability = LuaToNumber(l, -1);
897 } else if (!strcmp(value, "RandomMovementDistance")) {
898 type->RandomMovementDistance = LuaToNumber(l, -1);
899 } else if (!strcmp(value, "ClicksToExplode")) {
900 type->ClicksToExplode = LuaToNumber(l, -1);
901 } else if (!strcmp(value, "CanTransport")) {
902 // Warning: CanTransport should only be used AFTER all bool flags
903 // have been defined.
904 if (!lua_istable(l, -1)) {
905 LuaError(l, "incorrect argument");
906 }
907 if (type->MaxOnBoard == 0) { // set default value.
908 type->MaxOnBoard = 1;
909 }
910
911 if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
912 type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
913 }
914
915 const int subargs = lua_rawlen(l, -1);
916 for (int k = 0; k < subargs; ++k) {
917 value = LuaToString(l, -1, k + 1);
918 ++k;
919
920 const int index = UnitTypeVar.BoolFlagNameLookup[value];
921 if (index != -1) {
922 value = LuaToString(l, -1, k + 1);
923 type->BoolFlag[index].CanTransport = Ccl2Condition(l, value);
924 continue;
925 }
926 LuaError(l, "Unsupported flag tag for CanTransport: %s" _C_ value);
927 }
928 } else if (!strcmp(value, "CanGatherResources")) {
929 const int args = lua_rawlen(l, -1);
930 for (int j = 0; j < args; ++j) {
931 lua_rawgeti(l, -1, j + 1);
932 ResourceInfo *res = new ResourceInfo;
933 if (!lua_istable(l, -1)) {
934 LuaError(l, "incorrect argument");
935 }
936 const int subargs = lua_rawlen(l, -1);
937 for (int k = 0; k < subargs; ++k) {
938 value = LuaToString(l, -1, k + 1);
939 ++k;
940 if (!strcmp(value, "resource-id")) {
941 lua_rawgeti(l, -1, k + 1);
942 res->ResourceId = CclGetResourceByName(l);
943 lua_pop(l, 1);
944 type->ResInfo[res->ResourceId] = res;
945 } else if (!strcmp(value, "resource-step")) {
946 res->ResourceStep = LuaToNumber(l, -1, k + 1);
947 } else if (!strcmp(value, "final-resource")) {
948 lua_rawgeti(l, -1, k + 1);
949 res->FinalResource = CclGetResourceByName(l);
950 lua_pop(l, 1);
951 } else if (!strcmp(value, "wait-at-resource")) {
952 res->WaitAtResource = LuaToNumber(l, -1, k + 1);
953 } else if (!strcmp(value, "wait-at-depot")) {
954 res->WaitAtDepot = LuaToNumber(l, -1, k + 1);
955 } else if (!strcmp(value, "resource-capacity")) {
956 res->ResourceCapacity = LuaToNumber(l, -1, k + 1);
957 } else if (!strcmp(value, "terrain-harvester")) {
958 res->TerrainHarvester = 1;
959 --k;
960 } else if (!strcmp(value, "lose-resources")) {
961 res->LoseResources = 1;
962 --k;
963 } else if (!strcmp(value, "harvest-from-outside")) {
964 res->HarvestFromOutside = 1;
965 --k;
966 } else if (!strcmp(value, "refinery-harvester")) {
967 res->RefineryHarvester = 1;
968 --k;
969 } else if (!strcmp(value, "file-when-empty")) {
970 res->FileWhenEmpty = LuaToString(l, -1, k + 1);
971 } else if (!strcmp(value, "file-when-loaded")) {
972 res->FileWhenLoaded = LuaToString(l, -1, k + 1);
973 } else {
974 printf("\n%s\n", type->Name.c_str());
975 LuaError(l, "Unsupported tag: %s" _C_ value);
976 }
977 }
978 if (!res->FinalResource) {
979 res->FinalResource = res->ResourceId;
980 }
981 Assert(res->ResourceId);
982 lua_pop(l, 1);
983 }
984 type->BoolFlag[HARVESTER_INDEX].value = 1;
985 } else if (!strcmp(value, "GivesResource")) {
986 lua_pushvalue(l, -1);
987 type->GivesResource = CclGetResourceByName(l);
988 lua_pop(l, 1);
989 } else if (!strcmp(value, "CanStore")) {
990 if (!lua_istable(l, -1)) {
991 LuaError(l, "incorrect argument");
992 }
993 const int subargs = lua_rawlen(l, -1);
994 for (int k = 0; k < subargs; ++k) {
995 lua_rawgeti(l, -1, k + 1);
996 type->CanStore[CclGetResourceByName(l)] = 1;
997 lua_pop(l, 1);
998 }
999 } else if (!strcmp(value, "CanCastSpell")) {
1000 if (!lua_istable(l, -1)) {
1001 LuaError(l, "incorrect argument");
1002 }
1003 //
1004 // Warning: can-cast-spell should only be used AFTER all spells
1005 // have been defined. FIXME: MaxSpellType=500 or something?
1006 //
1007 if (!type->CanCastSpell) {
1008 type->CanCastSpell = new char[SpellTypeTable.size()];
1009 memset(type->CanCastSpell, 0, SpellTypeTable.size() * sizeof(char));
1010 }
1011 const int subargs = lua_rawlen(l, -1);
1012 if (subargs == 0) {
1013 delete[] type->CanCastSpell;
1014 type->CanCastSpell = NULL;
1015 }
1016 for (int k = 0; k < subargs; ++k) {
1017 value = LuaToString(l, -1, k + 1);
1018 const SpellType *spell = SpellTypeByIdent(value);
1019 if (spell == NULL) {
1020 LuaError(l, "Unknown spell type: %s" _C_ value);
1021 }
1022 type->CanCastSpell[spell->Slot] = 1;
1023 }
1024 } else if (!strcmp(value, "AutoCastActive")) {
1025 if (!lua_istable(l, -1)) {
1026 LuaError(l, "incorrect argument");
1027 }
1028 //
1029 // Warning: AutoCastActive should only be used AFTER all spells
1030 // have been defined.
1031 //
1032 if (!type->AutoCastActive) {
1033 type->AutoCastActive = new char[SpellTypeTable.size()];
1034 memset(type->AutoCastActive, 0, SpellTypeTable.size() * sizeof(char));
1035 }
1036 const int subargs = lua_rawlen(l, -1);
1037 if (subargs == 0) {
1038 delete[] type->AutoCastActive;
1039 type->AutoCastActive = NULL;
1040
1041 }
1042 for (int k = 0; k < subargs; ++k) {
1043 value = LuaToString(l, -1, k + 1);
1044 const SpellType *spell = SpellTypeByIdent(value);
1045 if (spell == NULL) {
1046 LuaError(l, "AutoCastActive : Unknown spell type: %s" _C_ value);
1047 }
1048 if (!spell->AutoCast) {
1049 LuaError(l, "AutoCastActive : Define autocast method for %s." _C_ value);
1050 }
1051 type->AutoCastActive[spell->Slot] = 1;
1052 }
1053 } else if (!strcmp(value, "CanTargetFlag")) {
1054 //
1055 // Warning: can-target-flag should only be used AFTER all bool flags
1056 // have been defined.
1057 //
1058 if (!lua_istable(l, -1)) {
1059 LuaError(l, "incorrect argument");
1060 }
1061 if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
1062 type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
1063 }
1064 const int subargs = lua_rawlen(l, -1);
1065 for (int k = 0; k < subargs; ++k) {
1066 value = LuaToString(l, -1, k + 1);
1067 ++k;
1068 int index = UnitTypeVar.BoolFlagNameLookup[value];
1069 if (index != -1) {
1070 value = LuaToString(l, -1, k + 1);
1071 type->BoolFlag[index].CanTargetFlag = Ccl2Condition(l, value);
1072 continue;
1073 }
1074 LuaError(l, "Unsupported flag tag for can-target-flag: %s" _C_ value);
1075 }
1076 } else if (!strcmp(value, "PriorityTarget")) {
1077 //
1078 // Warning: ai-priority-target should only be used AFTER all bool flags
1079 // have been defined.
1080 //
1081 if (!lua_istable(l, -1)) {
1082 LuaError(l, "incorrect argument");
1083 }
1084 if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
1085 type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
1086 }
1087 const int subargs = lua_rawlen(l, -1);
1088 for (int k = 0; k < subargs; ++k) {
1089 value = LuaToString(l, -1, k + 1);
1090 ++k;
1091 int index = UnitTypeVar.BoolFlagNameLookup[value];
1092 if (index != -1) {
1093 value = LuaToString(l, -1, k + 1);
1094 type->BoolFlag[index].AiPriorityTarget = Ccl2Condition(l, value);
1095 continue;
1096 }
1097 LuaError(l, "Unsupported flag tag for ai-priority-target: %s" _C_ value);
1098 }
1099 } else if (!strcmp(value, "Sounds")) {
1100 if (!lua_istable(l, -1)) {
1101 LuaError(l, "incorrect argument");
1102 }
1103 const int subargs = lua_rawlen(l, -1);
1104 for (int k = 0; k < subargs; ++k) {
1105 value = LuaToString(l, -1, k + 1);
1106 ++k;
1107
1108 if (!strcmp(value, "selected")) {
1109 type->Sound.Selected.Name = LuaToString(l, -1, k + 1);
1110 } else if (!strcmp(value, "acknowledge")) {
1111 type->Sound.Acknowledgement.Name = LuaToString(l, -1, k + 1);
1112 } else if (!strcmp(value, "attack")) {
1113 type->Sound.Attack.Name = LuaToString(l, -1, k + 1);
1114 } else if (!strcmp(value, "build")) {
1115 type->Sound.Build.Name = LuaToString(l, -1, k + 1);
1116 } else if (!strcmp(value, "ready")) {
1117 type->Sound.Ready.Name = LuaToString(l, -1, k + 1);
1118 } else if (!strcmp(value, "repair")) {
1119 type->Sound.Repair.Name = LuaToString(l, -1, k + 1);
1120 } else if (!strcmp(value, "harvest")) {
1121 const std::string name = LuaToString(l, -1, k + 1);
1122 ++k;
1123 const int resId = GetResourceIdByName(l, name.c_str());
1124 type->Sound.Harvest[resId].Name = LuaToString(l, -1, k + 1);
1125 } else if (!strcmp(value, "help")) {
1126 type->Sound.Help.Name = LuaToString(l, -1, k + 1);
1127 } else if (!strcmp(value, "work-complete")) {
1128 type->Sound.WorkComplete.Name = LuaToString(l, -1, k + 1);
1129 } else if (!strcmp(value, "dead")) {
1130 int death;
1131
1132 const std::string name = LuaToString(l, -1, k + 1);
1133 for (death = 0; death < ANIMATIONS_DEATHTYPES; ++death) {
1134 if (name == ExtraDeathTypes[death]) {
1135 ++k;
1136 break;
1137 }
1138 }
1139 if (death == ANIMATIONS_DEATHTYPES) {
1140 type->Sound.Dead[ANIMATIONS_DEATHTYPES].Name = name;
1141 } else {
1142 type->Sound.Dead[death].Name = LuaToString(l, -1, k + 1);
1143 }
1144 } else {
1145 LuaError(l, "Unsupported sound tag: %s" _C_ value);
1146 }
1147 }
1148 } else {
1149 int index = UnitTypeVar.VariableNameLookup[value];
1150 if (index != -1) { // valid index
1151 if (lua_isboolean(l, -1)) {
1152 type->DefaultStat.Variables[index].Enable = LuaToBoolean(l, -1);
1153 } else if (lua_istable(l, -1)) {
1154 DefineVariableField(l, type->DefaultStat.Variables + index, -1);
1155 } else if (lua_isnumber(l, -1)) {
1156 type->DefaultStat.Variables[index].Enable = 1;
1157 type->DefaultStat.Variables[index].Value = LuaToNumber(l, -1);
1158 type->DefaultStat.Variables[index].Max = LuaToNumber(l, -1);
1159 } else { // Error
1160 LuaError(l, "incorrect argument for the variable in unittype");
1161 }
1162 continue;
1163 }
1164
1165 if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
1166 type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
1167 }
1168
1169 index = UnitTypeVar.BoolFlagNameLookup[value];
1170 if (index != -1) {
1171 if (lua_isnumber(l, -1)) {
1172 type->BoolFlag[index].value = LuaToNumber(l, -1);
1173 } else {
1174 type->BoolFlag[index].value = LuaToBoolean(l, -1);
1175 }
1176 } else {
1177 printf("\n%s\n", type->Name.c_str());
1178 LuaError(l, "Unsupported tag: %s" _C_ value);
1179 }
1180 }
1181 }
1182
1183 // If number of directions is not specified, make a guess
1184 // Building have 1 direction and units 8
1185 if (type->Building && type->NumDirections == 0) {
1186 type->NumDirections = 1;
1187 } else if (type->NumDirections == 0) {
1188 type->NumDirections = 8;
1189 }
1190
1191 // FIXME: try to simplify/combine the flags instead
1192 if (type->MouseAction == MouseActionAttack && !type->CanAttack) {
1193 LuaError(l, "Unit-type '%s': right-attack is set, but can-attack is not\n" _C_ type->Name.c_str());
1194 }
1195 UpdateDefaultBoolFlags(*type);
1196 if (!CclInConfigFile) {
1197 UpdateUnitStats(*type, 1);
1198 }
1199 return 0;
1200 }
1201
1202 /**
1203 ** <b>Description</b>
1204 **
1205 ** Parse unit-stats.
1206 **
1207 ** @param l Lua state.
1208 **
1209 ** Example:
1210 **
1211 ** <div class="example"><code><strong>DefineUnitStats</strong>("unit-berserker", 2, {
1212 ** "HitPoints", {Value = 55, Max = 55, Increase = 0, Enable = true},
1213 ** "AttackRange", {Value = 5, Max = 6, Increase = 0, Enable = true},
1214 ** "SightRange", {Value = 7, Max = 7, Increase = 0, Enable = true},
1215 ** })</code></div>
1216 */
CclDefineUnitStats(lua_State * l)1217 static int CclDefineUnitStats(lua_State *l)
1218 {
1219 CUnitType *type = UnitTypeByIdent(LuaToString(l, 1));
1220 const int playerId = LuaToNumber(l, 2);
1221
1222 Assert(type);
1223 Assert(playerId < PlayerMax);
1224
1225 CUnitStats *stats = &type->Stats[playerId];
1226 if (!stats->Variables) {
1227 stats->Variables = new CVariable[UnitTypeVar.GetNumberVariable()];
1228 }
1229
1230 // Parse the list: (still everything could be changed!)
1231 const int args = lua_rawlen(l, 3);
1232 for (int j = 0; j < args; ++j) {
1233 const char *value = LuaToString(l, 3, j + 1);
1234 ++j;
1235
1236 if (!strcmp(value, "costs")) {
1237 lua_rawgeti(l, 3, j + 1);
1238 if (!lua_istable(l, -1)) {
1239 LuaError(l, "incorrect argument");
1240 }
1241 const int subargs = lua_rawlen(l, -1);
1242
1243 for (int k = 0; k < subargs; ++k) {
1244 lua_rawgeti(l, 3, j + 1);
1245 value = LuaToString(l, -1, k + 1);
1246 ++k;
1247 const int resId = GetResourceIdByName(l, value);
1248 stats->Costs[resId] = LuaToNumber(l, -1, k + 1);
1249 lua_pop(l, 1);
1250 }
1251 } else if (!strcmp(value, "storing")) {
1252 lua_rawgeti(l, 3, j + 1);
1253 if (!lua_istable(l, -1)) {
1254 LuaError(l, "incorrect argument");
1255 }
1256 const int subargs = lua_rawlen(l, -1);
1257
1258 for (int k = 0; k < subargs; ++k) {
1259 lua_rawgeti(l, 3, j + 1);
1260 value = LuaToString(l, -1, k + 1);
1261 ++k;
1262 const int resId = GetResourceIdByName(l, value);
1263 stats->Storing[resId] = LuaToNumber(l, -1, k + 1);
1264 lua_pop(l, 1);
1265 }
1266 } else if (!strcmp(value, "improve-production")) {
1267 lua_rawgeti(l, 3, j + 1);
1268 if (!lua_istable(l, -1)) {
1269 LuaError(l, "incorrect argument");
1270 }
1271 const int subargs = lua_rawlen(l, -1);
1272
1273 for (int k = 0; k < subargs; ++k) {
1274 lua_rawgeti(l, 3, j + 1);
1275 value = LuaToString(l, -1, k + 1);
1276 ++k;
1277 const int resId = GetResourceIdByName(l, value);
1278 stats->ImproveIncomes[resId] = LuaToNumber(l, -1, k + 1);
1279 lua_pop(l, 1);
1280 }
1281 } else {
1282 int i = UnitTypeVar.VariableNameLookup[value];// User variables
1283 if (i != -1) { // valid index
1284 lua_rawgeti(l, 3, j + 1);
1285 if (lua_istable(l, -1)) {
1286 DefineVariableField(l, stats->Variables + i, -1);
1287 } else if (lua_isnumber(l, -1)) {
1288 stats->Variables[i].Enable = 1;
1289 stats->Variables[i].Value = LuaToNumber(l, -1);
1290 stats->Variables[i].Max = LuaToNumber(l, -1);
1291 } else { // Error
1292 LuaError(l, "incorrect argument for the variable in unittype");
1293 }
1294 continue;
1295 }
1296 // This leaves a half initialized unit
1297 LuaError(l, "Unsupported tag: %s" _C_ value);
1298 }
1299 }
1300 return 0;
1301 }
1302
1303 // ----------------------------------------------------------------------------
1304
1305 /**
1306 ** Access unit-type object
1307 **
1308 ** @param l Lua state.
1309 */
CclGetUnitType(lua_State * l)1310 CUnitType *CclGetUnitType(lua_State *l)
1311 {
1312 // Be kind allow also strings or symbols
1313 if (lua_isstring(l, -1)) {
1314 const char *str = LuaToString(l, -1);
1315 return UnitTypeByIdent(str);
1316 } else if (lua_isuserdata(l, -1)) {
1317 LuaUserData *data = (LuaUserData *)lua_touserdata(l, -1);
1318 if (data->Type == LuaUnitType) {
1319 return (CUnitType *)data->Data;
1320 }
1321 }
1322 LuaError(l, "CclGetUnitType: not a unit-type");
1323 return NULL;
1324 }
1325
1326 /**
1327 ** Get unit-type structure.
1328 **
1329 ** @param l Lua state.
1330 **
1331 ** @return Unit-type structure.
1332 */
CclUnitType(lua_State * l)1333 static int CclUnitType(lua_State *l)
1334 {
1335 LuaCheckArgs(l, 1);
1336
1337 const char *str = LuaToString(l, 1);
1338 CUnitType *type = UnitTypeByIdent(str);
1339 LuaUserData *data = (LuaUserData *)lua_newuserdata(l, sizeof(LuaUserData));
1340 data->Type = LuaUnitType;
1341 data->Data = type;
1342 return 1;
1343 }
1344
1345 /**
1346 ** Get all unit-type structures.
1347 **
1348 ** @param l Lua state.
1349 **
1350 ** @return An array of all unit-type structures.
1351 */
CclUnitTypeArray(lua_State * l)1352 static int CclUnitTypeArray(lua_State *l)
1353 {
1354 LuaCheckArgs(l, 0);
1355
1356 lua_newtable(l);
1357
1358 for (std::vector<CUnitType *>::size_type i = 0; i < UnitTypes.size(); ++i) {
1359 LuaUserData *data = (LuaUserData *)lua_newuserdata(l, sizeof(LuaUserData));
1360 data->Type = LuaUnitType;
1361 data->Data = UnitTypes[i];
1362 lua_rawseti(l, 1, i + 1);
1363 }
1364 return 1;
1365 }
1366
1367 /**
1368 ** Get the ident of the unit-type structure.
1369 **
1370 ** @param l Lua state.
1371 **
1372 ** @return The identifier of the unit-type.
1373 */
CclGetUnitTypeIdent(lua_State * l)1374 static int CclGetUnitTypeIdent(lua_State *l)
1375 {
1376 LuaCheckArgs(l, 1);
1377
1378 const CUnitType *type = CclGetUnitType(l);
1379 if (type) {
1380 lua_pushstring(l, type->Ident.c_str());
1381 } else {
1382 LuaError(l, "unit '%s' not defined" _C_ LuaToString(l, -1));
1383 }
1384 return 1;
1385 }
1386
1387 /**
1388 ** <b>Description</b>
1389 **
1390 ** Get the name of the unit-type structure.
1391 **
1392 ** @param l Lua state.
1393 **
1394 ** @return The name of the unit-type.
1395 **
1396 ** Example:
1397 **
1398 ** <div class="example"><code>name = <strong>GetUnitTypeName</strong>("unit-knight")
1399 ** print(name)</code></div>
1400 */
CclGetUnitTypeName(lua_State * l)1401 static int CclGetUnitTypeName(lua_State *l)
1402 {
1403 LuaCheckArgs(l, 1);
1404
1405 const CUnitType *type = CclGetUnitType(l);
1406 lua_pushstring(l, type->Name.c_str());
1407 return 1;
1408 }
1409
1410 /**
1411 ** <b>Description</b>
1412 **
1413 ** Set the name of the unit-type structure.
1414 **
1415 ** @param l Lua state.
1416 **
1417 ** @return The name of the unit-type.
1418 **
1419 ** Example:
1420 **
1421 ** <div class="example"><code><strong>SetUnitTypeName</strong>("unit-beast-cry","Doomhammer")</code></div>
1422 */
CclSetUnitTypeName(lua_State * l)1423 static int CclSetUnitTypeName(lua_State *l)
1424 {
1425 LuaCheckArgs(l, 2);
1426
1427 lua_pushvalue(l, 1);
1428 CUnitType *type = CclGetUnitType(l);
1429 lua_pop(l, 1);
1430 type->Name = LuaToString(l, 2);
1431
1432 lua_pushvalue(l, 2);
1433 return 1;
1434 }
1435
1436 /**
1437 ** <b>Description</b>
1438 **
1439 ** Get unit type data.
1440 **
1441 ** @param l Lua state.
1442 **
1443 ** Example:
1444 **
1445 ** <div class="example"><code>-- Get the amount of supply from Human Farms
1446 ** supply = <strong>GetUnitTypeData</strong>("unit-farm","Supply")
1447 ** print(supply)</code></div>
1448 */
CclGetUnitTypeData(lua_State * l)1449 static int CclGetUnitTypeData(lua_State *l)
1450 {
1451 if (lua_gettop(l) < 2) {
1452 LuaError(l, "incorrect argument");
1453 }
1454 lua_pushvalue(l, 1);
1455 const CUnitType *type = CclGetUnitType(l);
1456 lua_pop(l, 1);
1457 const char *data = LuaToString(l, 2);
1458
1459 if (!strcmp(data, "Name")) {
1460 lua_pushstring(l, type->Name.c_str());
1461 return 1;
1462 } else if (!strcmp(data, "Icon")) {
1463 lua_pushstring(l, type->Icon.Name.c_str());
1464 return 1;
1465 } else if (!strcmp(data, "Costs")) {
1466 LuaCheckArgs(l, 3);
1467 const std::string res = LuaToString(l, 3);
1468 const int resId = GetResourceIdByName(l, res.c_str());
1469 if (!GameRunning && Editor.Running != EditorEditing) {
1470 lua_pushnumber(l, type->DefaultStat.Costs[resId]);
1471 } else {
1472 lua_pushnumber(l, type->MapDefaultStat.Costs[resId]);
1473 }
1474 return 1;
1475 } else if (!strcmp(data, "ImproveProduction")) {
1476 LuaCheckArgs(l, 3);
1477 const std::string res = LuaToString(l, 3);
1478 const int resId = GetResourceIdByName(l, res.c_str());
1479 if (!GameRunning && Editor.Running != EditorEditing) {
1480 lua_pushnumber(l, type->DefaultStat.ImproveIncomes[resId]);
1481 } else {
1482 lua_pushnumber(l, type->MapDefaultStat.ImproveIncomes[resId]);
1483 }
1484 return 1;
1485 } else if (!strcmp(data, "DrawLevel")) {
1486 lua_pushnumber(l, type->DrawLevel);
1487 return 1;
1488 } else if (!strcmp(data, "TileWidth")) {
1489 lua_pushnumber(l, type->TileWidth);
1490 return 1;
1491 } else if (!strcmp(data, "TileHeight")) {
1492 lua_pushnumber(l, type->TileHeight);
1493 return 1;
1494 } else if (!strcmp(data, "ComputerReactionRange")) {
1495 lua_pushnumber(l, type->ReactRangeComputer);
1496 return 1;
1497 } else if (!strcmp(data, "PersonReactionRange")) {
1498 lua_pushnumber(l, type->ReactRangePerson);
1499 return 1;
1500 } else if (!strcmp(data, "Missile")) {
1501 lua_pushstring(l, type->Missile.Name.c_str());
1502 return 1;
1503 } else if (!strcmp(data, "MinAttackRange")) {
1504 lua_pushnumber(l, type->MinAttackRange);
1505 return 1;
1506 } else if (!strcmp(data, "MaxAttackRange")) {
1507 if (!GameRunning && Editor.Running != EditorEditing) {
1508 lua_pushnumber(l, type->DefaultStat.Variables[ATTACKRANGE_INDEX].Value);
1509 } else {
1510 lua_pushnumber(l, type->MapDefaultStat.Variables[ATTACKRANGE_INDEX].Value);
1511 }
1512 return 1;
1513 } else if (!strcmp(data, "Priority")) {
1514 if (!GameRunning && Editor.Running != EditorEditing) {
1515 lua_pushnumber(l, type->DefaultStat.Variables[PRIORITY_INDEX].Value);
1516 } else {
1517 lua_pushnumber(l, type->MapDefaultStat.Variables[PRIORITY_INDEX].Value);
1518 }
1519 return 1;
1520 } else if (!strcmp(data, "Type")) {
1521 if (type->UnitType == UnitTypeLand) {
1522 lua_pushstring(l, "land");
1523 return 1;
1524 } else if (type->UnitType == UnitTypeFly) {
1525 lua_pushstring(l, "fly");
1526 return 1;
1527 } else if (type->UnitType == UnitTypeNaval) {
1528 lua_pushstring(l, "naval");
1529 return 1;
1530 }
1531 } else if (!strcmp(data, "Corpse")) {
1532 lua_pushstring(l, type->CorpseName.c_str());
1533 return 1;
1534 } else if (!strcmp(data, "CanAttack")) {
1535 lua_pushboolean(l, type->CanAttack);
1536 return 1;
1537 } else if (!strcmp(data, "Building")) {
1538 lua_pushboolean(l, type->Building);
1539 return 1;
1540 } else if (!strcmp(data, "LandUnit")) {
1541 lua_pushboolean(l, type->LandUnit);
1542 return 1;
1543 } else if (!strcmp(data, "GivesResource")) {
1544 if (type->GivesResource > 0) {
1545 lua_pushstring(l, DefaultResourceNames[type->GivesResource].c_str());
1546 return 1;
1547 } else {
1548 lua_pushstring(l, "");
1549 return 1;
1550 }
1551 } else if (!strcmp(data, "Sounds")) {
1552 LuaCheckArgs(l, 3);
1553 const std::string sound_type = LuaToString(l, 3);
1554 if (sound_type == "selected") {
1555 if (!GameRunning && Editor.Running != EditorEditing) {
1556 lua_pushstring(l, type->Sound.Selected.Name.c_str());
1557 } else {
1558 lua_pushstring(l, type->MapSound.Selected.Name.c_str());
1559 }
1560 } else if (sound_type == "acknowledge") {
1561 if (!GameRunning && Editor.Running != EditorEditing) {
1562 lua_pushstring(l, type->Sound.Acknowledgement.Name.c_str());
1563 } else {
1564 lua_pushstring(l, type->MapSound.Acknowledgement.Name.c_str());
1565 }
1566 } else if (sound_type == "attack") {
1567 if (!GameRunning && Editor.Running != EditorEditing) {
1568 lua_pushstring(l, type->Sound.Attack.Name.c_str());
1569 } else {
1570 lua_pushstring(l, type->MapSound.Attack.Name.c_str());
1571 }
1572 } else if (sound_type == "build") {
1573 if (!GameRunning && Editor.Running != EditorEditing) {
1574 lua_pushstring(l, type->Sound.Build.Name.c_str());
1575 } else {
1576 lua_pushstring(l, type->MapSound.Build.Name.c_str());
1577 }
1578 } else if (sound_type == "ready") {
1579 if (!GameRunning && Editor.Running != EditorEditing) {
1580 lua_pushstring(l, type->Sound.Ready.Name.c_str());
1581 } else {
1582 lua_pushstring(l, type->MapSound.Ready.Name.c_str());
1583 }
1584 } else if (sound_type == "repair") {
1585 if (!GameRunning && Editor.Running != EditorEditing) {
1586 lua_pushstring(l, type->Sound.Repair.Name.c_str());
1587 } else {
1588 lua_pushstring(l, type->MapSound.Repair.Name.c_str());
1589 }
1590 } else if (sound_type == "harvest") {
1591 LuaCheckArgs(l, 4);
1592 const std::string sound_subtype = LuaToString(l, 4);
1593 const int resId = GetResourceIdByName(sound_subtype.c_str());
1594 if (!GameRunning && Editor.Running != EditorEditing) {
1595 lua_pushstring(l, type->Sound.Harvest[resId].Name.c_str());
1596 } else {
1597 lua_pushstring(l, type->MapSound.Harvest[resId].Name.c_str());
1598 }
1599 } else if (sound_type == "help") {
1600 if (!GameRunning && Editor.Running != EditorEditing) {
1601 lua_pushstring(l, type->Sound.Help.Name.c_str());
1602 } else {
1603 lua_pushstring(l, type->MapSound.Help.Name.c_str());
1604 }
1605 } else if (sound_type == "dead") {
1606 if (lua_gettop(l) < 4) {
1607 if (!GameRunning && Editor.Running != EditorEditing) {
1608 lua_pushstring(l, type->Sound.Dead[ANIMATIONS_DEATHTYPES].Name.c_str());
1609 } else {
1610 lua_pushstring(l, type->MapSound.Dead[ANIMATIONS_DEATHTYPES].Name.c_str());
1611 }
1612 } else {
1613 int death;
1614 const std::string sound_subtype = LuaToString(l, 4);
1615
1616 for (death = 0; death < ANIMATIONS_DEATHTYPES; ++death) {
1617 if (sound_subtype == ExtraDeathTypes[death]) {
1618 break;
1619 }
1620 }
1621 if (death == ANIMATIONS_DEATHTYPES) {
1622 if (!GameRunning && Editor.Running != EditorEditing) {
1623 lua_pushstring(l, type->Sound.Dead[ANIMATIONS_DEATHTYPES].Name.c_str());
1624 } else {
1625 lua_pushstring(l, type->MapSound.Dead[ANIMATIONS_DEATHTYPES].Name.c_str());
1626 }
1627 } else {
1628 if (!GameRunning && Editor.Running != EditorEditing) {
1629 lua_pushstring(l, type->Sound.Dead[death].Name.c_str());
1630 } else {
1631 lua_pushstring(l, type->MapSound.Dead[death].Name.c_str());
1632 }
1633 }
1634 }
1635 }
1636 return 1;
1637 } else {
1638 int index = UnitTypeVar.VariableNameLookup[data];
1639 if (index != -1) { // valid index
1640 if (!GameRunning && Editor.Running != EditorEditing) {
1641 lua_pushnumber(l, type->DefaultStat.Variables[index].Value);
1642 } else {
1643 lua_pushnumber(l, type->MapDefaultStat.Variables[index].Value);
1644 }
1645 return 1;
1646 }
1647
1648 index = UnitTypeVar.BoolFlagNameLookup[data];
1649 if (index != -1) {
1650 lua_pushboolean(l, type->BoolFlag[index].value);
1651 return 1;
1652 } else {
1653 LuaError(l, "Invalid field: %s" _C_ data);
1654 }
1655 }
1656
1657 return 0;
1658 }
1659
1660 // ----------------------------------------------------------------------------
1661
1662 /**
1663 ** Define the field of the UserDefined variables.
1664 **
1665 ** @param l Lua state.
1666 ** @param var Variable to set.
1667 ** @param lua_index Index of the table where are the infos
1668 **
1669 ** @internal Use to not duplicate code.
1670 */
DefineVariableField(lua_State * l,CVariable * var,int lua_index)1671 void DefineVariableField(lua_State *l, CVariable *var, int lua_index)
1672 {
1673 if (lua_index < 0) { // relative index
1674 --lua_index;
1675 }
1676 lua_pushnil(l);
1677 while (lua_next(l, lua_index)) {
1678 const char *key = LuaToString(l, -2);
1679
1680 if (!strcmp(key, "Value")) {
1681 var->Value = LuaToNumber(l, -1);
1682 } else if (!strcmp(key, "Max")) {
1683 var->Max = LuaToNumber(l, -1);
1684 } else if (!strcmp(key, "Increase")) {
1685 var->Increase = LuaToNumber(l, -1);
1686 } else if (!strcmp(key, "IncreaseFrequency")) {
1687 int value = LuaToNumber(l, -1);
1688 var->IncreaseFrequency = value;
1689 if (var->IncreaseFrequency != value) {
1690 LuaError(l, "IncreaseFrequency out of range!");
1691 }
1692 } else if (!strcmp(key, "Enable")) {
1693 var->Enable = LuaToBoolean(l, -1);
1694 } else { // Error.
1695 LuaError(l, "incorrect field '%s' for variable\n" _C_ key);
1696 }
1697 lua_pop(l, 1); // pop the value;
1698 }
1699 }
1700
1701 /**
1702 ** Define user variables.
1703 **
1704 ** @param l Lua state.
1705 */
CclDefineVariables(lua_State * l)1706 static int CclDefineVariables(lua_State *l)
1707 {
1708 int old = UnitTypeVar.GetNumberVariable();
1709
1710 const int args = lua_gettop(l);
1711 for (int j = 0; j < args; ++j) {
1712 const char *str = LuaToString(l, j + 1);
1713
1714 const int index = UnitTypeVar.VariableNameLookup.AddKey(str);
1715 if (index == old) {
1716 old++;
1717 UnitTypeVar.Variable.resize(old);
1718 } else {
1719 DebugPrint("Warning, User Variable \"%s\" redefined\n" _C_ str);
1720 }
1721 if (!lua_istable(l, j + 2)) { // No change => default value.
1722 continue;
1723 }
1724 ++j;
1725 DefineVariableField(l, &(UnitTypeVar.Variable[index]), j + 1);
1726 }
1727 return 0;
1728 }
1729
1730 /**
1731 ** Define boolean flag.
1732 **
1733 ** @param l Lua state.
1734 */
CclDefineBoolFlags(lua_State * l)1735 static int CclDefineBoolFlags(lua_State *l)
1736 {
1737 const unsigned int old = UnitTypeVar.GetNumberBoolFlag();
1738 const int args = lua_gettop(l);
1739 for (int j = 0; j < args; ++j) {
1740 const char *str = LuaToString(l, j + 1);
1741
1742 UnitTypeVar.BoolFlagNameLookup.AddKey(str);
1743
1744 }
1745
1746 if (0 < old && old != UnitTypeVar.GetNumberBoolFlag()) {
1747 size_t new_size = UnitTypeVar.GetNumberBoolFlag();
1748 for (std::vector<CUnitType *>::size_type i = 0; i < UnitTypes.size(); ++i) { // adjust array for unit already defined
1749 UnitTypes[i]->BoolFlag.resize(new_size);
1750 }
1751 }
1752 return 0;
1753 }
1754
1755 /**
1756 ** Define Decorations for user variables
1757 **
1758 ** @param l Lua state.
1759 **
1760 ** @todo modify Assert with luastate with User Error.
1761 ** @todo continue to add configuration.
1762 */
CclDefineDecorations(lua_State * l)1763 static int CclDefineDecorations(lua_State *l)
1764 {
1765 struct {
1766 int Index;
1767 int OffsetX;
1768 int OffsetY;
1769 int OffsetXPercent;
1770 int OffsetYPercent;
1771 bool IsCenteredInX;
1772 bool IsCenteredInY;
1773 bool ShowIfNotEnable;
1774 bool ShowWhenNull;
1775 bool HideHalf;
1776 bool ShowWhenMax;
1777 bool ShowOnlySelected;
1778 bool HideNeutral;
1779 bool HideAllied;
1780 bool ShowOpponent;
1781 } tmp;
1782
1783 const int nargs = lua_gettop(l);
1784 for (int i = 0; i < nargs; i++) {
1785 Assert(lua_istable(l, i + 1));
1786 CDecoVar *decovar = NULL;
1787 memset(&tmp, 0, sizeof(tmp));
1788 lua_pushnil(l);
1789 while (lua_next(l, i + 1)) {
1790 const char *key = LuaToString(l, -2);
1791 if (!strcmp(key, "Index")) {
1792 const char *const value = LuaToString(l, -1);
1793 tmp.Index = UnitTypeVar.VariableNameLookup[value];// User variables
1794 Assert(tmp.Index != -1);
1795 } else if (!strcmp(key, "Offset")) {
1796 CclGetPos(l, &tmp.OffsetX, &tmp.OffsetY);
1797 } else if (!strcmp(key, "OffsetPercent")) {
1798 CclGetPos(l, &tmp.OffsetXPercent, &tmp.OffsetYPercent);
1799 } else if (!strcmp(key, "CenterX")) {
1800 tmp.IsCenteredInX = LuaToBoolean(l, -1);
1801 } else if (!strcmp(key, "CenterY")) {
1802 tmp.IsCenteredInY = LuaToBoolean(l, -1);
1803 } else if (!strcmp(key, "ShowIfNotEnable")) {
1804 tmp.ShowIfNotEnable = LuaToBoolean(l, -1);
1805 } else if (!strcmp(key, "ShowWhenNull")) {
1806 tmp.ShowWhenNull = LuaToBoolean(l, -1);
1807 } else if (!strcmp(key, "HideHalf")) {
1808 tmp.HideHalf = LuaToBoolean(l, -1);
1809 } else if (!strcmp(key, "ShowWhenMax")) {
1810 tmp.ShowWhenMax = LuaToBoolean(l, -1);
1811 } else if (!strcmp(key, "ShowOnlySelected")) {
1812 tmp.ShowOnlySelected = LuaToBoolean(l, -1);
1813 } else if (!strcmp(key, "HideNeutral")) {
1814 tmp.HideNeutral = LuaToBoolean(l, -1);
1815 } else if (!strcmp(key, "HideAllied")) {
1816 tmp.HideAllied = LuaToBoolean(l, -1);
1817 } else if (!strcmp(key, "ShowOpponent")) {
1818 tmp.ShowOpponent = LuaToBoolean(l, -1);
1819 } else if (!strcmp(key, "Method")) {
1820 Assert(lua_istable(l, -1));
1821 lua_rawgeti(l, -1, 1); // MethodName
1822 lua_rawgeti(l, -2, 2); // Data
1823 Assert(lua_istable(l, -1));
1824 key = LuaToString(l, -2);
1825 if (!strcmp(key, "bar")) {
1826 CDecoVarBar *decovarbar = new CDecoVarBar;
1827 lua_pushnil(l);
1828 while (lua_next(l, -2)) {
1829 key = LuaToString(l, -2);
1830 if (!strcmp(key, "Height")) {
1831 decovarbar->Height = LuaToNumber(l, -1);
1832 } else if (!strcmp(key, "Width")) {
1833 decovarbar->Width = LuaToNumber(l, -1);
1834 } else if (!strcmp(key, "Orientation")) {
1835 key = LuaToString(l, -1);
1836 if (!strcmp(key, "horizontal")) {
1837 decovarbar->IsVertical = 0;
1838 } else if (!strcmp(key, "vertical")) {
1839 decovarbar->IsVertical = 1;
1840 } else { // Error
1841 LuaError(l, "invalid Orientation '%s' for bar in DefineDecorations" _C_ key);
1842 }
1843 } else if (!strcmp(key, "SEToNW")) {
1844 decovarbar->SEToNW = LuaToBoolean(l, -1);
1845 } else if (!strcmp(key, "BorderSize")) {
1846 decovarbar->BorderSize = LuaToNumber(l, -1);
1847 } else if (!strcmp(key, "ShowFullBackground")) {
1848 decovarbar->ShowFullBackground = LuaToBoolean(l, -1);
1849 #if 0 // FIXME Color configuration
1850 } else if (!strcmp(key, "Color")) {
1851 decovar->Color = // FIXME
1852 } else if (!strcmp(key, "BColor")) {
1853 decovar->BColor = // FIXME
1854 #endif
1855 } else {
1856 LuaError(l, "'%s' invalid for Method bar" _C_ key);
1857 }
1858 lua_pop(l, 1); // Pop value
1859 }
1860 decovar = decovarbar;
1861 } else if (!strcmp(key, "frame")) {
1862 CDecoVarFrame *frame = new CDecoVarFrame;
1863 if (!lua_istable(l, -1)) {
1864 LuaError(l, "incorrect argument, need table with Thickness= and Color=");
1865 }
1866 for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
1867 const char* innerkey = LuaToString(l, -2);
1868 if (!strcmp(innerkey, "Thickness")) {
1869 frame->Thickness = LuaToNumber(l, -1);
1870 } else if (!strcmp(innerkey, "ColorName")) {
1871 const char *const colorName = LuaToString(l, -1);
1872 frame->ColorIndex = GetColorIndexByName(colorName);
1873 } else {
1874 LuaError(l, "'%s' invalid for Method frame" _C_ innerkey);
1875 }
1876 }
1877 decovar = frame;
1878 } else if (!strcmp(key, "text")) {
1879 CDecoVarText *decovartext = new CDecoVarText;
1880
1881 decovartext->Font = CFont::Get(LuaToString(l, -1, 1));
1882 // FIXME : More arguments ? color...
1883 decovar = decovartext;
1884 } else if (!strcmp(key, "sprite")) {
1885 CDecoVarSpriteBar *decovarspritebar = new CDecoVarSpriteBar;
1886 decovarspritebar->NSprite = GetSpriteIndex(LuaToString(l, -1, 1));
1887 if (decovarspritebar->NSprite == -1) {
1888 LuaError(l, "invalid sprite-name '%s' for Method in DefineDecorations" _C_ LuaToString(l, -1, 1));
1889 }
1890 // FIXME : More arguments ?
1891 decovar = decovarspritebar;
1892 } else if (!strcmp(key, "static-sprite")) {
1893 CDecoVarStaticSprite *decovarstaticsprite = new CDecoVarStaticSprite;
1894 if (lua_rawlen(l, -1) == 2) {
1895 decovarstaticsprite->NSprite = GetSpriteIndex(LuaToString(l, -1, 1));
1896 decovarstaticsprite->n = LuaToNumber(l, -1, 2);
1897 } else {
1898 decovarstaticsprite->NSprite = GetSpriteIndex(LuaToString(l, -1, 1));
1899 decovarstaticsprite->n = LuaToNumber(l, -1, 2);
1900 decovarstaticsprite->FadeValue = LuaToNumber(l, -1, 3);
1901 }
1902 decovar = decovarstaticsprite;
1903 } else { // Error
1904 LuaError(l, "invalid method '%s' for Method in DefineDecorations" _C_ key);
1905 }
1906 lua_pop(l, 2); // MethodName and data
1907 } else { // Error
1908 LuaError(l, "invalid key '%s' for DefineDecorations" _C_ key);
1909 }
1910 lua_pop(l, 1); // Pop the value
1911 }
1912 decovar->Index = tmp.Index;
1913 decovar->OffsetX = tmp.OffsetX;
1914 decovar->OffsetY = tmp.OffsetY;
1915 decovar->OffsetXPercent = tmp.OffsetXPercent;
1916 decovar->OffsetYPercent = tmp.OffsetYPercent;
1917 decovar->IsCenteredInX = tmp.IsCenteredInX;
1918 decovar->IsCenteredInY = tmp.IsCenteredInY;
1919 decovar->ShowIfNotEnable = tmp.ShowIfNotEnable;
1920 decovar->ShowWhenNull = tmp.ShowWhenNull;
1921 decovar->HideHalf = tmp.HideHalf;
1922 decovar->ShowWhenMax = tmp.ShowWhenMax;
1923 decovar->ShowOnlySelected = tmp.ShowOnlySelected;
1924 decovar->HideNeutral = tmp.HideNeutral;
1925 decovar->HideAllied = tmp.HideAllied;
1926 decovar->ShowOpponent = tmp.ShowOpponent;
1927 UnitTypeVar.DecoVar.push_back(decovar);
1928 }
1929 Assert(lua_gettop(l));
1930 return 0;
1931 }
1932
1933 /**
1934 ** Define default extra death types.
1935 **
1936 ** @param l Lua state.
1937 */
CclDefineExtraDeathTypes(lua_State * l)1938 static int CclDefineExtraDeathTypes(lua_State *l)
1939 {
1940 unsigned int args;
1941
1942 for (unsigned int i = 0; i < ANIMATIONS_DEATHTYPES; ++i) {
1943 ExtraDeathTypes[i].clear();
1944 }
1945 args = lua_gettop(l);
1946 for (unsigned int i = 0; i < ANIMATIONS_DEATHTYPES && i < args; ++i) {
1947 ExtraDeathTypes[i] = LuaToString(l, i + 1);
1948 }
1949 return 0;
1950 }
1951 // ----------------------------------------------------------------------------
1952
1953 /**
1954 ** Update unit variables which are not user defined.
1955 */
UpdateUnitVariables(CUnit & unit)1956 void UpdateUnitVariables(CUnit &unit)
1957 {
1958 const CUnitType *type = unit.Type;
1959
1960 for (int i = 0; i < NVARALREADYDEFINED; i++) { // default values
1961 if (i == ARMOR_INDEX || i == PIERCINGDAMAGE_INDEX || i == BASICDAMAGE_INDEX
1962 || i == SUPPLY_INDEX || i == DEMAND_INDEX
1963 || i == MANA_INDEX || i == KILL_INDEX || i == XP_INDEX || i == GIVERESOURCE_INDEX
1964 || i == BLOODLUST_INDEX || i == HASTE_INDEX || i == SLOW_INDEX
1965 || i == INVISIBLE_INDEX || i == UNHOLYARMOR_INDEX || i == HP_INDEX
1966 || i == SHIELD_INDEX || i == POINTS_INDEX || i == MAXHARVESTERS_INDEX
1967 || i == POISON_INDEX || i == SHIELDPERMEABILITY_INDEX || i == SHIELDPIERCING_INDEX
1968 || i == ISALIVE_INDEX || i == PLAYER_INDEX) {
1969 continue;
1970 }
1971 unit.Variable[i].Value = 0;
1972 unit.Variable[i].Max = 0;
1973 unit.Variable[i].Enable = 1;
1974 }
1975
1976 // Shield permeability
1977 unit.Variable[SHIELDPERMEABILITY_INDEX].Max = 100;
1978
1979 // Transport
1980 unit.Variable[TRANSPORT_INDEX].Value = unit.BoardCount;
1981 unit.Variable[TRANSPORT_INDEX].Max = unit.Type->MaxOnBoard;
1982
1983 unit.CurrentOrder()->UpdateUnitVariables(unit);
1984
1985 // Resources.
1986 if (unit.Type->GivesResource) {
1987 unit.Variable[GIVERESOURCE_INDEX].Value = unit.ResourcesHeld;
1988 unit.Variable[GIVERESOURCE_INDEX].Max = unit.ResourcesHeld > unit.Variable[GIVERESOURCE_INDEX].Max ? 0x7FFFFFFF : unit.Variable[GIVERESOURCE_INDEX].Max;
1989 }
1990 if (unit.Type->BoolFlag[HARVESTER_INDEX].value && unit.CurrentResource) {
1991 unit.Variable[CARRYRESOURCE_INDEX].Value = unit.ResourcesHeld;
1992 unit.Variable[CARRYRESOURCE_INDEX].Max = unit.Type->ResInfo[unit.CurrentResource]->ResourceCapacity;
1993 }
1994
1995 // SightRange
1996 unit.Variable[SIGHTRANGE_INDEX].Value = type->MapDefaultStat.Variables[SIGHTRANGE_INDEX].Value;
1997 unit.Variable[SIGHTRANGE_INDEX].Max = unit.Stats->Variables[SIGHTRANGE_INDEX].Max;
1998
1999 // AttackRange
2000 unit.Variable[ATTACKRANGE_INDEX].Value = type->MapDefaultStat.Variables[ATTACKRANGE_INDEX].Max;
2001 unit.Variable[ATTACKRANGE_INDEX].Max = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
2002
2003 // Priority
2004 unit.Variable[PRIORITY_INDEX].Value = type->MapDefaultStat.Variables[PRIORITY_INDEX].Max;
2005 unit.Variable[PRIORITY_INDEX].Max = unit.Stats->Variables[PRIORITY_INDEX].Max;
2006
2007 // Position
2008 unit.Variable[POSX_INDEX].Value = unit.tilePos.x;
2009 unit.Variable[POSX_INDEX].Max = Map.Info.MapWidth;
2010 unit.Variable[POSY_INDEX].Value = unit.tilePos.y;
2011 unit.Variable[POSY_INDEX].Max = Map.Info.MapHeight;
2012
2013 // Target Position
2014 const Vec2i goalPos = unit.CurrentOrder()->GetGoalPos();
2015 unit.Variable[TARGETPOSX_INDEX].Value = goalPos.x;
2016 unit.Variable[TARGETPOSX_INDEX].Max = Map.Info.MapWidth;
2017 unit.Variable[TARGETPOSY_INDEX].Value = goalPos.y;
2018 unit.Variable[TARGETPOSY_INDEX].Max = Map.Info.MapHeight;
2019
2020 // RadarRange
2021 unit.Variable[RADAR_INDEX].Value = unit.Stats->Variables[RADAR_INDEX].Value;
2022 unit.Variable[RADAR_INDEX].Max = unit.Stats->Variables[RADAR_INDEX].Value;
2023
2024 // RadarJammerRange
2025 unit.Variable[RADARJAMMER_INDEX].Value = unit.Stats->Variables[RADARJAMMER_INDEX].Value;
2026 unit.Variable[RADARJAMMER_INDEX].Max = unit.Stats->Variables[RADARJAMMER_INDEX].Value;
2027
2028 // SlotNumber
2029 unit.Variable[SLOT_INDEX].Value = UnitNumber(unit);
2030 unit.Variable[SLOT_INDEX].Max = UnitManager.GetUsedSlotCount();
2031
2032 // Is Alive
2033 unit.Variable[ISALIVE_INDEX].Value = unit.IsAlive() ? 1 : 0;
2034 unit.Variable[ISALIVE_INDEX].Max = 1;
2035
2036 // Player
2037 unit.Variable[PLAYER_INDEX].Value = unit.Player->Index;
2038 unit.Variable[PLAYER_INDEX].Max = PlayerMax;
2039
2040 for (int i = 0; i < NVARALREADYDEFINED; i++) { // default values
2041 unit.Variable[i].Enable &= unit.Variable[i].Max > 0;
2042 if (unit.Variable[i].Value > unit.Variable[i].Max) {
2043 DebugPrint("Value out of range: '%s'(%d), for variable '%s',"
2044 " value = %d, max = %d\n"
2045 _C_ type->Ident.c_str() _C_ UnitNumber(unit) _C_ UnitTypeVar.VariableNameLookup[i]
2046 _C_ unit.Variable[i].Value _C_ unit.Variable[i].Max);
2047 clamp(&unit.Variable[i].Value, 0, unit.Variable[i].Max);
2048 }
2049 }
2050 }
2051
2052 /**
2053 ** Set the map default stat for a unit type
2054 **
2055 ** @param ident Unit type ident
2056 ** @param variable_key Key of the desired variable
2057 ** @param value Value to set to
2058 ** @param variable_type Type to be modified (i.e. "Value", "Max", etc.); alternatively, resource type if variable_key equals "Costs"
2059 */
SetMapStat(std::string ident,std::string variable_key,int value,std::string variable_type)2060 void SetMapStat(std::string ident, std::string variable_key, int value, std::string variable_type)
2061 {
2062 CUnitType *type = UnitTypeByIdent(ident.c_str());
2063
2064 if (variable_key == "Costs") {
2065 const int resId = GetResourceIdByName(variable_type.c_str());
2066 type->MapDefaultStat.Costs[resId] = value;
2067 for (int player = 0; player < PlayerMax; ++player) {
2068 type->Stats[player].Costs[resId] = type->MapDefaultStat.Costs[resId];
2069 }
2070 } else if (variable_key == "ImproveProduction") {
2071 const int resId = GetResourceIdByName(variable_type.c_str());
2072 type->MapDefaultStat.ImproveIncomes[resId] = value;
2073 for (int player = 0; player < PlayerMax; ++player) {
2074 type->Stats[player].ImproveIncomes[resId] = type->MapDefaultStat.ImproveIncomes[resId];
2075 }
2076 } else {
2077 int variable_index = UnitTypeVar.VariableNameLookup[variable_key.c_str()];
2078 if (variable_index != -1) { // valid index
2079 if (variable_type == "Value") {
2080 type->MapDefaultStat.Variables[variable_index].Value = value;
2081 for (int player = 0; player < PlayerMax; ++player) {
2082 type->Stats[player].Variables[variable_index].Value = type->MapDefaultStat.Variables[variable_index].Value;
2083 }
2084 } else if (variable_type == "Max") {
2085 type->MapDefaultStat.Variables[variable_index].Max = value;
2086 for (int player = 0; player < PlayerMax; ++player) {
2087 type->Stats[player].Variables[variable_index].Max = type->MapDefaultStat.Variables[variable_index].Max;
2088 }
2089 } else if (variable_type == "Increase") {
2090 type->MapDefaultStat.Variables[variable_index].Increase = value;
2091 for (int player = 0; player < PlayerMax; ++player) {
2092 type->Stats[player].Variables[variable_index].Increase = type->MapDefaultStat.Variables[variable_index].Increase;
2093 }
2094 } else if (variable_type == "IncreaseFrequency") {
2095 type->MapDefaultStat.Variables[variable_index].IncreaseFrequency = value;
2096 // TODO: error
2097 // if (type->MapDefaultStat.Variables[variable_index].IncreaseFrequency != value) {
2098 // LuaError(l, "%s.IncreaseFrequency out of range!" _C_ variable_key.c_str());
2099 // }
2100 for (int player = 0; player < PlayerMax; ++player) {
2101 type->Stats[player].Variables[variable_index].IncreaseFrequency = type->MapDefaultStat.Variables[variable_index].IncreaseFrequency;
2102 }
2103 } else if (variable_type == "Enable") {
2104 type->MapDefaultStat.Variables[variable_index].Enable = value;
2105 for (int player = 0; player < PlayerMax; ++player) {
2106 type->Stats[player].Variables[variable_index].Enable = type->MapDefaultStat.Variables[variable_index].Enable;
2107 }
2108 } else {
2109 fprintf(stderr, "Invalid type: %s\n", variable_type.c_str());
2110 return;
2111 }
2112 } else {
2113 fprintf(stderr, "Invalid variable: %s\n", variable_key.c_str());
2114 return;
2115 }
2116 }
2117 }
2118
2119 /**
2120 ** Set the map sound for a unit type
2121 **
2122 ** @param ident Unit type ident
2123 ** @param sound_type Type of the sound
2124 ** @param sound The sound to be set for that type
2125 */
SetMapSound(std::string ident,std::string sound,std::string sound_type,std::string sound_subtype)2126 void SetMapSound(std::string ident, std::string sound, std::string sound_type, std::string sound_subtype)
2127 {
2128 if (sound.empty()) {
2129 return;
2130 }
2131 CUnitType *type = UnitTypeByIdent(ident.c_str());
2132
2133 if (sound_type == "selected") {
2134 type->MapSound.Selected.Name = sound;
2135 } else if (sound_type == "acknowledge") {
2136 type->MapSound.Acknowledgement.Name = sound;
2137 } else if (sound_type == "attack") {
2138 type->MapSound.Attack.Name = sound;
2139 } else if (sound_type == "build") {
2140 type->MapSound.Build.Name = sound;
2141 } else if (sound_type == "ready") {
2142 type->MapSound.Ready.Name = sound;
2143 } else if (sound_type == "repair") {
2144 type->MapSound.Repair.Name = sound;
2145 } else if (sound_type == "harvest") {
2146 const int resId = GetResourceIdByName(sound_subtype.c_str());
2147 type->MapSound.Harvest[resId].Name = sound;
2148 } else if (sound_type == "help") {
2149 type->MapSound.Help.Name = sound;
2150 } else if (sound_type == "dead") {
2151 int death;
2152
2153 for (death = 0; death < ANIMATIONS_DEATHTYPES; ++death) {
2154 if (sound_subtype == ExtraDeathTypes[death]) {
2155 break;
2156 }
2157 }
2158 if (death == ANIMATIONS_DEATHTYPES) {
2159 type->MapSound.Dead[ANIMATIONS_DEATHTYPES].Name = sound;
2160 } else {
2161 type->MapSound.Dead[death].Name = sound;
2162 }
2163 }
2164 }
2165
2166 /**
2167 ** Register CCL features for unit-type.
2168 */
UnitTypeCclRegister()2169 void UnitTypeCclRegister()
2170 {
2171 lua_register(Lua, "DefineUnitType", CclDefineUnitType);
2172 lua_register(Lua, "DefineUnitStats", CclDefineUnitStats);
2173 lua_register(Lua, "DefineBoolFlags", CclDefineBoolFlags);
2174 lua_register(Lua, "DefineVariables", CclDefineVariables);
2175 lua_register(Lua, "DefineDecorations", CclDefineDecorations);
2176
2177 lua_register(Lua, "DefineExtraDeathTypes", CclDefineExtraDeathTypes);
2178
2179 UnitTypeVar.Init();
2180
2181 lua_register(Lua, "UnitType", CclUnitType);
2182 lua_register(Lua, "UnitTypeArray", CclUnitTypeArray);
2183 // unit type structure access
2184 lua_register(Lua, "GetUnitTypeIdent", CclGetUnitTypeIdent);
2185 lua_register(Lua, "GetUnitTypeName", CclGetUnitTypeName);
2186 lua_register(Lua, "SetUnitTypeName", CclSetUnitTypeName);
2187 lua_register(Lua, "GetUnitTypeData", CclGetUnitTypeData);
2188 }
2189
2190 //@}
2191