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