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-2019 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 "unit/unittype.h"
39 
40 #include "actions.h"
41 #include "animation.h"
42 //Wyrmgus start
43 #include "character.h" //for updating levels
44 //Wyrmgus end
45 #include "civilization.h"
46 #include "construct.h"
47 #include "editor.h"
48 #include "font.h"
49 #include "luacallback.h"
50 #include "map/map.h"
51 #include "map/map_layer.h"
52 #include "map/terrain_type.h"
53 #include "map/tileset.h"
54 //Wyrmgus start
55 #include "network.h" //for updating levels
56 //Wyrmgus end
57 #include "plane.h"
58 #include "player.h"
59 //Wyrmgus start
60 #include "province.h"
61 #include "quest.h" //for updating levels
62 //Wyrmgus end
63 #include "script.h"
64 #include "sound.h"
65 #include "spells.h"
66 #include "time/season.h"
67 #include "ui/button_action.h"
68 #include "ui/button_level.h"
69 #include "ui/ui.h"
70 #include "unit/unit.h"
71 #include "unit/unit_manager.h"
72 #include "unit/unit_type_variation.h"
73 #include "unitsound.h"
74 //Wyrmgus start
75 #include "upgrade/upgrade.h"
76 //Wyrmgus end
77 #include "video.h"
78 #include "world.h"
79 
80 /*----------------------------------------------------------------------------
81 --  Variables
82 ----------------------------------------------------------------------------*/
83 
84 CUnitTypeVar UnitTypeVar;    /// Variables for UnitType and unit.
85 
86 // names of boolflags
87 static const char COWARD_KEY[] = "Coward";
88 static const char BUILDING_KEY[] = "Building";
89 static const char FLIP_KEY[] = "Flip";
90 static const char REVEALER_KEY[] = "Revealer";
91 static const char LANDUNIT_KEY[] = "LandUnit";
92 static const char AIRUNIT_KEY[] = "AirUnit";
93 static const char SEAUNIT_KEY[] = "SeaUnit";
94 static const char EXPLODEWHENKILLED_KEY[] = "ExplodeWhenKilled";
95 static const char VISIBLEUNDERFOG_KEY[] = "VisibleUnderFog";
96 static const char PERMANENTCLOAK_KEY[] = "PermanentCloak";
97 static const char DETECTCLOAK_KEY[] = "DetectCloak";
98 static const char ATTACKFROMTRANSPORTER_KEY[] = "AttackFromTransporter";
99 static const char VANISHES_KEY[] = "Vanishes";
100 static const char GROUNDATTACK_KEY[] = "GroundAttack";
101 static const char SHOREBUILDING_KEY[] = "ShoreBuilding";
102 static const char CANATTACK_KEY[] = "CanAttack";
103 //Wyrmgus start
104 static const char CANDOCK_KEY[] = "CanDock";
105 //Wyrmgus end
106 static const char BUILDEROUTSIDE_KEY[] = "BuilderOutside";
107 static const char BUILDERLOST_KEY[] = "BuilderLost";
108 static const char CANHARVEST_KEY[] = "CanHarvest";
109 static const char INEXHAUSTIBLE_KEY[] = "Inexhaustible";
110 static const char HARVESTER_KEY[] = "Harvester";
111 static const char SELECTABLEBYRECTANGLE_KEY[] = "SelectableByRectangle";
112 static const char ISNOTSELECTABLE_KEY[] = "IsNotSelectable";
113 static const char DECORATION_KEY[] = "Decoration";
114 static const char INDESTRUCTIBLE_KEY[] = "Indestructible";
115 static const char TELEPORTER_KEY[] = "Teleporter";
116 static const char SHIELDPIERCE_KEY[] = "ShieldPiercing";
117 static const char SAVECARGO_KEY[] = "SaveCargo";
118 static const char NONSOLID_KEY[] = "NonSolid";
119 static const char WALL_KEY[] = "Wall";
120 static const char NORANDOMPLACING_KEY[] = "NoRandomPlacing";
121 static const char ORGANIC_KEY[] = "Organic";
122 static const char SIDEATTACK_KEY[] = "SideAttack";
123 static const char NOFRIENDLYFIRE_KEY[] = "NoFriendlyFire";
124 //Wyrmgus start
125 static const char TOWNHALL_KEY[] = "TownHall";
126 static const char MARKET_KEY[] = "Market";
127 static const char RECRUITHEROES_KEY[] = "RecruitHeroes";
128 static const char GARRISONTRAINING_KEY[] = "GarrisonTraining";
129 static const char INCREASESLUXURYDEMAND_KEY[] = "IncreasesLuxuryDemand";
130 static const char ITEM_KEY[] = "Item";
131 static const char POWERUP_KEY[] = "PowerUp";
132 static const char INVENTORY_KEY[] = "Inventory";
133 static const char TRAP_KEY[] = "Trap";
134 static const char BRIDGE_KEY[] = "Bridge";
135 static const char TRADER_KEY[] = "Trader";
136 static const char FAUNA_KEY[] = "Fauna";
137 static const char PREDATOR_KEY[] = "Predator";
138 static const char SLIME_KEY[] = "Slime";
139 static const char PEOPLEAVERSION_KEY[] = "PeopleAversion";
140 static const char MOUNTED_KEY[] = "Mounted";
141 static const char RAIL_KEY[] = "Rail";
142 static const char DIMINUTIVE_KEY[] = "Diminutive";
143 static const char GIANT_KEY[] = "Giant";
144 static const char DRAGON_KEY[] = "Dragon";
145 static const char DETRITUS_KEY[] = "Detritus";
146 static const char FLESH_KEY[] = "Flesh";
147 static const char VEGETABLE_KEY[] = "Vegetable";
148 static const char INSECT_KEY[] = "Insect";
149 static const char DAIRY_KEY[] = "Dairy";
150 static const char DETRITIVORE_KEY[] = "Detritivore";
151 static const char CARNIVORE_KEY[] = "Carnivore";
152 static const char HERBIVORE_KEY[] = "Herbivore";
153 static const char INSECTIVORE_KEY[] = "Insectivore";
154 static const char HARVESTFROMOUTSIDE_KEY[] = "HarvestFromOutside";
155 static const char OBSTACLE_KEY[] = "Obstacle";
156 static const char AIRUNPASSABLE_KEY[] = "AirUnpassable";
157 static const char SLOWS_KEY[] = "Slows";
158 static const char GRAVEL_KEY[] = "Gravel";
159 static const char HACKDAMAGE_KEY[] = "HackDamage";
160 static const char PIERCEDAMAGE_KEY[] = "PierceDamage";
161 static const char BLUNTDAMAGE_KEY[] = "BluntDamage";
162 static const char ETHEREAL_KEY[] = "Ethereal";
163 static const char HIDDENOWNERSHIP_KEY[] = "HiddenOwnership";
164 static const char HIDDENINEDITOR_KEY[] = "HiddenInEditor";
165 static const char INVERTEDSOUTHEASTARMS_KEY[] = "InvertedSoutheastArms";
166 static const char INVERTEDEASTARMS_KEY[] = "InvertedEastArms";
167 //Wyrmgus end
168 
169 // names of the variable.
170 static const char HITPOINTS_KEY[] = "HitPoints";
171 static const char BUILD_KEY[] = "Build";
172 static const char MANA_KEY[] = "Mana";
173 static const char TRANSPORT_KEY[] = "Transport";
174 static const char RESEARCH_KEY[] = "Research";
175 static const char TRAINING_KEY[] = "Training";
176 static const char UPGRADETO_KEY[] = "UpgradeTo";
177 static const char GIVERESOURCE_KEY[] = "GiveResource";
178 static const char CARRYRESOURCE_KEY[] = "CarryResource";
179 static const char XP_KEY[] = "Xp";
180 static const char KILL_KEY[] = "Kill";
181 static const char SUPPLY_KEY[] = "Supply";
182 static const char DEMAND_KEY[] = "Demand";
183 static const char ARMOR_KEY[] = "Armor";
184 static const char SIGHTRANGE_KEY[] = "SightRange";
185 static const char ATTACKRANGE_KEY[] = "AttackRange";
186 static const char PIERCINGDAMAGE_KEY[] = "PiercingDamage";
187 static const char BASICDAMAGE_KEY[] = "BasicDamage";
188 //Wyrmgus start
189 static const char THORNSDAMAGE_KEY[] = "ThornsDamage";
190 static const char FIREDAMAGE_KEY[] = "FireDamage";
191 static const char COLDDAMAGE_KEY[] = "ColdDamage";
192 static const char ARCANEDAMAGE_KEY[] = "ArcaneDamage";
193 static const char LIGHTNINGDAMAGE_KEY[] = "LightningDamage";
194 static const char AIRDAMAGE_KEY[] = "AirDamage";
195 static const char EARTHDAMAGE_KEY[] = "EarthDamage";
196 static const char WATERDAMAGE_KEY[] = "WaterDamage";
197 static const char ACIDDAMAGE_KEY[] = "AcidDamage";
198 static const char SPEED_KEY[] = "Speed";
199 static const char FIRERESISTANCE_KEY[] = "FireResistance";
200 static const char COLDRESISTANCE_KEY[] = "ColdResistance";
201 static const char ARCANERESISTANCE_KEY[] = "ArcaneResistance";
202 static const char LIGHTNINGRESISTANCE_KEY[] = "LightningResistance";
203 static const char AIRRESISTANCE_KEY[] = "AirResistance";
204 static const char EARTHRESISTANCE_KEY[] = "EarthResistance";
205 static const char WATERRESISTANCE_KEY[] = "WaterResistance";
206 static const char ACIDRESISTANCE_KEY[] = "AcidResistance";
207 static const char HACKRESISTANCE_KEY[] = "HackResistance";
208 static const char PIERCERESISTANCE_KEY[] = "PierceResistance";
209 static const char BLUNTRESISTANCE_KEY[] = "BluntResistance";
210 static const char DEHYDRATIONIMMUNITY_KEY[] = "DehydrationImmunity";
211 //Wyrmgus end
212 static const char POSX_KEY[] = "PosX";
213 static const char POSY_KEY[] = "PosY";
214 static const char TARGETPOSX_KEY[] = "TargetPosX";
215 static const char TARGETPOSY_KEY[] = "TargetPosY";
216 static const char RADARRANGE_KEY[] = "RadarRange";
217 static const char RADARJAMMERRANGE_KEY[] = "RadarJammerRange";
218 static const char AUTOREPAIRRANGE_KEY[] = "AutoRepairRange";
219 static const char BLOODLUST_KEY[] = "Bloodlust";
220 static const char HASTE_KEY[] = "Haste";
221 static const char SLOW_KEY[] = "Slow";
222 static const char INVISIBLE_KEY[] = "Invisible";
223 static const char UNHOLYARMOR_KEY[] = "UnholyArmor";
224 static const char SLOT_KEY[] = "Slot";
225 static const char SHIELD_KEY[] = "ShieldPoints";
226 static const char POINTS_KEY[] = "Points";
227 static const char MAXHARVESTERS_KEY[] = "MaxHarvesters";
228 static const char POISON_KEY[] = "Poison";
229 static const char SHIELDPERMEABILITY_KEY[] = "ShieldPermeability";
230 static const char SHIELDPIERCING_KEY[] = "ShieldPiercing";
231 static const char ISALIVE_KEY[] = "IsAlive";
232 static const char PLAYER_KEY[] = "Player";
233 static const char PRIORITY_KEY[] = "Priority";
234 //Wyrmgus start
235 static const char STRENGTH_KEY[] = "Strength";
236 static const char DEXTERITY_KEY[] = "Dexterity";
237 static const char INTELLIGENCE_KEY[] = "Intelligence";
238 static const char CHARISMA_KEY[] = "Charisma";
239 static const char ACCURACY_KEY[] = "Accuracy";
240 static const char EVASION_KEY[] = "Evasion";
241 static const char LEVEL_KEY[] = "Level";
242 static const char LEVELUP_KEY[] = "LevelUp";
243 static const char XPREQUIRED_KEY[] = "XPRequired";
244 static const char VARIATION_KEY[] = "Variation";
245 static const char HITPOINTHEALING_KEY[] = "HitPointHealing";
246 static const char HITPOINTBONUS_KEY[] = "HitPointBonus";
247 static const char CRITICALSTRIKECHANCE_KEY[] = "CriticalStrikeChance";
248 static const char CHARGEBONUS_KEY[] = "ChargeBonus";
249 static const char BACKSTAB_KEY[] = "Backstab";
250 static const char BONUSAGAINSTMOUNTED_KEY[] = "BonusAgainstMounted";
251 static const char BONUSAGAINSTBUILDINGS_KEY[] = "BonusAgainstBuildings";
252 static const char BONUSAGAINSTAIR_KEY[] = "BonusAgainstAir";
253 static const char BONUSAGAINSTGIANTS_KEY[] = "BonusAgainstGiants";
254 static const char BONUSAGAINSTDRAGONS_KEY[] = "BonusAgainstDragons";
255 static const char DAYSIGHTRANGEBONUS_KEY[] = "DaySightRangeBonus";
256 static const char NIGHTSIGHTRANGEBONUS_KEY[] = "NightSightRangeBonus";
257 static const char KNOWLEDGEMAGIC_KEY[] = "KnowledgeMagic";
258 static const char KNOWLEDGEWARFARE_KEY[] = "KnowledgeWarfare";
259 static const char KNOWLEDGEMINING_KEY[] = "KnowledgeMining";
260 static const char MAGICLEVEL_KEY[] = "MagicLevel";
261 static const char TRANSPARENCY_KEY[] = "Transparency";
262 static const char GENDER_KEY[] = "Gender";
263 static const char BIRTHCYCLE_KEY[] = "BirthCycle";
264 static const char STUN_KEY[] = "Stun";
265 static const char BLEEDING_KEY[] = "Bleeding";
266 static const char LEADERSHIP_KEY[] = "Leadership";
267 static const char BLESSING_KEY[] = "Blessing";
268 static const char INSPIRE_KEY[] = "Inspire";
269 static const char PRECISION_KEY[] = "Precision";
270 static const char REGENERATION_KEY[] = "Regeneration";
271 static const char BARKSKIN_KEY[] = "Barkskin";
272 static const char TERROR_KEY[] = "Terror";
273 static const char WITHER_KEY[] = "Wither";
274 static const char DEHYDRATION_KEY[] = "Dehydration";
275 static const char HYDRATING_KEY[] = "Hydrating";
276 static const char TIMEEFFICIENCYBONUS_KEY[] = "TimeEfficiencyBonus";
277 static const char RESEARCHSPEEDBONUS_KEY[] = "ResearchSpeedBonus";
278 static const char GARRISONEDRANGEBONUS_KEY[] = "GarrisonedRangeBonus";
279 static const char SPEEDBONUS_KEY[] = "SpeedBonus";
280 static const char GATHERINGBONUS_KEY[] = "GatheringBonus";
281 static const char COPPERGATHERINGBONUS_KEY[] = "CopperGatheringBonus";
282 static const char SILVERGATHERINGBONUS_KEY[] = "SilverGatheringBonus";
283 static const char GOLDGATHERINGBONUS_KEY[] = "GoldGatheringBonus";
284 static const char IRONGATHERINGBONUS_KEY[] = "IronGatheringBonus";
285 static const char MITHRILGATHERINGBONUS_KEY[] = "MithrilGatheringBonus";
286 static const char LUMBERGATHERINGBONUS_KEY[] = "LumberGatheringBonus";
287 static const char STONEGATHERINGBONUS_KEY[] = "StoneGatheringBonus";
288 static const char COALGATHERINGBONUS_KEY[] = "CoalGatheringBonus";
289 static const char JEWELRYGATHERINGBONUS_KEY[] = "JewelryGatheringBonus";
290 static const char FURNITUREGATHERINGBONUS_KEY[] = "FurnitureGatheringBonus";
291 static const char LEATHERGATHERINGBONUS_KEY[] = "LeatherGatheringBonus";
292 static const char GEMSGATHERINGBONUS_KEY[] = "GemsGatheringBonus";
293 static const char DISEMBARKMENTBONUS_KEY[] = "DisembarkmentBonus";
294 static const char TRADECOST_KEY[] = "TradeCost";
295 static const char SALVAGEFACTOR_KEY[] = "SalvageFactor";
296 static const char MUGGING_KEY[] = "Mugging";
297 static const char RAIDING_KEY[] = "Raiding";
298 static const char DESERTSTALK_KEY[] = "Desertstalk";
299 static const char FORESTSTALK_KEY[] = "Foreststalk";
300 static const char SWAMPSTALK_KEY[] = "Swampstalk";
301 static const char LEADERSHIPAURA_KEY[] = "LeadershipAura";
302 static const char REGENERATIONAURA_KEY[] = "RegenerationAura";
303 static const char HYDRATINGAURA_KEY[] = "HydratingAura";
304 static const char ETHEREALVISION_KEY[] = "EtherealVision";
305 static const char HERO_KEY[] = "Hero";
306 static const char OWNERSHIPINFLUENCERANGE_KEY[] = "OwnershipInfluenceRange";
307 //Wyrmgus end
308 
309 /*----------------------------------------------------------------------------
310 --  Functions
311 ----------------------------------------------------------------------------*/
312 
CBoolKeys()313 CUnitTypeVar::CBoolKeys::CBoolKeys()
314 {
315 
316 	const char *const tmp[] = {COWARD_KEY, BUILDING_KEY, FLIP_KEY, REVEALER_KEY,
317 							   LANDUNIT_KEY, AIRUNIT_KEY, SEAUNIT_KEY, EXPLODEWHENKILLED_KEY,
318 							   VISIBLEUNDERFOG_KEY, PERMANENTCLOAK_KEY, DETECTCLOAK_KEY,
319 							   ATTACKFROMTRANSPORTER_KEY, VANISHES_KEY, GROUNDATTACK_KEY,
320 							   //Wyrmgus start
321 //							   SHOREBUILDING_KEY, CANATTACK_KEY, BUILDEROUTSIDE_KEY,
322 							   SHOREBUILDING_KEY, CANATTACK_KEY, CANDOCK_KEY, BUILDEROUTSIDE_KEY,
323 							   //Wyrmgus end
324 							   BUILDERLOST_KEY, CANHARVEST_KEY, INEXHAUSTIBLE_KEY, HARVESTER_KEY, SELECTABLEBYRECTANGLE_KEY,
325 							   ISNOTSELECTABLE_KEY, DECORATION_KEY, INDESTRUCTIBLE_KEY, TELEPORTER_KEY, SHIELDPIERCE_KEY,
326 							   //Wyrmgus start
327 //							   SAVECARGO_KEY, NONSOLID_KEY, WALL_KEY, NORANDOMPLACING_KEY, ORGANIC_KEY
328 							   SAVECARGO_KEY, NONSOLID_KEY, WALL_KEY, NORANDOMPLACING_KEY, ORGANIC_KEY, SIDEATTACK_KEY, NOFRIENDLYFIRE_KEY,
329 							   TOWNHALL_KEY, MARKET_KEY, RECRUITHEROES_KEY, GARRISONTRAINING_KEY, INCREASESLUXURYDEMAND_KEY, ITEM_KEY, POWERUP_KEY, INVENTORY_KEY, TRAP_KEY, BRIDGE_KEY,
330 							   TRADER_KEY,
331 							   FAUNA_KEY, PREDATOR_KEY, SLIME_KEY, PEOPLEAVERSION_KEY, MOUNTED_KEY, RAIL_KEY, DIMINUTIVE_KEY, GIANT_KEY, DRAGON_KEY,
332 							   DETRITUS_KEY, FLESH_KEY, VEGETABLE_KEY, INSECT_KEY, DAIRY_KEY,
333 							   DETRITIVORE_KEY, CARNIVORE_KEY, HERBIVORE_KEY, INSECTIVORE_KEY,
334 							   HARVESTFROMOUTSIDE_KEY, OBSTACLE_KEY, AIRUNPASSABLE_KEY, SLOWS_KEY, GRAVEL_KEY,
335 							   HACKDAMAGE_KEY, PIERCEDAMAGE_KEY, BLUNTDAMAGE_KEY,
336 							   ETHEREAL_KEY, HIDDENOWNERSHIP_KEY, HIDDENINEDITOR_KEY, INVERTEDSOUTHEASTARMS_KEY, INVERTEDEASTARMS_KEY
337 							   //Wyrmgus end
338 							  };
339 
340 	for (int i = 0; i < NBARALREADYDEFINED; ++i) {
341 		buildin[i].offset = i;
342 		buildin[i].keylen = strlen(tmp[i]);
343 		buildin[i].key = tmp[i];
344 	}
345 	Init();
346 }
347 
CVariableKeys()348 CUnitTypeVar::CVariableKeys::CVariableKeys()
349 {
350 
351 	const char *const tmp[] = {HITPOINTS_KEY, BUILD_KEY, MANA_KEY, TRANSPORT_KEY,
352 							   RESEARCH_KEY, TRAINING_KEY, UPGRADETO_KEY, GIVERESOURCE_KEY,
353 							   CARRYRESOURCE_KEY, XP_KEY, KILL_KEY,	SUPPLY_KEY, DEMAND_KEY, ARMOR_KEY,
354 							   SIGHTRANGE_KEY, ATTACKRANGE_KEY, PIERCINGDAMAGE_KEY,
355 							   //Wyrmgus start
356 //							   BASICDAMAGE_KEY, POSX_KEY, POSY_KEY, TARGETPOSX_KEY, TARGETPOSY_KEY, RADARRANGE_KEY,
357 							   BASICDAMAGE_KEY, THORNSDAMAGE_KEY,
358 							   FIREDAMAGE_KEY, COLDDAMAGE_KEY, ARCANEDAMAGE_KEY, LIGHTNINGDAMAGE_KEY, AIRDAMAGE_KEY, EARTHDAMAGE_KEY, WATERDAMAGE_KEY, ACIDDAMAGE_KEY,
359 							   SPEED_KEY,
360 							   FIRERESISTANCE_KEY, COLDRESISTANCE_KEY, ARCANERESISTANCE_KEY, LIGHTNINGRESISTANCE_KEY, AIRRESISTANCE_KEY, EARTHRESISTANCE_KEY, WATERRESISTANCE_KEY, ACIDRESISTANCE_KEY,
361 							   HACKRESISTANCE_KEY, PIERCERESISTANCE_KEY, BLUNTRESISTANCE_KEY, DEHYDRATIONIMMUNITY_KEY,
362 							   POSX_KEY, POSY_KEY, TARGETPOSX_KEY, TARGETPOSY_KEY, RADARRANGE_KEY,
363 							   //Wyrmgus end
364 							   RADARJAMMERRANGE_KEY, AUTOREPAIRRANGE_KEY, BLOODLUST_KEY, HASTE_KEY,
365 							   SLOW_KEY, INVISIBLE_KEY, UNHOLYARMOR_KEY, SLOT_KEY, SHIELD_KEY, POINTS_KEY,
366 							   MAXHARVESTERS_KEY, POISON_KEY, SHIELDPERMEABILITY_KEY, SHIELDPIERCING_KEY, ISALIVE_KEY, PLAYER_KEY,
367 //Wyrmgus
368 //							   PRIORITY_KEY
369 							   PRIORITY_KEY,
370 							   STRENGTH_KEY, DEXTERITY_KEY, INTELLIGENCE_KEY, CHARISMA_KEY,
371 							   ACCURACY_KEY, EVASION_KEY, LEVEL_KEY, LEVELUP_KEY, XPREQUIRED_KEY, VARIATION_KEY, HITPOINTHEALING_KEY, HITPOINTBONUS_KEY, CRITICALSTRIKECHANCE_KEY,
372 							   CHARGEBONUS_KEY, BACKSTAB_KEY, BONUSAGAINSTMOUNTED_KEY, BONUSAGAINSTBUILDINGS_KEY, BONUSAGAINSTAIR_KEY, BONUSAGAINSTGIANTS_KEY,
373 							   BONUSAGAINSTDRAGONS_KEY,
374 							   DAYSIGHTRANGEBONUS_KEY, NIGHTSIGHTRANGEBONUS_KEY,
375 							   KNOWLEDGEMAGIC_KEY, KNOWLEDGEWARFARE_KEY, KNOWLEDGEMINING_KEY,
376 							   MAGICLEVEL_KEY, TRANSPARENCY_KEY, GENDER_KEY, BIRTHCYCLE_KEY,
377 							   STUN_KEY, BLEEDING_KEY, LEADERSHIP_KEY, BLESSING_KEY, INSPIRE_KEY, PRECISION_KEY, REGENERATION_KEY, BARKSKIN_KEY, TERROR_KEY, WITHER_KEY, DEHYDRATION_KEY, HYDRATING_KEY,
378 							   TIMEEFFICIENCYBONUS_KEY, RESEARCHSPEEDBONUS_KEY, GARRISONEDRANGEBONUS_KEY, SPEEDBONUS_KEY,
379 							   GATHERINGBONUS_KEY, COPPERGATHERINGBONUS_KEY, SILVERGATHERINGBONUS_KEY, GOLDGATHERINGBONUS_KEY, IRONGATHERINGBONUS_KEY, MITHRILGATHERINGBONUS_KEY, LUMBERGATHERINGBONUS_KEY, STONEGATHERINGBONUS_KEY, COALGATHERINGBONUS_KEY, JEWELRYGATHERINGBONUS_KEY, FURNITUREGATHERINGBONUS_KEY, LEATHERGATHERINGBONUS_KEY, GEMSGATHERINGBONUS_KEY,
380 							   DISEMBARKMENTBONUS_KEY, TRADECOST_KEY, SALVAGEFACTOR_KEY, MUGGING_KEY, RAIDING_KEY,
381 							   DESERTSTALK_KEY, FORESTSTALK_KEY, SWAMPSTALK_KEY,
382 							   LEADERSHIPAURA_KEY, REGENERATIONAURA_KEY, HYDRATINGAURA_KEY, ETHEREALVISION_KEY, HERO_KEY, OWNERSHIPINFLUENCERANGE_KEY
383 //Wyrmgus end
384 							  };
385 
386 	for (int i = 0; i < NVARALREADYDEFINED; ++i) {
387 		buildin[i].offset = i;
388 		buildin[i].keylen = strlen(tmp[i]);
389 		buildin[i].key = tmp[i];
390 	}
391 	Init();
392 }
393 
394 int GetSpriteIndex(const char *SpriteName);
395 
396 /**
397 **  Get the resource ID from a SCM object.
398 **
399 **  @param l  Lua state.
400 **
401 **  @return   the resource id
402 */
CclGetResourceByName(lua_State * l)403 unsigned CclGetResourceByName(lua_State *l)
404 {
405 	const char *const tmp = LuaToString(l, -1);
406 	const std::string value = tmp ? tmp : "";
407 	const int resId = GetResourceIdByName(l, value.c_str());
408 
409 	return resId;
410 }
411 
412 
413 /**
414 **  Find the index of a extra death type
415 */
ExtraDeathIndex(const char * death)416 int ExtraDeathIndex(const char *death)
417 {
418 	for (unsigned int det = 0; det < ANIMATIONS_DEATHTYPES; ++det) {
419 		if (!strcmp(death, ExtraDeathTypes[det].c_str())) {
420 			return det;
421 		}
422 	}
423 	return ANIMATIONS_DEATHTYPES;
424 }
425 
426 /**
427 **  Parse BuildingRules
428 **
429 **  @param l      Lua state.
430 **  @param blist  BuildingRestriction to fill in
431 */
ParseBuildingRules(lua_State * l,std::vector<CBuildRestriction * > & blist)432 static void ParseBuildingRules(lua_State *l, std::vector<CBuildRestriction *> &blist)
433 {
434 	//Wyrmgus start
435 //	CBuildRestrictionAnd *andlist = new CBuildRestrictionAnd();
436 	//Wyrmgus end
437 
438 	const int args = lua_rawlen(l, -1);
439 	Assert(!(args & 1)); // must be even
440 
441 	for (int i = 0; i < args; ++i) {
442 		const char *value = LuaToString(l, -1, i + 1);
443 		++i;
444 		lua_rawgeti(l, -1, i + 1);
445 		if (!lua_istable(l, -1)) {
446 			LuaError(l, "incorrect argument");
447 		}
448 		if (!strcmp(value, "distance")) {
449 			CBuildRestrictionDistance *b = new CBuildRestrictionDistance;
450 
451 			for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
452 				value = LuaToString(l, -2);
453 				if (!strcmp(value, "Distance")) {
454 					b->Distance = LuaToNumber(l, -1);
455 				} else if (!strcmp(value, "DistanceType")) {
456 					value = LuaToString(l, -1);
457 					if (value[0] == '=') {
458 						if ((value[1] == '=' && value[2] == '\0') || (value[1] == '\0')) {
459 							b->DistanceType = Equal;
460 						}
461 					} else if (value[0] == '>') {
462 						if (value[1] == '=' && value[2] == '\0') {
463 							b->DistanceType = GreaterThanEqual;
464 						} else if (value[1] == '\0') {
465 							b->DistanceType = GreaterThan;
466 						}
467 					} else if (value[0] == '<') {
468 						if (value[1] == '=' && value[2] == '\0') {
469 							b->DistanceType = LessThanEqual;
470 						} else if (value[1] == '\0') {
471 							b->DistanceType = LessThan;
472 						}
473 					} else if (value[0] == '!' && value[1] == '=' && value[2] == '\0') {
474 						b->DistanceType = NotEqual;
475 					}
476 				} else if (!strcmp(value, "Type")) {
477 					b->RestrictTypeName = LuaToString(l, -1);
478 				} else if (!strcmp(value, "Class")) {
479 					b->RestrictClassName = LuaToString(l, -1);
480 				} else if (!strcmp(value, "Owner")) {
481 					b->RestrictTypeOwner = LuaToString(l, -1);
482 				} else if (!strcmp(value, "CheckBuilder")) {
483 					b->CheckBuilder = LuaToBoolean(l, -1);
484 				} else if (!strcmp(value, "Diagonal")) {
485 					b->Diagonal = LuaToBoolean(l, -1);
486 				} else {
487 					LuaError(l, "Unsupported BuildingRules distance tag: %s" _C_ value);
488 				}
489 			}
490 			//Wyrmgus start
491 //			andlist->push_back(b);
492 			blist.push_back(b);
493 			//Wyrmgus end
494 		} else if (!strcmp(value, "addon")) {
495 			CBuildRestrictionAddOn *b = new CBuildRestrictionAddOn;
496 
497 			for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
498 				value = LuaToString(l, -2);
499 				if (!strcmp(value, "OffsetX")) {
500 					b->Offset.x = LuaToNumber(l, -1);
501 				} else if (!strcmp(value, "OffsetY")) {
502 					b->Offset.y = LuaToNumber(l, -1);
503 				} else if (!strcmp(value, "Type")) {
504 					b->ParentName = LuaToString(l, -1);
505 				} else {
506 					LuaError(l, "Unsupported BuildingRules addon tag: %s" _C_ value);
507 				}
508 			}
509 			//Wyrmgus start
510 //			andlist->push_back(b);
511 			blist.push_back(b);
512 			//Wyrmgus end
513 		} else if (!strcmp(value, "ontop")) {
514 			CBuildRestrictionOnTop *b = new CBuildRestrictionOnTop;
515 
516 			for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
517 				value = LuaToString(l, -2);
518 				if (!strcmp(value, "Type")) {
519 					b->ParentName = LuaToString(l, -1);
520 				} else if (!strcmp(value, "ReplaceOnDie")) {
521 					b->ReplaceOnDie = LuaToBoolean(l, -1);
522 				} else if (!strcmp(value, "ReplaceOnBuild")) {
523 					b->ReplaceOnBuild = LuaToBoolean(l, -1);
524 				} else {
525 					LuaError(l, "Unsupported BuildingRules ontop tag: %s" _C_ value);
526 				}
527 			}
528 			//Wyrmgus start
529 //			andlist->push_back(b);
530 			blist.push_back(b);
531 			//Wyrmgus end
532 		} else if (!strcmp(value, "has-unit")) {
533 			CBuildRestrictionHasUnit *b = new CBuildRestrictionHasUnit;
534 
535 			for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
536 				value = LuaToString(l, -2);
537 				if (!strcmp(value, "Type")) {
538 					b->RestrictTypeName = LuaToString(l, -1);
539 				} else if (!strcmp(value, "Owner")) {
540 					b->RestrictTypeOwner = LuaToString(l, -1);
541 				} else if (!strcmp(value, "Count")) {
542 					b->Count = LuaToNumber(l, -1);
543 				} else if (!strcmp(value, "CountType")) {
544 					value = LuaToString(l, -1);
545 					if (value[0] == '=') {
546 						if ((value[1] == '=' && value[2] == '\0') || (value[1] == '\0')) {
547 							b->CountType = Equal;
548 						}
549 					} else if (value[0] == '>') {
550 						if (value[1] == '=' && value[2] == '\0') {
551 							b->CountType = GreaterThanEqual;
552 						} else if (value[1] == '\0') {
553 							b->CountType = GreaterThan;
554 						}
555 					} else if (value[0] == '<') {
556 						if (value[1] == '=' && value[2] == '\0') {
557 							b->CountType = LessThanEqual;
558 						} else if (value[1] == '\0') {
559 							b->CountType = LessThan;
560 						}
561 					} else if (value[0] == '!' && value[1] == '=' && value[2] == '\0') {
562 						b->CountType = NotEqual;
563 					}
564 				} else {
565 					LuaError(l, "Unsupported BuildingRules has-unit tag: %s" _C_ value);
566 				}
567 			}
568 			//Wyrmgus start
569 //			andlist->push_back(b);
570 			blist.push_back(b);
571 			//Wyrmgus end
572 		} else if (!strcmp(value, "surrounded-by")) {
573 			CBuildRestrictionSurroundedBy *b = new CBuildRestrictionSurroundedBy;
574 
575 			for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
576 				value = LuaToString(l, -2);
577 				if (!strcmp(value, "Type")) {
578 					b->RestrictTypeName = LuaToString(l, -1);
579 				} else if (!strcmp(value, "Count")) {
580 					b->Count = LuaToNumber(l, -1);
581 				} else if (!strcmp(value, "CountType")) {
582 					value = LuaToString(l, -1);
583 					if (value[0] == '=') {
584 						if ((value[1] == '=' && value[2] == '\0') || (value[1] == '\0')) {
585 							b->CountType = Equal;
586 						}
587 					} else if (value[0] == '>') {
588 						if (value[1] == '=' && value[2] == '\0') {
589 							b->CountType = GreaterThanEqual;
590 						} else if (value[1] == '\0') {
591 							b->CountType = GreaterThan;
592 						}
593 					} else if (value[0] == '<') {
594 						if (value[1] == '=' && value[2] == '\0') {
595 							b->CountType = LessThanEqual;
596 						} else if (value[1] == '\0') {
597 							b->CountType = LessThan;
598 						}
599 					} else if (value[0] == '!' && value[1] == '=' && value[2] == '\0') {
600 						b->CountType = NotEqual;
601 					}
602 				} else if (!strcmp(value, "Distance")) {
603 					b->Distance = LuaToNumber(l, -1);
604 				} else if (!strcmp(value, "DistanceType")) {
605 					value = LuaToString(l, -1);
606 					if (value[0] == '=') {
607 						if ((value[1] == '=' && value[2] == '\0') || (value[1] == '\0')) {
608 							b->DistanceType = Equal;
609 						}
610 					} else if (value[0] == '>') {
611 						if (value[1] == '=' && value[2] == '\0') {
612 							b->DistanceType = GreaterThanEqual;
613 						} else if (value[1] == '\0') {
614 							b->DistanceType = GreaterThan;
615 						}
616 					} else if (value[0] == '<') {
617 						if (value[1] == '=' && value[2] == '\0') {
618 							b->DistanceType = LessThanEqual;
619 						} else if (value[1] == '\0') {
620 							b->DistanceType = LessThan;
621 						}
622 					} else if (value[0] == '!' && value[1] == '=' && value[2] == '\0') {
623 						b->DistanceType = NotEqual;
624 					}
625 				} else if (!strcmp(value, "Owner")) {
626 					b->RestrictTypeOwner = LuaToString(l, -1);
627 				} else if (!strcmp(value, "CheckBuilder")) {
628 					b->CheckBuilder = LuaToBoolean(l, -1);
629 				} else {
630 					LuaError(l, "Unsupported BuildingRules surrounded-by tag: %s" _C_ value);
631 				}
632 			}
633 			//Wyrmgus start
634 //			andlist->push_back(b);
635 			blist.push_back(b);
636 			//Wyrmgus end
637 		//Wyrmgus start
638 		} else if (!strcmp(value, "terrain")) {
639 			CBuildRestrictionTerrain *b = new CBuildRestrictionTerrain;
640 
641 			for (lua_pushnil(l); lua_next(l, -2); lua_pop(l, 1)) {
642 				value = LuaToString(l, -2);
643 				if (!strcmp(value, "Terrain")) {
644 					b->RestrictTerrainTypeName = LuaToString(l, -1);
645 				} else {
646 					LuaError(l, "Unsupported BuildingRules terrain tag: %s" _C_ value);
647 				}
648 			}
649 
650 			blist.push_back(b);
651 		} else if (!strcmp(value, "and")) {
652 			CBuildRestrictionAnd *b = new CBuildRestrictionAnd();
653 			std::vector<CBuildRestriction *> and_list;
654 
655 			ParseBuildingRules(l, and_list);
656 
657 			for (size_t k = 0; k < and_list.size(); ++k) {
658 				b->push_back(and_list[k]);
659 			}
660 
661 			blist.push_back(b);
662 		} else if (!strcmp(value, "or")) {
663 			CBuildRestrictionOr *b = new CBuildRestrictionOr();
664 			std::vector<CBuildRestriction *> and_list;
665 
666 			ParseBuildingRules(l, and_list);
667 
668 			for (size_t k = 0; k < and_list.size(); ++k) {
669 				b->push_back(and_list[k]);
670 			}
671 
672 			blist.push_back(b);
673 		//Wyrmgus end
674 		} else {
675 			LuaError(l, "Unsupported BuildingRules tag: %s" _C_ value);
676 		}
677 		lua_pop(l, 1);
678 	}
679 	//Wyrmgus start
680 //	blist.push_back(andlist);
681 	//Wyrmgus end
682 }
683 
684 /**
685 **  Parse unit-type.
686 **
687 **  @param l  Lua state.
688 */
CclDefineUnitType(lua_State * l)689 static int CclDefineUnitType(lua_State *l)
690 {
691 	LuaCheckArgs(l, 2);
692 	if (!lua_istable(l, 2)) {
693 		LuaError(l, "incorrect argument");
694 	}
695 
696 	// Slot identifier
697 	const char *str = LuaToString(l, 1);
698 	CUnitType *type = UnitTypeByIdent(str);
699 	int redefine;
700 	if (type) {
701 		redefine = 1;
702 		//Wyrmgus start
703 		type->RemoveButtons(ButtonMove);
704 		type->RemoveButtons(ButtonStop);
705 		type->RemoveButtons(ButtonAttack);
706 		type->RemoveButtons(ButtonPatrol);
707 		type->RemoveButtons(ButtonStandGround);
708 		type->RemoveButtons(ButtonReturn);
709 		//Wyrmgus end
710 	} else {
711 		type = NewUnitTypeSlot(str);
712 		redefine = 0;
713 	}
714 
715 	//  Parse the list: (still everything could be changed!)
716 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
717 		const char *value = LuaToString(l, -2);
718 		if (!strcmp(value, "Name")) {
719 			type->Name = LuaToString(l, -1);
720 		//Wyrmgus start
721 		} else if (!strcmp(value, "Parent")) {
722 			std::string parent_ident = LuaToString(l, -1);
723 			CUnitType *parent_type = UnitTypeByIdent(parent_ident);
724 			if (!parent_type) {
725 				LuaError(l, "Unit type %s not defined" _C_ parent_ident.c_str());
726 			}
727 			type->SetParent(parent_type);
728 		} else if (!strcmp(value, "Variations")) {
729 			type->DefaultStat.Variables[VARIATION_INDEX].Enable = 1;
730 			type->DefaultStat.Variables[VARIATION_INDEX].Value = 0;
731 			//remove previously defined variations, if any
732 			for (CUnitTypeVariation *variation : type->Variations) {
733 				delete variation;
734 			}
735 			type->Variations.clear();
736 			//remove previously defined layer variations, if any
737 			for (int i = 0; i < MaxImageLayers; ++i) {
738 				for (CUnitTypeVariation *variation : type->LayerVariations[i]) {
739 					delete variation;
740 				}
741 				type->LayerVariations[i].clear();
742 			}
743 			const int args = lua_rawlen(l, -1);
744 			for (int j = 0; j < args; ++j) {
745 				lua_rawgeti(l, -1, j + 1);
746 				CUnitTypeVariation *variation = new CUnitTypeVariation;
747 				if (!lua_istable(l, -1)) {
748 					LuaError(l, "incorrect argument (expected table for variations)");
749 				}
750 				const int subargs = lua_rawlen(l, -1);
751 				for (int k = 0; k < subargs; ++k) {
752 					value = LuaToString(l, -1, k + 1);
753 					++k;
754 					if (!strcmp(value, "layer")) {
755 						std::string image_layer_name = LuaToString(l, -1, k + 1);
756 						variation->ImageLayer = GetImageLayerIdByName(image_layer_name);
757 						if (variation->ImageLayer != -1) {
758 							variation->ID = type->LayerVariations[variation->ImageLayer].size();
759 							type->LayerVariations[variation->ImageLayer].push_back(variation);
760 						} else {
761 							LuaError(l, "Image layer \"%s\" doesn't exist." _C_ image_layer_name.c_str());
762 						}
763 					} else if (!strcmp(value, "variation-id")) {
764 						variation->VariationId = LuaToString(l, -1, k + 1);
765 						if (variation->ImageLayer == -1) {
766 							variation->ID = type->Variations.size();
767 							type->Variations.push_back(variation);
768 						}
769 					} else if (!strcmp(value, "type-name")) {
770 						variation->TypeName = LuaToString(l, -1, k + 1);
771 					} else if (!strcmp(value, "file")) {
772 						variation->File = LuaToString(l, -1, k + 1);
773 					} else if (!strcmp(value, "file-when-loaded")) {
774 						const int res = GetResourceIdByName(LuaToString(l, -1, k + 1));
775 						++k;
776 						variation->FileWhenLoaded[res] = LuaToString(l, -1, k + 1);
777 					} else if (!strcmp(value, "file-when-empty")) {
778 						const int res = GetResourceIdByName(LuaToString(l, -1, k + 1));
779 						++k;
780 						variation->FileWhenEmpty[res] = LuaToString(l, -1, k + 1);
781 					} else if (!strcmp(value, "shadow-file")) {
782 						variation->ShadowFile = LuaToString(l, -1, k + 1);
783 					} else if (!strcmp(value, "light-file")) {
784 						variation->LightFile = LuaToString(l, -1, k + 1);
785 					} else if (!strcmp(value, "layer-file")) {
786 						int image_layer = GetImageLayerIdByName(LuaToString(l, -1, k + 1));
787 						++k;
788 						variation->LayerFiles[image_layer] = LuaToString(l, -1, k + 1);
789 					} else if (!strcmp(value, "frame-size")) {
790 						lua_rawgeti(l, -1, k + 1);
791 						CclGetPos(l, &variation->FrameWidth, &variation->FrameHeight);
792 						lua_pop(l, 1);
793 					} else if (!strcmp(value, "icon")) {
794 						variation->Icon.Name = LuaToString(l, -1, k + 1);
795 						variation->Icon.Icon = nullptr;
796 						variation->Icon.Load();
797 						variation->Icon.Icon->Load();
798 					} else if (!strcmp(value, "button-icon")) {
799 						int button_action = GetButtonActionIdByName(LuaToString(l, -1, k + 1));
800 						++k;
801 						variation->ButtonIcons[button_action].Name = LuaToString(l, -1, k + 1);
802 						variation->ButtonIcons[button_action].Icon = nullptr;
803 						variation->ButtonIcons[button_action].Load();
804 						variation->ButtonIcons[button_action].Icon->Load();
805 					} else if (!strcmp(value, "animations")) {
806 						variation->Animations = AnimationsByIdent(LuaToString(l, -1, k + 1));
807 						if (!variation->Animations) {
808 							DebugPrint("Warning animation '%s' not found\n" _C_ LuaToString(l, -1, k + 1));
809 						}
810 					} else if (!strcmp(value, "construction")) {
811 						variation->Construction = ConstructionByIdent(LuaToString(l, -1, k + 1));
812 					} else if (!strcmp(value, "upgrade-required")) {
813 						const std::string upgrade_ident = LuaToString(l, -1, k + 1);
814 						const CUpgrade *upgrade = CUpgrade::Get(upgrade_ident);
815 						if (upgrade != nullptr) {
816 							variation->UpgradesRequired.push_back(upgrade);
817 						} else {
818 							variation->UpgradesRequired.push_back(CUpgrade::New(upgrade_ident)); //if this upgrade doesn't exist, define it now (this is useful if the unit type is defined before the upgrade)
819 						}
820 					} else if (!strcmp(value, "upgrade-forbidden")) {
821 						const std::string upgrade_ident = LuaToString(l, -1, k + 1);
822 						const CUpgrade *upgrade = CUpgrade::Get(upgrade_ident);
823 						if (upgrade != nullptr) {
824 							variation->UpgradesForbidden.push_back(upgrade);
825 						} else {
826 							variation->UpgradesForbidden.push_back(CUpgrade::New(upgrade_ident)); //if this upgrade doesn't exist, define it now (this is useful if the unit type is defined before the upgrade)
827 						}
828 					} else if (!strcmp(value, "item-class-equipped")) {
829 						std::string item_class_ident = LuaToString(l, -1, k + 1);
830 						int item_class = GetItemClassIdByName(item_class_ident);
831 						if (item_class != -1) {
832 							variation->ItemClassesEquipped.push_back(item_class);
833 						} else {
834 							LuaError(l, "Item class \"%s\" does not exist." _C_ item_class_ident.c_str());
835 						}
836 					} else if (!strcmp(value, "item-class-not-equipped")) {
837 						std::string item_class_ident = LuaToString(l, -1, k + 1);
838 						int item_class = GetItemClassIdByName(item_class_ident);
839 						if (item_class != -1) {
840 							variation->ItemClassesNotEquipped.push_back(item_class);
841 						} else {
842 							LuaError(l, "Item class \"%s\" does not exist." _C_ item_class_ident.c_str());
843 						}
844 					} else if (!strcmp(value, "item-equipped")) {
845 						std::string type_ident = LuaToString(l, -1, k + 1);
846 						const CUnitType *type = UnitTypeByIdent(type_ident);
847 						if (type) {
848 							variation->ItemsEquipped.push_back(type);
849 						} else {
850 							LuaError(l, "Unit type %s not defined" _C_ type_ident.c_str());
851 						}
852 					} else if (!strcmp(value, "item-not-equipped")) {
853 						std::string type_ident = LuaToString(l, -1, k + 1);
854 						const CUnitType *type = UnitTypeByIdent(type_ident);
855 						if (type) {
856 							variation->ItemsNotEquipped.push_back(type);
857 						} else {
858 							LuaError(l, "Unit type %s not defined" _C_ type_ident.c_str());
859 						}
860 					} else if (!strcmp(value, "terrain")) {
861 						std::string terrain_ident = LuaToString(l, -1, k + 1);
862 						const CTerrainType *terrain = CTerrainType::GetTerrainType(terrain_ident);
863 						if (terrain) {
864 							variation->Terrains.push_back(terrain);
865 						} else {
866 							LuaError(l, "Terrain type \"%s\" doesn't exist." _C_ terrain_ident.c_str());
867 						}
868 					} else if (!strcmp(value, "terrain-forbidden")) {
869 						std::string terrain_ident = LuaToString(l, -1, k + 1);
870 						const CTerrainType *terrain = CTerrainType::GetTerrainType(terrain_ident);
871 						if (terrain) {
872 							variation->TerrainsForbidden.push_back(terrain);
873 						} else {
874 							LuaError(l, "Terrain type \"%s\" doesn't exist." _C_ terrain_ident.c_str());
875 						}
876 					} else if (!strcmp(value, "season")) {
877 						const std::string season_ident = LuaToString(l, -1, k + 1);
878 						CSeason *season = CSeason::GetSeason(season_ident);
879 						if (season) {
880 							variation->Seasons.push_back(season);
881 						}
882 					} else if (!strcmp(value, "forbidden-season")) {
883 						const std::string season_ident = LuaToString(l, -1, k + 1);
884 						CSeason *season = CSeason::GetSeason(season_ident);
885 						if (season) {
886 							variation->ForbiddenSeasons.push_back(season);
887 						}
888 					} else if (!strcmp(value, "resource-min")) {
889 						variation->ResourceMin = LuaToNumber(l, -1, k + 1);
890 					} else if (!strcmp(value, "resource-max")) {
891 						variation->ResourceMax = LuaToNumber(l, -1, k + 1);
892 					} else if (!strcmp(value, "weight")) {
893 						variation->Weight = LuaToNumber(l, -1, k + 1);
894 					} else {
895 						printf("\n%s\n", type->Name.c_str());
896 						LuaError(l, "Unsupported tag: %s" _C_ value);
897 					}
898 				}
899 				// Assert(variation->VariationId);
900 				lua_pop(l, 1);
901 			}
902 
903 			type->DefaultStat.Variables[VARIATION_INDEX].Max = type->Variations.size();
904 		//Wyrmgus end
905 		} else if (!strcmp(value, "Image")) {
906 			if (!lua_istable(l, -1)) {
907 				LuaError(l, "incorrect argument");
908 			}
909 			int subargs = lua_rawlen(l, -1);
910 			for (int k = 0; k < subargs; ++k) {
911 				value = LuaToString(l, -1, k + 1);
912 				++k;
913 
914 				if (!strcmp(value, "file")) {
915 					type->File = LuaToString(l, -1, k + 1);
916 				} else if (!strcmp(value, "size")) {
917 					lua_rawgeti(l, -1, k + 1);
918 					CclGetPos(l, &type->Width, &type->Height);
919 					lua_pop(l, 1);
920 				} else {
921 					LuaError(l, "Unsupported image tag: %s" _C_ value);
922 				}
923 			}
924 			if (redefine && type->Sprite) {
925 				CGraphic::Free(type->Sprite);
926 				type->Sprite = nullptr;
927 			}
928 		} else if (!strcmp(value, "Shadow")) {
929 			if (!lua_istable(l, -1)) {
930 				LuaError(l, "incorrect argument");
931 			}
932 			const int subargs = lua_rawlen(l, -1);
933 			for (int k = 0; k < subargs; ++k) {
934 				value = LuaToString(l, -1, k + 1);
935 				++k;
936 
937 				if (!strcmp(value, "file")) {
938 					type->ShadowFile = LuaToString(l, -1, k + 1);
939 				} else if (!strcmp(value, "size")) {
940 					lua_rawgeti(l, -1, k + 1);
941 					CclGetPos(l, &type->ShadowWidth, &type->ShadowHeight);
942 					lua_pop(l, 1);
943 				} else if (!strcmp(value, "offset")) {
944 					lua_rawgeti(l, -1, k + 1);
945 					CclGetPos(l, &type->ShadowOffsetX, &type->ShadowOffsetY);
946 					lua_pop(l, 1);
947 				} else {
948 					LuaError(l, "Unsupported shadow tag: %s" _C_ value);
949 				}
950 			}
951 			if (redefine && type->ShadowSprite) {
952 				CGraphic::Free(type->ShadowSprite);
953 				type->ShadowSprite = nullptr;
954 			}
955 		//Wyrmgus start
956 		} else if (!strcmp(value, "LightImage")) {
957 			if (!lua_istable(l, -1)) {
958 				LuaError(l, "incorrect argument");
959 			}
960 			const int subargs = lua_rawlen(l, -1);
961 			for (int k = 0; k < subargs; ++k) {
962 				value = LuaToString(l, -1, k + 1);
963 				++k;
964 
965 				if (!strcmp(value, "file")) {
966 					type->LightFile = LuaToString(l, -1, k + 1);
967 				} else {
968 					LuaError(l, "Unsupported light tag: %s" _C_ value);
969 				}
970 			}
971 			if (redefine && type->LightSprite) {
972 				CGraphic::Free(type->LightSprite);
973 				type->LightSprite = nullptr;
974 			}
975 		} else if (!strcmp(value, "LayerImages")) {
976 			if (!lua_istable(l, -1)) {
977 				LuaError(l, "incorrect argument");
978 			}
979 			const int args = lua_rawlen(l, -1);
980 			for (int j = 0; j < args; ++j) {
981 				lua_rawgeti(l, -1, j + 1);
982 				const int subargs = lua_rawlen(l, -1);
983 				int image_layer = 0;
984 				for (int k = 0; k < subargs; ++k) {
985 					value = LuaToString(l, -1, k + 1);
986 					++k;
987 
988 					if (!strcmp(value, "layer")) {
989 						image_layer = GetImageLayerIdByName(LuaToString(l, -1, k + 1));
990 					} else if (!strcmp(value, "file")) {
991 						type->LayerFiles[image_layer] = LuaToString(l, -1, k + 1);
992 					} else {
993 						LuaError(l, "Unsupported layer image tag: %s" _C_ value);
994 					}
995 				}
996 				if (redefine && type->LayerSprites[image_layer]) {
997 					CGraphic::Free(type->LayerSprites[image_layer]);
998 					type->LayerSprites[image_layer] = nullptr;
999 				}
1000 				lua_pop(l, 1);
1001 			}
1002 		} else if (!strcmp(value, "ButtonIcons")) {
1003 			if (!lua_istable(l, -1)) {
1004 				LuaError(l, "incorrect argument");
1005 			}
1006 			const int args = lua_rawlen(l, -1);
1007 			for (int j = 0; j < args; ++j) {
1008 				lua_rawgeti(l, -1, j + 1);
1009 				const int subargs = lua_rawlen(l, -1);
1010 				int image_layer = 0;
1011 				for (int k = 0; k < subargs; ++k) {
1012 					int button_action = GetButtonActionIdByName(LuaToString(l, -1, k + 1));
1013 					++k;
1014 					type->ButtonIcons[button_action].Name = LuaToString(l, -1, k + 1);
1015 					type->ButtonIcons[button_action].Icon = nullptr;
1016 					type->ButtonIcons[button_action].Load();
1017 					type->ButtonIcons[button_action].Icon->Load();
1018 				}
1019 				lua_pop(l, 1);
1020 			}
1021 		} else if (!strcmp(value, "DefaultEquipment")) {
1022 			if (!lua_istable(l, -1)) {
1023 				LuaError(l, "incorrect argument");
1024 			}
1025 			const int args = lua_rawlen(l, -1);
1026 			for (int j = 0; j < args; ++j) {
1027 				lua_rawgeti(l, -1, j + 1);
1028 				const int subargs = lua_rawlen(l, -1);
1029 				int image_layer = 0;
1030 				for (int k = 0; k < subargs; ++k) {
1031 					int item_slot = GetItemSlotIdByName(LuaToString(l, -1, k + 1));
1032 					++k;
1033 					CUnitType *default_equipment = UnitTypeByIdent(LuaToString(l, -1, k + 1));
1034 					if (default_equipment != nullptr) {
1035 						type->DefaultEquipment[item_slot] = default_equipment;
1036 					} else { // Error
1037 						LuaError(l, "incorrect default equipment unit-type");
1038 					}
1039 				}
1040 				lua_pop(l, 1);
1041 			}
1042 		//Wyrmgus end
1043 		} else if (!strcmp(value, "Offset")) {
1044 			CclGetPos(l, &type->OffsetX, &type->OffsetY);
1045 		} else if (!strcmp(value, "Flip")) {
1046 			type->Flip = LuaToBoolean(l, -1);
1047 		} else if (!strcmp(value, "Animations")) {
1048 			type->Animations = AnimationsByIdent(LuaToString(l, -1));
1049 			if (!type->Animations) {
1050 				DebugPrint("Warning animation '%s' not found\n" _C_ LuaToString(l, -1));
1051 			}
1052 		} else if (!strcmp(value, "Icon")) {
1053 			type->Icon.Name = LuaToString(l, -1);
1054 			type->Icon.Icon = nullptr;
1055 			//Wyrmgus start
1056 			type->Icon.Load();
1057 			type->Icon.Icon->Load();
1058 			//Wyrmgus end
1059 #ifdef USE_MNG
1060 		} else if (!strcmp(value, "Portrait")) {
1061 			if (!lua_istable(l, -1)) {
1062 				LuaError(l, "incorrect argument");
1063 			}
1064 			const int subargs = lua_rawlen(l, -1);
1065 			type->Portrait.Num = subargs;
1066 			type->Portrait.Files = new std::string[type->Portrait.Num];
1067 			type->Portrait.Mngs = new Mng *[type->Portrait.Num];
1068 			memset(type->Portrait.Mngs, 0, type->Portrait.Num * sizeof(Mng *));
1069 			for (int k = 0; k < subargs; ++k) {
1070 				type->Portrait.Files[k] = LuaToString(l, -1, k + 1);
1071 			}
1072 #endif
1073 		} else if (!strcmp(value, "Costs")) {
1074 			if (!lua_istable(l, -1)) {
1075 				LuaError(l, "incorrect argument");
1076 			}
1077 			const int subargs = lua_rawlen(l, -1);
1078 			for (int k = 0; k < subargs; ++k) {
1079 				lua_rawgeti(l, -1, k + 1);
1080 				const int res = CclGetResourceByName(l);
1081 				lua_pop(l, 1);
1082 				++k;
1083 				type->DefaultStat.Costs[res] = LuaToNumber(l, -1, k + 1);
1084 			}
1085 		} else if (!strcmp(value, "Storing")) {
1086 			if (!lua_istable(l, -1)) {
1087 				LuaError(l, "incorrect argument");
1088 			}
1089 			const int subargs = lua_rawlen(l, -1);
1090 			for (int k = 0; k < subargs; ++k) {
1091 				lua_rawgeti(l, -1, k + 1);
1092 				const int res = CclGetResourceByName(l);
1093 				lua_pop(l, 1);
1094 				++k;
1095 				type->DefaultStat.Storing[res] = LuaToNumber(l, -1, k + 1);
1096 			}
1097 		} else if (!strcmp(value, "ImproveProduction")) {
1098 			if (!lua_istable(l, -1)) {
1099 				LuaError(l, "incorrect argument");
1100 			}
1101 			const int subargs = lua_rawlen(l, -1);
1102 			for (int k = 0; k < subargs; ++k) {
1103 				lua_rawgeti(l, -1, k + 1);
1104 				const int res = CclGetResourceByName(l);
1105 				lua_pop(l, 1);
1106 				++k;
1107 				type->DefaultStat.ImproveIncomes[res] = CResource::Resources[res]->DefaultIncome + LuaToNumber(l, -1, k + 1);
1108 			}
1109 		//Wyrmgus start
1110 		} else if (!strcmp(value, "ResourceDemand")) {
1111 			if (!lua_istable(l, -1)) {
1112 				LuaError(l, "incorrect argument");
1113 			}
1114 			const int subargs = lua_rawlen(l, -1);
1115 			for (int k = 0; k < subargs; ++k) {
1116 				lua_rawgeti(l, -1, k + 1);
1117 				const int res = CclGetResourceByName(l);
1118 				lua_pop(l, 1);
1119 				++k;
1120 				type->DefaultStat.ResourceDemand[res] = LuaToNumber(l, -1, k + 1);
1121 			}
1122 		} else if (!strcmp(value, "UnitStock")) {
1123 			if (!lua_istable(l, -1)) {
1124 				LuaError(l, "incorrect argument");
1125 			}
1126 			const int subargs = lua_rawlen(l, -1);
1127 			for (int k = 0; k < subargs; ++k) {
1128 				CUnitType *unit_type = UnitTypeByIdent(LuaToString(l, -1, k + 1));
1129 				if (!unit_type) {
1130 					LuaError(l, "Unit type \"%s\" does not exist." _C_ value);
1131 				}
1132 				++k;
1133 				type->DefaultStat.SetUnitStock(unit_type, LuaToNumber(l, -1, k + 1));
1134 			}
1135 		//Wyrmgus end
1136 		} else if (!strcmp(value, "Construction")) {
1137 			// FIXME: What if constructions aren't yet loaded?
1138 			type->Construction = ConstructionByIdent(LuaToString(l, -1));
1139 		} else if (!strcmp(value, "DrawLevel")) {
1140 			type->DrawLevel = LuaToNumber(l, -1);
1141 		} else if (!strcmp(value, "MaxOnBoard")) {
1142 			type->MaxOnBoard = LuaToNumber(l, -1);
1143 			//Wyrmgus start
1144 			type->DefaultStat.Variables[TRANSPORT_INDEX].Max = type->MaxOnBoard;
1145 			type->DefaultStat.Variables[TRANSPORT_INDEX].Enable = 1;
1146 			//Wyrmgus end
1147 		} else if (!strcmp(value, "BoardSize")) {
1148 			type->BoardSize = LuaToNumber(l, -1);
1149 		} else if (!strcmp(value, "ButtonLevelForTransporter")) {
1150 			type->ButtonLevelForTransporter = CButtonLevel::GetButtonLevel(LuaToString(l, -1));
1151 		//Wyrmgus start
1152 		} else if (!strcmp(value, "ButtonPos")) {
1153 			type->ButtonPos = LuaToNumber(l, -1);
1154 		} else if (!strcmp(value, "ButtonLevel")) {
1155 			type->ButtonLevel = CButtonLevel::GetButtonLevel(LuaToString(l, -1));
1156 		} else if (!strcmp(value, "ButtonPopup")) {
1157 			type->ButtonPopup = LuaToString(l, -1);
1158 		} else if (!strcmp(value, "ButtonHint")) {
1159 			type->ButtonHint = LuaToString(l, -1);
1160 		} else if (!strcmp(value, "ButtonKey")) {
1161 			type->ButtonKey = LuaToString(l, -1);
1162 		} else if (!strcmp(value, "Trains")) {
1163 			type->RemoveButtons(ButtonTrain);
1164 			type->RemoveButtons(ButtonBuild);
1165 			for (size_t i = 0; i < type->Trains.size(); ++i) {
1166 				if (std::find(type->Trains[i]->TrainedBy.begin(), type->Trains[i]->TrainedBy.end(), type) != type->Trains[i]->TrainedBy.end()) {
1167 					type->Trains[i]->TrainedBy.erase(std::remove(type->Trains[i]->TrainedBy.begin(), type->Trains[i]->TrainedBy.end(), type), type->Trains[i]->TrainedBy.end());
1168 				}
1169 			}
1170 			type->Trains.clear();
1171 			const int args = lua_rawlen(l, -1);
1172 			for (int j = 0; j < args; ++j) {
1173 				value = LuaToString(l, -1, j + 1);
1174 				CUnitType *trained_unit = UnitTypeByIdent(value);
1175 				if (trained_unit != nullptr) {
1176 					type->Trains.push_back(trained_unit);
1177 					trained_unit->TrainedBy.push_back(type);
1178 				} else {
1179 					LuaError(l, "Unit type \"%s\" doesn't exist." _C_ value);
1180 				}
1181 			}
1182 		//Wyrmgus end
1183 		//Wyrmgus start
1184 //		} else if (!strcmp(value, "StartingResources")) {
1185 //			type->StartingResources = LuaToNumber(l, -1);
1186 		} else if (!strcmp(value, "StartingResources")) {
1187 			type->StartingResources.clear();
1188 			const int args = lua_rawlen(l, -1);
1189 			for (int j = 0; j < args; ++j) {
1190 				type->StartingResources.push_back(LuaToNumber(l, -1, j + 1));
1191 			}
1192 		//Wyrmgus end
1193 		} else if (!strcmp(value, "RegenerationRate")) {
1194 			type->DefaultStat.Variables[HP_INDEX].Increase = LuaToNumber(l, -1);
1195 		} else if (!strcmp(value, "BurnPercent")) {
1196 			type->BurnPercent = LuaToNumber(l, -1);
1197 		} else if (!strcmp(value, "BurnDamageRate")) {
1198 			type->BurnDamageRate = LuaToNumber(l, -1);
1199 		} else if (!strcmp(value, "PoisonDrain")) {
1200 			type->PoisonDrain = LuaToNumber(l, -1);
1201 		} else if (!strcmp(value, "ShieldPoints")) {
1202 			if (lua_istable(l, -1)) {
1203 				DefineVariableField(l, type->DefaultStat.Variables + SHIELD_INDEX, -1);
1204 			} else if (lua_isnumber(l, -1)) {
1205 				type->DefaultStat.Variables[SHIELD_INDEX].Max = LuaToNumber(l, -1);
1206 				type->DefaultStat.Variables[SHIELD_INDEX].Value = 0;
1207 				type->DefaultStat.Variables[SHIELD_INDEX].Increase = 1;
1208 				type->DefaultStat.Variables[SHIELD_INDEX].Enable = 1;
1209 			}
1210 		} else if (!strcmp(value, "TileSize")) {
1211 			CclGetPos(l, &type->TileSize.x, &type->TileSize.y);
1212 		} else if (!strcmp(value, "NeutralMinimapColor")) {
1213 			type->NeutralMinimapColorRGB.Parse(l);
1214 		} else if (!strcmp(value, "BoxSize")) {
1215 			CclGetPos(l, &type->BoxWidth, &type->BoxHeight);
1216 		} else if (!strcmp(value, "BoxOffset")) {
1217 			CclGetPos(l, &type->BoxOffsetX, &type->BoxOffsetY);
1218 		} else if (!strcmp(value, "NumDirections")) {
1219 			type->NumDirections = LuaToNumber(l, -1);
1220 		//Wyrmgus start
1221 //		} else if (!strcmp(value, "ComputerReactionRange")) {
1222 //			type->ReactRangeComputer = LuaToNumber(l, -1);
1223 //		} else if (!strcmp(value, "PersonReactionRange")) {
1224 //			type->ReactRangePerson = LuaToNumber(l, -1);
1225 		//Wyrmgus end
1226 		} else if (!strcmp(value, "Missile")) {
1227 			type->Missile.Name = LuaToString(l, -1);
1228 			type->Missile.Missile = nullptr;
1229 		//Wyrmgus start
1230 		} else if (!strcmp(value, "FireMissile")) {
1231 			type->FireMissile.Name = LuaToString(l, -1);
1232 			type->FireMissile.Missile = nullptr;
1233 		//Wyrmgus end
1234 		} else if (!strcmp(value, "MinAttackRange")) {
1235 			type->MinAttackRange = LuaToNumber(l, -1);
1236 		} else if (!strcmp(value, "MaxAttackRange")) {
1237 			type->DefaultStat.Variables[ATTACKRANGE_INDEX].Value = LuaToNumber(l, -1);
1238 			type->DefaultStat.Variables[ATTACKRANGE_INDEX].Max = LuaToNumber(l, -1);
1239 			type->DefaultStat.Variables[ATTACKRANGE_INDEX].Enable = 1;
1240 		//Wyrmgus start
1241 //		} else if (!strcmp(value, "MaxHarvesters")) {
1242 //			type->DefaultStat.Variables[MAXHARVESTERS_INDEX].Value = LuaToNumber(l, -1);
1243 //			type->DefaultStat.Variables[MAXHARVESTERS_INDEX].Max = LuaToNumber(l, -1);
1244 		//Wyrmgus end
1245 		} else if (!strcmp(value, "Priority")) {
1246 			type->DefaultStat.Variables[PRIORITY_INDEX].Value  = LuaToNumber(l, -1);
1247 			type->DefaultStat.Variables[PRIORITY_INDEX].Max  = LuaToNumber(l, -1);
1248 		} else if (!strcmp(value, "AnnoyComputerFactor")) {
1249 			type->AnnoyComputerFactor = LuaToNumber(l, -1);
1250 		} else if (!strcmp(value, "AiAdjacentRange")) {
1251 			type->AiAdjacentRange = LuaToNumber(l, -1);
1252 		} else if (!strcmp(value, "DecayRate")) {
1253 			type->DecayRate = LuaToNumber(l, -1);
1254 		} else if (!strcmp(value, "Corpse")) {
1255 			type->CorpseName = LuaToString(l, -1);
1256 			type->CorpseType = nullptr;
1257 		} else if (!strcmp(value, "DamageType")) {
1258 			value = LuaToString(l, -1);
1259 			//int check = ExtraDeathIndex(value);
1260 			type->DamageType = value;
1261 		} else if (!strcmp(value, "ExplodeWhenKilled")) {
1262 			type->ExplodeWhenKilled = 1;
1263 			type->Explosion.Name = LuaToString(l, -1);
1264 			type->Explosion.Missile = nullptr;
1265 		} else if (!strcmp(value, "TeleportCost")) {
1266 			type->TeleportCost = LuaToNumber(l, -1);
1267 		} else if (!strcmp(value, "TeleportEffectIn")) {
1268 			type->TeleportEffectIn = new LuaCallback(l, -1);
1269 		} else if (!strcmp(value, "TeleportEffectOut")) {
1270 			type->TeleportEffectOut = new LuaCallback(l, -1);
1271 		} else if (!strcmp(value, "DeathExplosion")) {
1272 			type->DeathExplosion = new LuaCallback(l, -1);
1273 		} else if (!strcmp(value, "OnHit")) {
1274 			type->OnHit = new LuaCallback(l, -1);
1275 		} else if (!strcmp(value, "OnEachCycle")) {
1276 			type->OnEachCycle = new LuaCallback(l, -1);
1277 		} else if (!strcmp(value, "OnEachSecond")) {
1278 			type->OnEachSecond = new LuaCallback(l, -1);
1279 		} else if (!strcmp(value, "OnInit")) {
1280 			type->OnInit = new LuaCallback(l, -1);
1281 		} else if (!strcmp(value, "Type")) {
1282 			value = LuaToString(l, -1);
1283 			if (!strcmp(value, "land")) {
1284 				type->UnitType = UnitTypeLand;
1285 				//Wyrmgus start
1286 				type->LandUnit = true;
1287 				//Wyrmgus end
1288 			} else if (!strcmp(value, "fly")) {
1289 				type->UnitType = UnitTypeFly;
1290 				//Wyrmgus start
1291 				type->AirUnit = true;
1292 				//Wyrmgus end
1293 			//Wyrmgus start
1294 			} else if (!strcmp(value, "fly-low")) {
1295 				type->UnitType = UnitTypeFlyLow;
1296 				//Wyrmgus start
1297 				type->AirUnit = true;
1298 				//Wyrmgus end
1299 			//Wyrmgus end
1300 			} else if (!strcmp(value, "naval")) {
1301 				type->UnitType = UnitTypeNaval;
1302 				//Wyrmgus start
1303 				type->SeaUnit = true;
1304 				//Wyrmgus end
1305 			} else {
1306 				LuaError(l, "Unsupported Type: %s" _C_ value);
1307 			}
1308 		} else if (!strcmp(value, "MissileOffsets")) {
1309 			if (!lua_istable(l, -1)) {
1310 				LuaError(l, "incorrect argument");
1311 			}
1312 			const int subargs = lua_rawlen(l, -1);
1313 			for (int k = 0; k < subargs; ++k) {
1314 				lua_rawgeti(l, -1, k + 1);
1315 				if (!lua_istable(l, -1) || lua_rawlen(l, -1) != UnitSides) {
1316 					LuaError(l, "incorrect argument");
1317 				}
1318 				for (int m = 0; m < UnitSides; ++m) {
1319 					lua_rawgeti(l, -1, m + 1);
1320 					CclGetPos(l, &type->MissileOffsets[m][k].x, &type->MissileOffsets[m][k].y);
1321 					lua_pop(l, 1);
1322 				}
1323 				lua_pop(l, 1);
1324 			}
1325 		} else if (!strcmp(value, "Impact")) {
1326 			if (!lua_istable(l, -1)) {
1327 				LuaError(l, "incorrect argument");
1328 			}
1329 			const int subargs = lua_rawlen(l, -1);
1330 			for (int k = 0; k < subargs; ++k) {
1331 				const char *dtype = LuaToString(l, -1, k + 1);
1332 				++k;
1333 
1334 				if (!strcmp(dtype, "general")) {
1335 					type->Impact[ANIMATIONS_DEATHTYPES].Name = LuaToString(l, -1, k + 1);
1336 					type->Impact[ANIMATIONS_DEATHTYPES].Missile = nullptr;
1337 				} else if (!strcmp(dtype, "shield")) {
1338 					type->Impact[ANIMATIONS_DEATHTYPES + 1].Name = LuaToString(l, -1, k + 1);
1339 					type->Impact[ANIMATIONS_DEATHTYPES + 1].Missile = nullptr;
1340 				} else {
1341 					int num = 0;
1342 					for (; num < ANIMATIONS_DEATHTYPES; ++num) {
1343 						if (dtype == ExtraDeathTypes[num]) {
1344 							break;
1345 						}
1346 					}
1347 					if (num == ANIMATIONS_DEATHTYPES) {
1348 						LuaError(l, "Death type not found: %s" _C_ dtype);
1349 					} else {
1350 						type->Impact[num].Name = LuaToString(l, -1, k + 1);
1351 						type->Impact[num].Missile = nullptr;
1352 					}
1353 				}
1354 			}
1355 		} else if (!strcmp(value, "RightMouseAction")) {
1356 			value = LuaToString(l, -1);
1357 			if (!strcmp(value, "none")) {
1358 				type->MouseAction = MouseActionNone;
1359 			} else if (!strcmp(value, "attack")) {
1360 				type->MouseAction = MouseActionAttack;
1361 			} else if (!strcmp(value, "move")) {
1362 				type->MouseAction = MouseActionMove;
1363 			} else if (!strcmp(value, "harvest")) {
1364 				type->MouseAction = MouseActionHarvest;
1365 			} else if (!strcmp(value, "spell-cast")) {
1366 				type->MouseAction = MouseActionSpellCast;
1367 			} else if (!strcmp(value, "sail")) {
1368 				type->MouseAction = MouseActionSail;
1369 			} else if (!strcmp(value, "rally-point")) {
1370 				type->MouseAction = MouseActionRallyPoint;
1371 			//Wyrmgus start
1372 			} else if (!strcmp(value, "trade")) {
1373 				type->MouseAction = MouseActionTrade;
1374 			//Wyrmgus end
1375 			} else {
1376 				LuaError(l, "Unsupported RightMouseAction: %s" _C_ value);
1377 			}
1378 		} else if (!strcmp(value, "CanAttack")) {
1379 			type->CanAttack = LuaToBoolean(l, -1);
1380 		} else if (!strcmp(value, "RepairRange")) {
1381 			type->RepairRange = LuaToNumber(l, -1);
1382 		} else if (!strcmp(value, "RepairHp")) {
1383 			type->RepairHP = LuaToNumber(l, -1);
1384 		} else if (!strcmp(value, "RepairCosts")) {
1385 			if (!lua_istable(l, -1)) {
1386 				LuaError(l, "incorrect argument");
1387 			}
1388 			const int subargs = lua_rawlen(l, -1);
1389 			for (int k = 0; k < subargs; ++k) {
1390 				lua_rawgeti(l, -1, k + 1);
1391 				const int res = CclGetResourceByName(l);
1392 				lua_pop(l, 1);
1393 				++k;
1394 				type->RepairCosts[res] = LuaToNumber(l, -1, k + 1);
1395 			}
1396 		} else if (!strcmp(value, "CanTargetLand")) {
1397 			if (LuaToBoolean(l, -1)) {
1398 				type->CanTarget |= CanTargetLand;
1399 			} else {
1400 				type->CanTarget &= ~CanTargetLand;
1401 			}
1402 		} else if (!strcmp(value, "CanTargetSea")) {
1403 			if (LuaToBoolean(l, -1)) {
1404 				type->CanTarget |= CanTargetSea;
1405 			} else {
1406 				type->CanTarget &= ~CanTargetSea;
1407 			}
1408 		} else if (!strcmp(value, "CanTargetAir")) {
1409 			if (LuaToBoolean(l, -1)) {
1410 				type->CanTarget |= CanTargetAir;
1411 			} else {
1412 				type->CanTarget &= ~CanTargetAir;
1413 			}
1414 		} else if (!strcmp(value, "BuildingRules")) {
1415 			if (!lua_istable(l, -1)) {
1416 				LuaError(l, "incorrect argument");
1417 			}
1418 			const int subargs = lua_rawlen(l, -1);
1419 			// Free any old restrictions if they are redefined
1420 			for (std::vector<CBuildRestriction *>::iterator b = type->BuildingRules.begin();
1421 				 b != type->BuildingRules.end(); ++b) {
1422 				delete *b;
1423 			}
1424 			type->BuildingRules.clear();
1425 			//Wyrmgus start
1426 //			for (int k = 0; k < subargs; ++k) {
1427 //				lua_rawgeti(l, -1, k + 1);
1428 //				if (!lua_istable(l, -1)) {
1429 //					LuaError(l, "incorrect argument");
1430 //				}
1431 //				ParseBuildingRules(l, type->BuildingRules);
1432 //				lua_pop(l, 1);
1433 //			}
1434 			ParseBuildingRules(l, type->BuildingRules);
1435 			//Wyrmgus end
1436 		} else if (!strcmp(value, "AiBuildingRules")) {
1437 			if (!lua_istable(l, -1)) {
1438 				LuaError(l, "incorrect argument");
1439 			}
1440 			const int subargs = lua_rawlen(l, -1);
1441 			// Free any old restrictions if they are redefined
1442 			for (std::vector<CBuildRestriction *>::iterator b = type->AiBuildingRules.begin();
1443 				 b != type->AiBuildingRules.end(); ++b) {
1444 				delete *b;
1445 			}
1446 			type->AiBuildingRules.clear();
1447 			//Wyrmgus start
1448 //			for (int k = 0; k < subargs; ++k) {
1449 //				lua_rawgeti(l, -1, k + 1);
1450 //				if (!lua_istable(l, -1)) {
1451 //					LuaError(l, "incorrect argument");
1452 //				}
1453 //				ParseBuildingRules(l, type->AiBuildingRules);
1454 //				lua_pop(l, 1);
1455 //			}
1456 			ParseBuildingRules(l, type->AiBuildingRules);
1457 			//Wyrmgus end
1458 		} else if (!strcmp(value, "AutoBuildRate")) {
1459 			type->AutoBuildRate = LuaToNumber(l, -1);
1460 		//Wyrmgus start
1461 		/*
1462 		} else if (!strcmp(value, "LandUnit")) {
1463 			type->LandUnit = LuaToBoolean(l, -1);
1464 		} else if (!strcmp(value, "AirUnit")) {
1465 			type->AirUnit = LuaToBoolean(l, -1);
1466 		} else if (!strcmp(value, "SeaUnit")) {
1467 			type->SeaUnit = LuaToBoolean(l, -1);
1468 		*/
1469 		//Wyrmgus end
1470 		} else if (!strcmp(value, "RandomMovementProbability")) {
1471 			type->RandomMovementProbability = LuaToNumber(l, -1);
1472 		} else if (!strcmp(value, "RandomMovementDistance")) {
1473 			type->RandomMovementDistance = LuaToNumber(l, -1);
1474 		} else if (!strcmp(value, "ClicksToExplode")) {
1475 			type->ClicksToExplode = LuaToNumber(l, -1);
1476 		} else if (!strcmp(value, "CanTransport")) {
1477 			//  Warning: CanTransport should only be used AFTER all bool flags
1478 			//  have been defined.
1479 			if (!lua_istable(l, -1)) {
1480 				LuaError(l, "incorrect argument");
1481 			}
1482 			if (type->MaxOnBoard == 0) { // set default value.
1483 				type->MaxOnBoard = 1;
1484 				//Wyrmgus start
1485 				type->DefaultStat.Variables[TRANSPORT_INDEX].Max = type->MaxOnBoard;
1486 				type->DefaultStat.Variables[TRANSPORT_INDEX].Enable = 1;
1487 				//Wyrmgus end
1488 			}
1489 
1490 			if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
1491 				type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
1492 			}
1493 
1494 			const int subargs = lua_rawlen(l, -1);
1495 			for (int k = 0; k < subargs; ++k) {
1496 				value = LuaToString(l, -1, k + 1);
1497 				++k;
1498 
1499 				const int index = UnitTypeVar.BoolFlagNameLookup[value];
1500 				if (index != -1) {
1501 					value = LuaToString(l, -1, k + 1);
1502 					type->BoolFlag[index].CanTransport = Ccl2Condition(l, value);
1503 					continue;
1504 				}
1505 				LuaError(l, "Unsupported flag tag for CanTransport: %s" _C_ value);
1506 			}
1507 		} else if (!strcmp(value, "CanGatherResources")) {
1508 			const int args = lua_rawlen(l, -1);
1509 			for (int j = 0; j < args; ++j) {
1510 				lua_rawgeti(l, -1, j + 1);
1511 				//Wyrmgus start
1512 //				ResourceInfo *res = new ResourceInfo;
1513 				ResourceInfo *res;
1514 				//Wyrmgus end
1515 				if (!lua_istable(l, -1)) {
1516 					LuaError(l, "incorrect argument");
1517 				}
1518 				const int subargs = lua_rawlen(l, -1);
1519 				for (int k = 0; k < subargs; ++k) {
1520 					value = LuaToString(l, -1, k + 1);
1521 					++k;
1522 					if (!strcmp(value, "resource-id")) {
1523 						lua_rawgeti(l, -1, k + 1);
1524 						//Wyrmgus start
1525 //						res->ResourceId = CclGetResourceByName(l);
1526 //						type->ResInfo[res->ResourceId] = res;
1527 						int resource_id = CclGetResourceByName(l);
1528 						//allow redefinition
1529 						res = type->ResInfo[resource_id];
1530 						if (!res) {
1531 							res = new ResourceInfo;
1532 							type->ResInfo[resource_id] = res;
1533 						}
1534 						res->ResourceId = resource_id;
1535 						//Wyrmgus end
1536 						lua_pop(l, 1);
1537 					} else if (!strcmp(value, "resource-step")) {
1538 						res->ResourceStep = LuaToNumber(l, -1, k + 1);
1539 					} else if (!strcmp(value, "wait-at-resource")) {
1540 						res->WaitAtResource = LuaToNumber(l, -1, k + 1);
1541 					} else if (!strcmp(value, "wait-at-depot")) {
1542 						res->WaitAtDepot = LuaToNumber(l, -1, k + 1);
1543 					} else if (!strcmp(value, "resource-capacity")) {
1544 						res->ResourceCapacity = LuaToNumber(l, -1, k + 1);
1545 					//Wyrmgus start
1546 					/*
1547 					} else if (!strcmp(value, "terrain-harvester")) {
1548 						res->TerrainHarvester = 1;
1549 						--k;
1550 					*/
1551 					//Wyrmgus end
1552 					} else if (!strcmp(value, "lose-resources")) {
1553 						res->LoseResources = 1;
1554 						--k;
1555 					//Wyrmgus start
1556 					/*
1557 					} else if (!strcmp(value, "harvest-from-outside")) {
1558 						res->HarvestFromOutside = 1;
1559 						--k;
1560 					*/
1561 					//Wyrmgus end
1562 					} else if (!strcmp(value, "refinery-harvester")) {
1563 						res->RefineryHarvester = 1;
1564 						--k;
1565 					} else if (!strcmp(value, "file-when-empty")) {
1566 						res->FileWhenEmpty = LuaToString(l, -1, k + 1);
1567 					} else if (!strcmp(value, "file-when-loaded")) {
1568 						res->FileWhenLoaded = LuaToString(l, -1, k + 1);
1569 					} else {
1570 						printf("\n%s\n", type->Name.c_str());
1571 						LuaError(l, "Unsupported tag: %s" _C_ value);
1572 					}
1573 				}
1574 				Assert(res->ResourceId);
1575 				lua_pop(l, 1);
1576 			}
1577 			type->BoolFlag[HARVESTER_INDEX].value = 1;
1578 		} else if (!strcmp(value, "GivesResource")) {
1579 			lua_pushvalue(l, -1);
1580 			type->GivesResource = CclGetResourceByName(l);
1581 			lua_pop(l, 1);
1582 		} else if (!strcmp(value, "CanStore")) {
1583 			if (!lua_istable(l, -1)) {
1584 				LuaError(l, "incorrect argument");
1585 			}
1586 			const int subargs = lua_rawlen(l, -1);
1587 			for (int k = 0; k < subargs; ++k) {
1588 				lua_rawgeti(l, -1, k + 1);
1589 				type->CanStore[CclGetResourceByName(l)] = 1;
1590 				lua_pop(l, 1);
1591 			}
1592 		//Wyrmgus start
1593 		} else if (!strcmp(value, "GrandStrategyProductionEfficiencyModifier")) {
1594 			if (!lua_istable(l, -1)) {
1595 				LuaError(l, "incorrect argument");
1596 			}
1597 			const int subargs = lua_rawlen(l, -1);
1598 			for (int k = 0; k < subargs; ++k) {
1599 				lua_rawgeti(l, -1, k + 1);
1600 				const int res = CclGetResourceByName(l);
1601 				lua_pop(l, 1);
1602 				++k;
1603 				type->GrandStrategyProductionEfficiencyModifier[res] = LuaToNumber(l, -1, k + 1);
1604 			}
1605 		//Wyrmgus end
1606 		} else if (!strcmp(value, "CanCastSpell")) {
1607 			if (!lua_istable(l, -1)) {
1608 				LuaError(l, "incorrect argument");
1609 			}
1610 			const int subargs = lua_rawlen(l, -1);
1611 			if (subargs == 0) {
1612 				type->Spells.clear();
1613 			}
1614 			for (int k = 0; k < subargs; ++k) {
1615 				value = LuaToString(l, -1, k + 1);
1616 				CSpell *spell = CSpell::GetSpell(value);
1617 				if (spell == nullptr) {
1618 					LuaError(l, "Unknown spell type: %s" _C_ value);
1619 				}
1620 				type->Spells.push_back(spell);
1621 			}
1622 		} else if (!strcmp(value, "AutoCastActive")) {
1623 			if (!lua_istable(l, -1)) {
1624 				LuaError(l, "incorrect argument");
1625 			}
1626 			//
1627 			// Warning: AutoCastActive should only be used AFTER all spells
1628 			// have been defined.
1629 			//
1630 			if (!type->AutoCastActive) {
1631 				type->AutoCastActive = new char[CSpell::Spells.size()];
1632 				memset(type->AutoCastActive, 0, CSpell::Spells.size() * sizeof(char));
1633 			}
1634 			const int subargs = lua_rawlen(l, -1);
1635 			if (subargs == 0) {
1636 				delete[] type->AutoCastActive;
1637 				type->AutoCastActive = nullptr;
1638 
1639 			}
1640 			for (int k = 0; k < subargs; ++k) {
1641 				value = LuaToString(l, -1, k + 1);
1642 				const CSpell *spell = CSpell::GetSpell(value);
1643 				if (spell == nullptr) {
1644 					LuaError(l, "AutoCastActive : Unknown spell type: %s" _C_ value);
1645 				}
1646 				type->AutoCastActive[spell->Slot] = 1;
1647 			}
1648 		} else if (!strcmp(value, "CanTargetFlag")) {
1649 			//
1650 			// Warning: can-target-flag should only be used AFTER all bool flags
1651 			// have been defined.
1652 			//
1653 			if (!lua_istable(l, -1)) {
1654 				LuaError(l, "incorrect argument");
1655 			}
1656 			if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
1657 				type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
1658 			}
1659 			const int subargs = lua_rawlen(l, -1);
1660 			for (int k = 0; k < subargs; ++k) {
1661 				value = LuaToString(l, -1, k + 1);
1662 				++k;
1663 				int index = UnitTypeVar.BoolFlagNameLookup[value];
1664 				if (index != -1) {
1665 					value = LuaToString(l, -1, k + 1);
1666 					type->BoolFlag[index].CanTargetFlag = Ccl2Condition(l, value);
1667 					continue;
1668 				}
1669 				LuaError(l, "Unsupported flag tag for can-target-flag: %s" _C_ value);
1670 			}
1671 		} else if (!strcmp(value, "PriorityTarget")) {
1672 			//
1673 			// Warning: ai-priority-target should only be used AFTER all bool flags
1674 			// have been defined.
1675 			//
1676 			if (!lua_istable(l, -1)) {
1677 				LuaError(l, "incorrect argument");
1678 			}
1679 			if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
1680 				type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
1681 			}
1682 			const int subargs = lua_rawlen(l, -1);
1683 			for (int k = 0; k < subargs; ++k) {
1684 				value = LuaToString(l, -1, k + 1);
1685 				++k;
1686 				int index = UnitTypeVar.BoolFlagNameLookup[value];
1687 				if (index != -1) {
1688 					value = LuaToString(l, -1, k + 1);
1689 					type->BoolFlag[index].AiPriorityTarget = Ccl2Condition(l, value);
1690 					continue;
1691 				}
1692 				LuaError(l, "Unsupported flag tag for ai-priority-target: %s" _C_ value);
1693 			}
1694 		} else if (!strcmp(value, "Sounds")) {
1695 			if (!lua_istable(l, -1)) {
1696 				LuaError(l, "incorrect argument");
1697 			}
1698 			const int subargs = lua_rawlen(l, -1);
1699 			for (int k = 0; k < subargs; ++k) {
1700 				value = LuaToString(l, -1, k + 1);
1701 				++k;
1702 
1703 				if (!strcmp(value, "selected")) {
1704 					type->Sound.Selected.Name = LuaToString(l, -1, k + 1);
1705 				} else if (!strcmp(value, "acknowledge")) {
1706 					type->Sound.Acknowledgement.Name = LuaToString(l, -1, k + 1);
1707 				} else if (!strcmp(value, "attack")) {
1708 					type->Sound.Attack.Name = LuaToString(l, -1, k + 1);
1709 				//Wyrmgus start
1710 				} else if (!strcmp(value, "idle")) {
1711 					type->Sound.Idle.Name = LuaToString(l, -1, k + 1);
1712 				} else if (!strcmp(value, "hit")) {
1713 					type->Sound.Hit.Name = LuaToString(l, -1, k + 1);
1714 				} else if (!strcmp(value, "miss")) {
1715 					type->Sound.Miss.Name = LuaToString(l, -1, k + 1);
1716 				} else if (!strcmp(value, "fire-missile")) {
1717 					type->Sound.FireMissile.Name = LuaToString(l, -1, k + 1);
1718 				} else if (!strcmp(value, "step")) {
1719 					type->Sound.Step.Name = LuaToString(l, -1, k + 1);
1720 				} else if (!strcmp(value, "step-dirt")) {
1721 					type->Sound.StepDirt.Name = LuaToString(l, -1, k + 1);
1722 				} else if (!strcmp(value, "step-grass")) {
1723 					type->Sound.StepGrass.Name = LuaToString(l, -1, k + 1);
1724 				} else if (!strcmp(value, "step-gravel")) {
1725 					type->Sound.StepGravel.Name = LuaToString(l, -1, k + 1);
1726 				} else if (!strcmp(value, "step-mud")) {
1727 					type->Sound.StepMud.Name = LuaToString(l, -1, k + 1);
1728 				} else if (!strcmp(value, "step-stone")) {
1729 					type->Sound.StepStone.Name = LuaToString(l, -1, k + 1);
1730 				} else if (!strcmp(value, "used")) {
1731 					type->Sound.Used.Name = LuaToString(l, -1, k + 1);
1732 				//Wyrmgus end
1733 				} else if (!strcmp(value, "build")) {
1734 					type->Sound.Build.Name = LuaToString(l, -1, k + 1);
1735 				} else if (!strcmp(value, "ready")) {
1736 					type->Sound.Ready.Name = LuaToString(l, -1, k + 1);
1737 				} else if (!strcmp(value, "repair")) {
1738 					type->Sound.Repair.Name = LuaToString(l, -1, k + 1);
1739 				} else if (!strcmp(value, "harvest")) {
1740 					const std::string name = LuaToString(l, -1, k + 1);
1741 					++k;
1742 					const int resId = GetResourceIdByName(l, name.c_str());
1743 					type->Sound.Harvest[resId].Name = LuaToString(l, -1, k + 1);
1744 				} else if (!strcmp(value, "help")) {
1745 					type->Sound.Help.Name = LuaToString(l, -1, k + 1);
1746 				} else if (!strcmp(value, "dead")) {
1747 					int death;
1748 
1749 					const std::string name = LuaToString(l, -1, k + 1);
1750 					for (death = 0; death < ANIMATIONS_DEATHTYPES; ++death) {
1751 						if (name == ExtraDeathTypes[death]) {
1752 							++k;
1753 							break;
1754 						}
1755 					}
1756 					if (death == ANIMATIONS_DEATHTYPES) {
1757 						type->Sound.Dead[ANIMATIONS_DEATHTYPES].Name = name;
1758 					} else {
1759 						type->Sound.Dead[death].Name = LuaToString(l, -1, k + 1);
1760 					}
1761 				} else {
1762 					LuaError(l, "Unsupported sound tag: %s" _C_ value);
1763 				}
1764 			}
1765 		//Wyrmgus start
1766 		} else if (!strcmp(value, "Class")) {
1767 			if (type->Class != -1)  {
1768 				ClassUnitTypes[type->Class].erase(std::remove(ClassUnitTypes[type->Class].begin(), ClassUnitTypes[type->Class].end(), type), ClassUnitTypes[type->Class].end());
1769 			}
1770 
1771 			type->Class = GetOrAddUnitTypeClassIndexByName(LuaToString(l, -1));
1772 
1773 			if (type->Class != -1 && std::find(ClassUnitTypes[type->Class].begin(), ClassUnitTypes[type->Class].end(), type) == ClassUnitTypes[type->Class].end()) {
1774 				ClassUnitTypes[type->Class].push_back(type);
1775 			}
1776 		} else if (!strcmp(value, "Civilization")) {
1777 			std::string civilization_name = LuaToString(l, -1);
1778 			CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1779 			if (civilization) {
1780 				type->Civilization = civilization->ID;
1781 			}
1782 		} else if (!strcmp(value, "Faction")) {
1783 			std::string faction_name = LuaToString(l, -1);
1784 			CFaction *faction = PlayerRaces.GetFaction(faction_name);
1785 			if (faction) {
1786 				type->Faction = faction->ID;
1787 			} else {
1788 				LuaError(l, "Faction \"%s\" doesn't exist." _C_ faction_name.c_str());
1789 			}
1790 		} else if (!strcmp(value, "Description")) {
1791 			type->Description = LuaToString(l, -1);
1792 		} else if (!strcmp(value, "Quote")) {
1793 			type->Quote = LuaToString(l, -1);
1794 		} else if (!strcmp(value, "Gender")) {
1795 			type->DefaultStat.Variables[GENDER_INDEX].Enable = 1;
1796 			type->DefaultStat.Variables[GENDER_INDEX].Value = GetGenderIdByName(LuaToString(l, -1));
1797 			type->DefaultStat.Variables[GENDER_INDEX].Max = type->DefaultStat.Variables[GENDER_INDEX].Value;
1798 		} else if (!strcmp(value, "Background")) {
1799 			type->Background = LuaToString(l, -1);
1800 		} else if (!strcmp(value, "RequirementsString")) {
1801 			type->RequirementsString = LuaToString(l, -1);
1802 		} else if (!strcmp(value, "ExperienceRequirementsString")) {
1803 			type->ExperienceRequirementsString = LuaToString(l, -1);
1804 		} else if (!strcmp(value, "BuildingRulesString")) {
1805 			type->BuildingRulesString = LuaToString(l, -1);
1806 		} else if (!strcmp(value, "TrainQuantity")) {
1807 			type->TrainQuantity = LuaToNumber(l, -1);
1808 		} else if (!strcmp(value, "CostModifier")) {
1809 			type->CostModifier = LuaToNumber(l, -1);
1810 		} else if (!strcmp(value, "Elixir")) {
1811 			std::string elixir_ident = LuaToString(l, -1);
1812 			int elixir_id = UpgradeIdByIdent(elixir_ident);
1813 			if (elixir_id != -1) {
1814 				type->Elixir = AllUpgrades[elixir_id];
1815 			} else {
1816 				type->Elixir = CUpgrade::New(elixir_ident); //if this elixir upgrade doesn't exist, define it now (this is useful if the unit type is defined before the upgrade)
1817 			}
1818 		} else if (!strcmp(value, "SoldUnits")) {
1819 			const int args = lua_rawlen(l, -1);
1820 			for (int j = 0; j < args; ++j) {
1821 				int sold_unit_type_id = UnitTypeIdByIdent(LuaToString(l, -1, j + 1));
1822 				if (sold_unit_type_id != -1) {
1823 					type->SoldUnits.push_back(UnitTypes[sold_unit_type_id]);
1824 				} else { // Error
1825 					LuaError(l, "incorrect sold unit unit-type");
1826 				}
1827 			}
1828 		} else if (!strcmp(value, "SpawnUnits")) {
1829 			const int args = lua_rawlen(l, -1);
1830 			for (int j = 0; j < args; ++j) {
1831 				int sold_unit_type_id = UnitTypeIdByIdent(LuaToString(l, -1, j + 1));
1832 				if (sold_unit_type_id != -1) {
1833 					type->SpawnUnits.push_back(UnitTypes[sold_unit_type_id]);
1834 				} else { // Error
1835 					LuaError(l, "incorrect spawn unit unit-type");
1836 				}
1837 			}
1838 		} else if (!strcmp(value, "Drops")) {
1839 			const int args = lua_rawlen(l, -1);
1840 			for (int j = 0; j < args; ++j) {
1841 				int drop_type_id = UnitTypeIdByIdent(LuaToString(l, -1, j + 1));
1842 				if (drop_type_id != -1) {
1843 					type->Drops.push_back(UnitTypes[drop_type_id]);
1844 				} else { // Error
1845 					LuaError(l, "incorrect drop unit-type");
1846 				}
1847 			}
1848 		} else if (!strcmp(value, "AiDrops")) {
1849 			const int args = lua_rawlen(l, -1);
1850 			for (int j = 0; j < args; ++j) {
1851 				int drop_type_id = UnitTypeIdByIdent(LuaToString(l, -1, j + 1));
1852 				if (drop_type_id != -1) {
1853 					type->AiDrops.push_back(UnitTypes[drop_type_id]);
1854 				} else { // Error
1855 					LuaError(l, "incorrect drop unit-type");
1856 				}
1857 			}
1858 		} else if (!strcmp(value, "DropSpells")) {
1859 			const int args = lua_rawlen(l, -1);
1860 			for (int j = 0; j < args; ++j) {
1861 				value = LuaToString(l, -1, j + 1);
1862 				CSpell *spell = CSpell::GetSpell(value);
1863 				if (spell != nullptr) {
1864 					type->DropSpells.push_back(spell);
1865 				}
1866 			}
1867 		} else if (!strcmp(value, "Affixes")) {
1868 			const int args = lua_rawlen(l, -1);
1869 			for (int j = 0; j < args; ++j) {
1870 				std::string affix_ident = LuaToString(l, -1, j + 1);
1871 				int affix_id = UpgradeIdByIdent(affix_ident);
1872 				if (affix_id != -1) {
1873 					type->Affixes.push_back(AllUpgrades[affix_id]);
1874 				} else {
1875 					type->Affixes.push_back(CUpgrade::New(affix_ident)); //if this affix doesn't exist, define it now (this is useful if the unit type is defined before the upgrade)
1876 				}
1877 			}
1878 		} else if (!strcmp(value, "Traits")) {
1879 			type->Traits.clear(); // remove previously defined traits, to allow unit types to not inherit traits from their parent unit types
1880 			const int args = lua_rawlen(l, -1);
1881 			for (int j = 0; j < args; ++j) {
1882 				std::string trait_ident = LuaToString(l, -1, j + 1);
1883 				int trait_id = UpgradeIdByIdent(trait_ident);
1884 				if (trait_id != -1) {
1885 					type->Traits.push_back(AllUpgrades[trait_id]);
1886 				} else {
1887 					type->Traits.push_back(CUpgrade::New(trait_ident)); //if this trait doesn't exist, define it now (this is useful if the unit type is defined before the upgrade)
1888 				}
1889 			}
1890 		} else if (!strcmp(value, "StartingAbilities")) {
1891 			const int args = lua_rawlen(l, -1);
1892 			for (int j = 0; j < args; ++j) {
1893 				std::string ability_ident = LuaToString(l, -1, j + 1);
1894 				int ability_id = UpgradeIdByIdent(ability_ident);
1895 				if (ability_id != -1) {
1896 					type->StartingAbilities.push_back(AllUpgrades[ability_id]);
1897 				} else {
1898 					LuaError(l, "Ability \"%s\" doesn't exist." _C_ ability_ident.c_str());
1899 				}
1900 			}
1901 		} else if (!strcmp(value, "ItemClass")) {
1902 			type->ItemClass = GetItemClassIdByName(LuaToString(l, -1));
1903 		} else if (!strcmp(value, "Species")) {
1904 			type->Species = GetSpecies(LuaToString(l, -1));
1905 			if (!type->Species) {
1906 				LuaError(l, "Species doesn't exist.");
1907 			}
1908 			type->Species->Type = type;
1909 		} else if (!strcmp(value, "TerrainType")) {
1910 			type->TerrainType = CTerrainType::GetTerrainType(LuaToString(l, -1));
1911 			if (!type->TerrainType) {
1912 				LuaError(l, "Terrain type doesn't exist.");
1913 			}
1914 			type->TerrainType->UnitType = type;
1915 		} else if (!strcmp(value, "WeaponClasses")) {
1916 			type->WeaponClasses.clear();
1917 			const int args = lua_rawlen(l, -1);
1918 			for (int j = 0; j < args; ++j) {
1919 				int weapon_class_id = GetItemClassIdByName(LuaToString(l, -1, j + 1));
1920 				if (weapon_class_id != -1) {
1921 					type->WeaponClasses.push_back(weapon_class_id);
1922 				} else { // Error
1923 					LuaError(l, "incorrect weapon class");
1924 				}
1925 			}
1926 		} else if (!strcmp(value, "PersonalNames")) {
1927 			type->PersonalNames.clear();
1928 			const int args = lua_rawlen(l, -1);
1929 			for (int j = 0; j < args; ++j) {
1930 				int gender_id = GetGenderIdByName(LuaToString(l, -1, j + 1));
1931 				if (gender_id == -1) {
1932 					gender_id = NoGender;
1933 				} else {
1934 					++j;
1935 				}
1936 
1937 				type->PersonalNames[gender_id].push_back(LuaToString(l, -1, j + 1));
1938 			}
1939 		} else if (!strcmp(value, "Mod")) {
1940 			type->Mod = LuaToString(l, -1);
1941 		//Wyrmgus end
1942 		} else {
1943 			int index = UnitTypeVar.VariableNameLookup[value];
1944 			if (index != -1) { // valid index
1945 				if (lua_isboolean(l, -1)) {
1946 					type->DefaultStat.Variables[index].Enable = LuaToBoolean(l, -1);
1947 				} else if (lua_istable(l, -1)) {
1948 					DefineVariableField(l, type->DefaultStat.Variables + index, -1);
1949 				} else if (lua_isnumber(l, -1)) {
1950 					type->DefaultStat.Variables[index].Enable = 1;
1951 					type->DefaultStat.Variables[index].Value = LuaToNumber(l, -1);
1952 					type->DefaultStat.Variables[index].Max = LuaToNumber(l, -1);
1953 				} else { // Error
1954 					LuaError(l, "incorrect argument for the variable in unittype");
1955 				}
1956 				continue;
1957 			}
1958 
1959 			if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
1960 				type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
1961 			}
1962 
1963 			index = UnitTypeVar.BoolFlagNameLookup[value];
1964 			if (index != -1) {
1965 				if (lua_isnumber(l, -1)) {
1966 					type->BoolFlag[index].value = (LuaToNumber(l, -1) != 0);
1967 				} else {
1968 					type->BoolFlag[index].value = LuaToBoolean(l, -1);
1969 				}
1970 			} else {
1971 				printf("\n%s\n", type->Name.c_str());
1972 				LuaError(l, "Unsupported tag: %s" _C_ value);
1973 			}
1974 		}
1975 	}
1976 
1977 	//Wyrmgus start
1978 	if (type->Class != -1) { //if class is defined, then use this unit type to help build the classes table, and add this unit to the civilization class table (if the civilization is defined)
1979 		int class_id = type->Class;
1980 
1981 		//see if this unit type is set as the civilization class unit type or the faction class unit type of any civilization/class (or faction/class) combination, and remove it from there (to not create problems with redefinitions)
1982 		for (int i = 0; i < MAX_RACES; ++i) {
1983 			for (std::map<int, int>::reverse_iterator iterator = PlayerRaces.CivilizationClassUnitTypes[i].rbegin(); iterator != PlayerRaces.CivilizationClassUnitTypes[i].rend(); ++iterator) {
1984 				if (iterator->second == type->Slot) {
1985 					PlayerRaces.CivilizationClassUnitTypes[i].erase(iterator->first);
1986 					break;
1987 				}
1988 			}
1989 		}
1990 		for (size_t i = 0; i < PlayerRaces.Factions.size(); ++i) {
1991 			for (std::map<int, int>::reverse_iterator iterator = PlayerRaces.Factions[i]->ClassUnitTypes.rbegin(); iterator != PlayerRaces.Factions[i]->ClassUnitTypes.rend(); ++iterator) {
1992 				if (iterator->second == type->Slot) {
1993 					PlayerRaces.Factions[i]->ClassUnitTypes.erase(iterator->first);
1994 					break;
1995 				}
1996 			}
1997 		}
1998 
1999 		if (type->Civilization != -1) {
2000 			int civilization_id = type->Civilization;
2001 
2002 			if (type->Faction != -1) {
2003 				int faction_id = type->Faction;
2004 				if (faction_id != -1 && class_id != -1) {
2005 					PlayerRaces.Factions[faction_id]->ClassUnitTypes[class_id] = type->Slot;
2006 				}
2007 			} else {
2008 				if (civilization_id != -1 && class_id != -1) {
2009 					PlayerRaces.CivilizationClassUnitTypes[civilization_id][class_id] = type->Slot;
2010 				}
2011 			}
2012 		}
2013 	}
2014 	//Wyrmgus end
2015 
2016 	// If number of directions is not specified, make a guess
2017 	// Building have 1 direction and units 8
2018 	if (type->BoolFlag[BUILDING_INDEX].value && type->NumDirections == 0) {
2019 		type->NumDirections = 1;
2020 	} else if (type->NumDirections == 0) {
2021 		type->NumDirections = 8;
2022 	}
2023 
2024 	//Wyrmgus start
2025 	//unit type's level must be at least 1
2026 	if (type->DefaultStat.Variables[LEVEL_INDEX].Value == 0) {
2027 		type->DefaultStat.Variables[LEVEL_INDEX].Enable = 1;
2028 		type->DefaultStat.Variables[LEVEL_INDEX].Value = 1;
2029 		type->DefaultStat.Variables[LEVEL_INDEX].Max = 1;
2030 	}
2031 	//Wyrmgus end
2032 
2033 	// FIXME: try to simplify/combine the flags instead
2034 	if (type->MouseAction == MouseActionAttack && !type->CanAttack) {
2035 		LuaError(l, "Unit-type '%s': right-attack is set, but can-attack is not\n" _C_ type->Name.c_str());
2036 	}
2037 	type->UpdateDefaultBoolFlags();
2038 	//Wyrmgus start
2039 	if (GameRunning || Editor.Running == EditorEditing) {
2040 		InitUnitType(*type);
2041 		LoadUnitType(*type);
2042 	}
2043 	//Wyrmgus end
2044 	//Wyrmgus start
2045 //	if (!CclInConfigFile) {
2046 	if (!CclInConfigFile || GameRunning || Editor.Running == EditorEditing) {
2047 	//Wyrmgus end
2048 		UpdateUnitStats(*type, 1);
2049 	}
2050 	//Wyrmgus start
2051 	if (Editor.Running == EditorEditing && std::find(Editor.UnitTypes.begin(), Editor.UnitTypes.end(), type->Ident) == Editor.UnitTypes.end()) {
2052 		Editor.UnitTypes.push_back(type->Ident);
2053 		RecalculateShownUnits();
2054 	}
2055 
2056 	for (size_t i = 0; i < type->Trains.size(); ++i) {
2057 		std::string button_definition = "DefineButton({\n";
2058 		button_definition += "\tPos = " + std::to_string((long long) type->Trains[i]->ButtonPos) + ",\n";
2059 		if (type->Trains[i]->ButtonLevel) {
2060 			button_definition += "\tLevel = " + type->Trains[i]->ButtonLevel->Ident + ",\n";
2061 		}
2062 		button_definition += "\tAction = ";
2063 		if (type->Trains[i]->BoolFlag[BUILDING_INDEX].value) {
2064 			button_definition += "\"build\"";
2065 		} else {
2066 			button_definition += "\"train-unit\"";
2067 		}
2068 		button_definition += ",\n";
2069 		button_definition += "\tValue = \"" + type->Trains[i]->Ident + "\",\n";
2070 		if (!type->Trains[i]->ButtonPopup.empty()) {
2071 			button_definition += "\tPopup = \"" + type->Trains[i]->ButtonPopup + "\",\n";
2072 		}
2073 		button_definition += "\tKey = \"" + type->Trains[i]->ButtonKey + "\",\n";
2074 		button_definition += "\tHint = \"" + type->Trains[i]->ButtonHint + "\",\n";
2075 		button_definition += "\tForUnit = {\"" + type->Ident + "\"},\n";
2076 		button_definition += "})";
2077 		CclCommand(button_definition);
2078 	}
2079 
2080 	if (type->CanMove()) {
2081 		std::string button_definition = "DefineButton({\n";
2082 		button_definition += "\tPos = 1,\n";
2083 		button_definition += "\tAction = \"move\",\n";
2084 		button_definition += "\tPopup = \"popup-commands\",\n";
2085 		button_definition += "\tKey = \"m\",\n";
2086 		button_definition += "\tHint = _(\"~!Move\"),\n";
2087 		button_definition += "\tForUnit = {\"" + type->Ident + "\"},\n";
2088 		button_definition += "})";
2089 		CclCommand(button_definition);
2090 	}
2091 
2092 	if (type->CanMove()) {
2093 		std::string button_definition = "DefineButton({\n";
2094 		button_definition += "\tPos = 2,\n";
2095 		button_definition += "\tAction = \"stop\",\n";
2096 		button_definition += "\tPopup = \"popup-commands\",\n";
2097 		button_definition += "\tKey = \"s\",\n";
2098 		button_definition += "\tHint = _(\"~!Stop\"),\n";
2099 		button_definition += "\tForUnit = {\"" + type->Ident + "\"},\n";
2100 		button_definition += "})";
2101 		CclCommand(button_definition);
2102 	}
2103 
2104 	if (type->CanMove() && type->CanAttack) {
2105 		std::string button_definition = "DefineButton({\n";
2106 		button_definition += "\tPos = 3,\n";
2107 		button_definition += "\tAction = \"attack\",\n";
2108 		button_definition += "\tPopup = \"popup-commands\",\n";
2109 		button_definition += "\tKey = \"a\",\n";
2110 		button_definition += "\tHint = _(\"~!Attack\"),\n";
2111 		button_definition += "\tForUnit = {\"" + type->Ident + "\"},\n";
2112 		button_definition += "})";
2113 		CclCommand(button_definition);
2114 	}
2115 
2116 	if (type->CanMove() && ((!type->BoolFlag[COWARD_INDEX].value && type->CanAttack) || type->UnitType == UnitTypeFly)) {
2117 		std::string button_definition = "DefineButton({\n";
2118 		button_definition += "\tPos = 4,\n";
2119 		button_definition += "\tAction = \"patrol\",\n";
2120 		button_definition += "\tPopup = \"popup-commands\",\n";
2121 		button_definition += "\tKey = \"p\",\n";
2122 		button_definition += "\tHint = _(\"~!Patrol\"),\n";
2123 		button_definition += "\tForUnit = {\"" + type->Ident + "\"},\n";
2124 		button_definition += "})";
2125 		CclCommand(button_definition);
2126 	}
2127 
2128 	if (type->CanMove() && !type->BoolFlag[COWARD_INDEX].value && type->CanAttack && !(type->CanTransport() && type->BoolFlag[ATTACKFROMTRANSPORTER_INDEX].value)) {
2129 		std::string button_definition = "DefineButton({\n";
2130 		button_definition += "\tPos = 5,\n";
2131 		button_definition += "\tAction = \"stand-ground\",\n";
2132 		button_definition += "\tPopup = \"popup-commands\",\n";
2133 		button_definition += "\tKey = \"t\",\n";
2134 		button_definition += "\tHint = _(\"S~!tand Ground\"),\n";
2135 		button_definition += "\tForUnit = {\"" + type->Ident + "\"},\n";
2136 		button_definition += "})";
2137 		CclCommand(button_definition);
2138 	}
2139 
2140 	// make units allowed by default
2141 	for (int i = 0; i < PlayerMax; ++i) {
2142 		AllowUnitId(Players[i], type->Slot, 65536);
2143 	}
2144 	//Wyrmgus end
2145 
2146 	type->Initialized = true;
2147 
2148 	return 0;
2149 }
2150 
2151 /**
2152 **  Parse unit-stats.
2153 **
2154 **  @param l  Lua state.
2155 */
CclDefineUnitStats(lua_State * l)2156 static int CclDefineUnitStats(lua_State *l)
2157 {
2158 	CUnitType *type = UnitTypeByIdent(LuaToString(l, 1));
2159 	const int playerId = LuaToNumber(l, 2);
2160 
2161 	Assert(type);
2162 	Assert(playerId < PlayerMax);
2163 
2164 	CUnitStats *stats = &type->Stats[playerId];
2165 	if (!stats->Variables) {
2166 		stats->Variables = new CVariable[UnitTypeVar.GetNumberVariable()];
2167 	}
2168 
2169 	// Parse the list: (still everything could be changed!)
2170 	const int args = lua_rawlen(l, 3);
2171 	for (int j = 0; j < args; ++j) {
2172 		const char *value = LuaToString(l, 3, j + 1);
2173 		++j;
2174 
2175 		if (!strcmp(value, "costs")) {
2176 			lua_rawgeti(l, 3, j + 1);
2177 			if (!lua_istable(l, -1)) {
2178 				LuaError(l, "incorrect argument");
2179 			}
2180 			const int subargs = lua_rawlen(l, -1);
2181 
2182 			for (int k = 0; k < subargs; ++k) {
2183 				lua_rawgeti(l, 3, j + 1);
2184 				value = LuaToString(l, -1, k + 1);
2185 				++k;
2186 				const int resId = GetResourceIdByName(l, value);
2187 				stats->Costs[resId] = LuaToNumber(l, -1, k + 1);
2188 				lua_pop(l, 1);
2189 			}
2190 		} else if (!strcmp(value, "storing")) {
2191 			lua_rawgeti(l, 3, j + 1);
2192 			if (!lua_istable(l, -1)) {
2193 				LuaError(l, "incorrect argument");
2194 			}
2195 			const int subargs = lua_rawlen(l, -1);
2196 
2197 			for (int k = 0; k < subargs; ++k) {
2198 				lua_rawgeti(l, 3, j + 1);
2199 				value = LuaToString(l, -1, k + 1);
2200 				++k;
2201 				const int resId = GetResourceIdByName(l, value);
2202 				stats->Storing[resId] = LuaToNumber(l, -1, k + 1);
2203 				lua_pop(l, 1);
2204 			}
2205 		} else if (!strcmp(value, "improve-production")) {
2206 			lua_rawgeti(l, 3, j + 1);
2207 			if (!lua_istable(l, -1)) {
2208 				LuaError(l, "incorrect argument");
2209 			}
2210 			const int subargs = lua_rawlen(l, -1);
2211 
2212 			for (int k = 0; k < subargs; ++k) {
2213 				lua_rawgeti(l, 3, j + 1);
2214 				value = LuaToString(l, -1, k + 1);
2215 				++k;
2216 				const int resId = GetResourceIdByName(l, value);
2217 				stats->ImproveIncomes[resId] = LuaToNumber(l, -1, k + 1);
2218 				lua_pop(l, 1);
2219 			}
2220 		//Wyrmgus start
2221 		} else if (!strcmp(value, "resource-demand")) {
2222 			lua_rawgeti(l, 3, j + 1);
2223 			if (!lua_istable(l, -1)) {
2224 				LuaError(l, "incorrect argument");
2225 			}
2226 			const int subargs = lua_rawlen(l, -1);
2227 
2228 			for (int k = 0; k < subargs; ++k) {
2229 				lua_rawgeti(l, 3, j + 1);
2230 				value = LuaToString(l, -1, k + 1);
2231 				++k;
2232 				const int resId = GetResourceIdByName(l, value);
2233 				stats->ResourceDemand[resId] = LuaToNumber(l, -1, k + 1);
2234 				lua_pop(l, 1);
2235 			}
2236 		} else if (!strcmp(value, "unit-stock")) {
2237 			lua_rawgeti(l, 3, j + 1);
2238 			if (!lua_istable(l, -1)) {
2239 				LuaError(l, "incorrect argument");
2240 			}
2241 			const int subargs = lua_rawlen(l, -1);
2242 
2243 			for (int k = 0; k < subargs; ++k) {
2244 				lua_rawgeti(l, 3, j + 1);
2245 				CUnitType *unit_type = UnitTypeByIdent(LuaToString(l, -1, k + 1));
2246 				if (!unit_type) {
2247 					LuaError(l, "Unit type doesn't exist.");
2248 				}
2249 				++k;
2250 				stats->SetUnitStock(unit_type, LuaToNumber(l, -1, k + 1));
2251 				lua_pop(l, 1);
2252 			}
2253 		//Wyrmgus end
2254 		} else {
2255 			int i = UnitTypeVar.VariableNameLookup[value];// User variables
2256 			if (i != -1) { // valid index
2257 				lua_rawgeti(l, 3, j + 1);
2258 				if (lua_istable(l, -1)) {
2259 					DefineVariableField(l, stats->Variables + i, -1);
2260 				} else if (lua_isnumber(l, -1)) {
2261 					stats->Variables[i].Enable = 1;
2262 					stats->Variables[i].Value = LuaToNumber(l, -1);
2263 					stats->Variables[i].Max = LuaToNumber(l, -1);
2264 				} else { // Error
2265 					LuaError(l, "incorrect argument for the variable in unittype");
2266 				}
2267 				continue;
2268 			}
2269 			// This leaves a half initialized unit
2270 			LuaError(l, "Unsupported tag: %s" _C_ value);
2271 		}
2272 	}
2273 	return 0;
2274 }
2275 
2276 // ----------------------------------------------------------------------------
2277 
2278 /**
2279 **  Access unit-type object
2280 **
2281 **  @param l  Lua state.
2282 */
CclGetUnitType(lua_State * l)2283 CUnitType *CclGetUnitType(lua_State *l)
2284 {
2285 	//Wyrmgus start
2286 	if (lua_isnil(l, -1)) {
2287 		return nullptr;
2288 	}
2289 	//Wyrmgus end
2290 
2291 	// Be kind allow also strings or symbols
2292 	if (lua_isstring(l, -1)) {
2293 		const char *str = LuaToString(l, -1);
2294 		return UnitTypeByIdent(str);
2295 	} else if (lua_isuserdata(l, -1)) {
2296 		LuaUserData *data = (LuaUserData *)lua_touserdata(l, -1);
2297 		if (data->Type == LuaUnitType) {
2298 			return (CUnitType *)data->Data;
2299 		}
2300 	}
2301 	LuaError(l, "CclGetUnitType: not a unit-type");
2302 	return nullptr;
2303 }
2304 
2305 /**
2306 **  Get unit-type structure.
2307 **
2308 **  @param l  Lua state.
2309 **
2310 **  @return   Unit-type structure.
2311 */
CclUnitType(lua_State * l)2312 static int CclUnitType(lua_State *l)
2313 {
2314 	LuaCheckArgs(l, 1);
2315 
2316 	const char *str = LuaToString(l, 1);
2317 	CUnitType *type = UnitTypeByIdent(str);
2318 	LuaUserData *data = (LuaUserData *)lua_newuserdata(l, sizeof(LuaUserData));
2319 	data->Type = LuaUnitType;
2320 	data->Data = type;
2321 	return 1;
2322 }
2323 
2324 /**
2325 **  Get all unit-type structures.
2326 **
2327 **  @param l  Lua state.
2328 **
2329 **  @return   An array of all unit-type structures.
2330 */
CclUnitTypeArray(lua_State * l)2331 static int CclUnitTypeArray(lua_State *l)
2332 {
2333 	LuaCheckArgs(l, 0);
2334 
2335 	lua_newtable(l);
2336 
2337 	for (std::vector<CUnitType *>::size_type i = 0; i < UnitTypes.size(); ++i) {
2338 		LuaUserData *data = (LuaUserData *)lua_newuserdata(l, sizeof(LuaUserData));
2339 		data->Type = LuaUnitType;
2340 		data->Data = UnitTypes[i];
2341 		lua_rawseti(l, 1, i + 1);
2342 	}
2343 	return 1;
2344 }
2345 
2346 /**
2347 **  Get the ident of the unit-type structure.
2348 **
2349 **  @param l  Lua state.
2350 **
2351 **  @return   The identifier of the unit-type.
2352 */
CclGetUnitTypeIdent(lua_State * l)2353 static int CclGetUnitTypeIdent(lua_State *l)
2354 {
2355 	LuaCheckArgs(l, 1);
2356 
2357 	const CUnitType *type = CclGetUnitType(l);
2358 	if (type) {
2359 		lua_pushstring(l, type->Ident.c_str());
2360 	} else {
2361 		LuaError(l, "unit '%s' not defined" _C_ LuaToString(l, -1));
2362 	}
2363 	return 1;
2364 }
2365 
2366 /**
2367 **  Get the name of the unit-type structure.
2368 **
2369 **  @param l  Lua state.
2370 **
2371 **  @return   The name of the unit-type.
2372 */
CclGetUnitTypeName(lua_State * l)2373 static int CclGetUnitTypeName(lua_State *l)
2374 {
2375 	LuaCheckArgs(l, 1);
2376 
2377 	const CUnitType *type = CclGetUnitType(l);
2378 	lua_pushstring(l, type->Name.c_str());
2379 	return 1;
2380 }
2381 
2382 /**
2383 **  Set the name of the unit-type structure.
2384 **
2385 **  @param l  Lua state.
2386 **
2387 **  @return   The name of the unit-type.
2388 */
CclSetUnitTypeName(lua_State * l)2389 static int CclSetUnitTypeName(lua_State *l)
2390 {
2391 	LuaCheckArgs(l, 2);
2392 
2393 	lua_pushvalue(l, 1);
2394 	CUnitType *type = CclGetUnitType(l);
2395 	lua_pop(l, 1);
2396 	type->Name = LuaToString(l, 2);
2397 
2398 	lua_pushvalue(l, 2);
2399 	return 1;
2400 }
2401 
2402 /**
2403 **  Get unit type data.
2404 **
2405 **  @param l  Lua state.
2406 */
CclGetUnitTypeData(lua_State * l)2407 static int CclGetUnitTypeData(lua_State *l)
2408 {
2409 	if (lua_gettop(l) < 2) {
2410 		LuaError(l, "incorrect argument");
2411 	}
2412 	std::string ident = LuaToString(l, 1);
2413 	const CUnitType *type = UnitTypeByIdent(ident.c_str());
2414 	const char *data = LuaToString(l, 2);
2415 
2416 	if (!type) {
2417 		LuaError(l, "Invalid unit type: \"%s\"" _C_ ident.c_str());
2418 	}
2419 
2420 	if (!strcmp(data, "Name")) {
2421 		lua_pushstring(l, type->Name.c_str());
2422 		return 1;
2423 	//Wyrmgus start
2424 	} else if (!strcmp(data, "NamePlural")) {
2425 		lua_pushstring(l, type->GetNamePlural().c_str());
2426 		return 1;
2427 	} else if (!strcmp(data, "Parent")) {
2428 		lua_pushstring(l, type->Parent->Ident.c_str());
2429 		return 1;
2430 	} else if (!strcmp(data, "Class")) {
2431 		if (type->ItemClass != -1) {
2432 			lua_pushstring(l, GetItemClassNameById(type->ItemClass).c_str());
2433 		} else if (type->Class != -1) {
2434 			lua_pushstring(l, UnitTypeClasses[type->Class].c_str());
2435 		} else {
2436 			lua_pushstring(l, "");
2437 		}
2438 		return 1;
2439 	} else if (!strcmp(data, "Civilization")) {
2440 		if (type->Civilization != -1) {
2441 			lua_pushstring(l, PlayerRaces.Name[type->Civilization].c_str());
2442 		} else {
2443 			lua_pushstring(l, "");
2444 		}
2445 		return 1;
2446 	} else if (!strcmp(data, "Faction")) {
2447 		if (type->Faction != -1) {
2448 			lua_pushstring(l, PlayerRaces.Factions[type->Faction]->Ident.c_str());
2449 		} else {
2450 			lua_pushstring(l, "");
2451 		}
2452 		return 1;
2453 	} else if (!strcmp(data, "Description")) {
2454 		lua_pushstring(l, type->Description.c_str());
2455 		return 1;
2456 	} else if (!strcmp(data, "Quote")) {
2457 		lua_pushstring(l, type->Quote.c_str());
2458 		return 1;
2459 	} else if (!strcmp(data, "Background")) {
2460 		lua_pushstring(l, type->Background.c_str());
2461 		return 1;
2462 	} else if (!strcmp(data, "RequirementsString")) {
2463 		lua_pushstring(l, type->RequirementsString.c_str());
2464 		return 1;
2465 	} else if (!strcmp(data, "ExperienceRequirementsString")) {
2466 		lua_pushstring(l, type->ExperienceRequirementsString.c_str());
2467 		return 1;
2468 	} else if (!strcmp(data, "BuildingRulesString")) {
2469 		lua_pushstring(l, type->BuildingRulesString.c_str());
2470 		return 1;
2471 	} else if (!strcmp(data, "Image")) {
2472 		lua_pushstring(l, type->File.c_str());
2473 		return 1;
2474 	//Wyrmgus start
2475 	} else if (!strcmp(data, "Shadow")) {
2476 		lua_pushstring(l, type->ShadowFile.c_str());
2477 		return 1;
2478 	//Wyrmgus end
2479 	} else if (!strcmp(data, "Width")) {
2480 		lua_pushnumber(l, type->Width);
2481 		return 1;
2482 	} else if (!strcmp(data, "Height")) {
2483 		lua_pushnumber(l, type->Height);
2484 		return 1;
2485 	} else if (!strcmp(data, "Animations")) {
2486 		if (type->Animations != nullptr) {
2487 			lua_pushstring(l, type->Animations->Ident.c_str());
2488 		} else {
2489 			lua_pushstring(l, "");
2490 		}
2491 		return 1;
2492 	//Wyrmgus end
2493 	} else if (!strcmp(data, "Icon")) {
2494 		lua_pushstring(l, type->Icon.Name.c_str());
2495 		return 1;
2496 	} else if (!strcmp(data, "Costs")) {
2497 		LuaCheckArgs(l, 3);
2498 		const std::string res = LuaToString(l, 3);
2499 		const int resId = GetResourceIdByName(l, res.c_str());
2500 		if (!GameRunning && Editor.Running != EditorEditing) {
2501 			lua_pushnumber(l, type->DefaultStat.Costs[resId]);
2502 		} else {
2503 			lua_pushnumber(l, type->MapDefaultStat.Costs[resId]);
2504 		}
2505 		return 1;
2506 	} else if (!strcmp(data, "ImproveProduction")) {
2507 		LuaCheckArgs(l, 3);
2508 		const std::string res = LuaToString(l, 3);
2509 		const int resId = GetResourceIdByName(l, res.c_str());
2510 		if (!GameRunning && Editor.Running != EditorEditing) {
2511 			lua_pushnumber(l, type->DefaultStat.ImproveIncomes[resId]);
2512 		} else {
2513 			lua_pushnumber(l, type->MapDefaultStat.ImproveIncomes[resId]);
2514 		}
2515 		return 1;
2516 	//Wyrmgus start
2517 	} else if (!strcmp(data, "UnitStock")) {
2518 		LuaCheckArgs(l, 3);
2519 		CUnitType *unit_type = UnitTypeByIdent(LuaToString(l, 3));
2520 		if (!GameRunning && Editor.Running != EditorEditing) {
2521 			lua_pushnumber(l, type->DefaultStat.GetUnitStock(unit_type));
2522 		} else {
2523 			lua_pushnumber(l, type->MapDefaultStat.GetUnitStock(unit_type));
2524 		}
2525 		return 1;
2526 	} else if (!strcmp(data, "TrainQuantity")) {
2527 		lua_pushnumber(l, type->TrainQuantity);
2528 		return 1;
2529 	} else if (!strcmp(data, "CostModifier")) {
2530 		lua_pushnumber(l, type->CostModifier);
2531 		return 1;
2532 	//Wyrmgus end
2533 	} else if (!strcmp(data, "DrawLevel")) {
2534 		lua_pushnumber(l, type->DrawLevel);
2535 		return 1;
2536 	} else if (!strcmp(data, "TileWidth")) {
2537 		lua_pushnumber(l, type->TileSize.x);
2538 		return 1;
2539 	} else if (!strcmp(data, "TileHeight")) {
2540 		lua_pushnumber(l, type->TileSize.y);
2541 		return 1;
2542 	//Wyrmgus start
2543 	/*
2544 	} else if (!strcmp(data, "ComputerReactionRange")) {
2545 		lua_pushnumber(l, type->ReactRangeComputer);
2546 		return 1;
2547 	} else if (!strcmp(data, "PersonReactionRange")) {
2548 		lua_pushnumber(l, type->ReactRangePerson);
2549 		return 1;
2550 	*/
2551 	} else if (!strcmp(data, "Species")) {
2552 		if (type->Species != nullptr) {
2553 			lua_pushstring(l, type->Species->Ident.c_str());
2554 		} else {
2555 			lua_pushstring(l, "");
2556 		}
2557 		return 1;
2558 	} else if (!strcmp(data, "ItemClass")) {
2559 		lua_pushstring(l, GetItemClassNameById(type->ItemClass).c_str());
2560 		return 1;
2561 	} else if (!strcmp(data, "ItemSlot")) {
2562 		const int item_slot = GetItemClassSlot(type->ItemClass);
2563 		if (item_slot != -1) {
2564 			lua_pushstring(l, GetItemSlotNameById(item_slot).c_str());
2565 		} else {
2566 			lua_pushstring(l, "");
2567 		}
2568 		return 1;
2569 	} else if (!strcmp(data, "ItemSlotId")) {
2570 		const int item_slot = GetItemClassSlot(type->ItemClass);
2571 		lua_pushnumber(l, item_slot);
2572 		return 1;
2573 	} else if (!strcmp(data, "WeaponClasses")) {
2574 		lua_createtable(l, type->WeaponClasses.size(), 0);
2575 		for (size_t i = 1; i <= type->WeaponClasses.size(); ++i)
2576 		{
2577 			lua_pushstring(l, GetItemClassNameById(type->WeaponClasses[i-1]).c_str());
2578 			lua_rawseti(l, -2, i);
2579 		}
2580 		return 1;
2581 	//Wyrmgus end
2582 	} else if (!strcmp(data, "Missile")) {
2583 		lua_pushstring(l, type->Missile.Name.c_str());
2584 		return 1;
2585 	} else if (!strcmp(data, "FireMissile")) {
2586 		lua_pushstring(l, type->FireMissile.Name.c_str());
2587 		return 1;
2588 	} else if (!strcmp(data, "MinAttackRange")) {
2589 		lua_pushnumber(l, type->MinAttackRange);
2590 		return 1;
2591 	} else if (!strcmp(data, "MaxAttackRange")) {
2592 		if (!GameRunning && Editor.Running != EditorEditing) {
2593 			lua_pushnumber(l, type->DefaultStat.Variables[ATTACKRANGE_INDEX].Value);
2594 		} else {
2595 			lua_pushnumber(l, type->MapDefaultStat.Variables[ATTACKRANGE_INDEX].Value);
2596 		}
2597 		return 1;
2598 	} else if (!strcmp(data, "Priority")) {
2599 		if (!GameRunning && Editor.Running != EditorEditing) {
2600 			lua_pushnumber(l, type->DefaultStat.Variables[PRIORITY_INDEX].Value);
2601 		} else {
2602 			lua_pushnumber(l, type->MapDefaultStat.Variables[PRIORITY_INDEX].Value);
2603 		}
2604 		return 1;
2605 	} else if (!strcmp(data, "Type")) {
2606 		if (type->UnitType == UnitTypeLand) {
2607 			lua_pushstring(l, "land");
2608 			return 1;
2609 		} else if (type->UnitType == UnitTypeFly) {
2610 			lua_pushstring(l, "fly");
2611 			return 1;
2612 		//Wyrmgus start
2613 		} else if (type->UnitType == UnitTypeFlyLow) {
2614 			lua_pushstring(l, "fly-low");
2615 			return 1;
2616 		//Wyrmgus end
2617 		} else if (type->UnitType == UnitTypeNaval) {
2618 			lua_pushstring(l, "naval");
2619 			return 1;
2620 		}
2621 	} else if (!strcmp(data, "Corpse")) {
2622 		lua_pushstring(l, type->CorpseName.c_str());
2623 		return 1;
2624 	} else if (!strcmp(data, "CanAttack")) {
2625 		lua_pushboolean(l, type->CanAttack);
2626 		return 1;
2627 	} else if (!strcmp(data, "Building")) {
2628 		lua_pushboolean(l, type->BoolFlag[BUILDING_INDEX].value);
2629 		return 1;
2630 	//Wyrmgus start
2631 	} else if (!strcmp(data, "Item")) {
2632 		lua_pushboolean(l, type->BoolFlag[ITEM_INDEX].value);
2633 		return 1;
2634 	} else if (!strcmp(data, "ButtonPos")) {
2635 		lua_pushnumber(l, type->ButtonPos);
2636 		return 1;
2637 	} else if (!strcmp(data, "ButtonKey")) {
2638 		lua_pushstring(l, type->ButtonKey.c_str());
2639 		return 1;
2640 	} else if (!strcmp(data, "ButtonHint")) {
2641 		lua_pushstring(l, type->ButtonHint.c_str());
2642 		return 1;
2643 	} else if (!strcmp(data, "Mod")) {
2644 		lua_pushstring(l, type->Mod.c_str());
2645 		return 1;
2646 	//Wyrmgus end
2647 	} else if (!strcmp(data, "LandUnit")) {
2648 		lua_pushboolean(l, type->LandUnit);
2649 		return 1;
2650 	} else if (!strcmp(data, "GivesResource")) {
2651 		if (type->GivesResource > 0) {
2652 			lua_pushstring(l, DefaultResourceNames[type->GivesResource].c_str());
2653 			return 1;
2654 		} else {
2655 			lua_pushstring(l, "");
2656 			return 1;
2657 		}
2658 	} else if (!strcmp(data, "Sounds")) {
2659 		LuaCheckArgs(l, 3);
2660 		const std::string sound_type = LuaToString(l, 3);
2661 		if (sound_type == "selected") {
2662 			if (!GameRunning && Editor.Running != EditorEditing) {
2663 				lua_pushstring(l, type->Sound.Selected.Name.c_str());
2664 			} else {
2665 				lua_pushstring(l, type->MapSound.Selected.Name.c_str());
2666 			}
2667 		} else if (sound_type == "acknowledge") {
2668 			if (!GameRunning && Editor.Running != EditorEditing) {
2669 				lua_pushstring(l, type->Sound.Acknowledgement.Name.c_str());
2670 			} else {
2671 				lua_pushstring(l, type->MapSound.Acknowledgement.Name.c_str());
2672 			}
2673 		} else if (sound_type == "attack") {
2674 			if (!GameRunning && Editor.Running != EditorEditing) {
2675 				lua_pushstring(l, type->Sound.Attack.Name.c_str());
2676 			} else {
2677 				lua_pushstring(l, type->MapSound.Attack.Name.c_str());
2678 			}
2679 		//Wyrmgus start
2680 		} else if (sound_type == "idle") {
2681 			if (!GameRunning && Editor.Running != EditorEditing) {
2682 				lua_pushstring(l, type->Sound.Idle.Name.c_str());
2683 			} else {
2684 				lua_pushstring(l, type->MapSound.Idle.Name.c_str());
2685 			}
2686 		} else if (sound_type == "hit") {
2687 			if (!GameRunning && Editor.Running != EditorEditing) {
2688 				lua_pushstring(l, type->Sound.Hit.Name.c_str());
2689 			} else {
2690 				lua_pushstring(l, type->MapSound.Hit.Name.c_str());
2691 			}
2692 		} else if (sound_type == "miss") {
2693 			if (!GameRunning && Editor.Running != EditorEditing) {
2694 				lua_pushstring(l, type->Sound.Miss.Name.c_str());
2695 			} else {
2696 				lua_pushstring(l, type->MapSound.Miss.Name.c_str());
2697 			}
2698 		} else if (sound_type == "fire-missile") {
2699 			if (!GameRunning && Editor.Running != EditorEditing) {
2700 				lua_pushstring(l, type->Sound.FireMissile.Name.c_str());
2701 			} else {
2702 				lua_pushstring(l, type->MapSound.FireMissile.Name.c_str());
2703 			}
2704 		} else if (sound_type == "step") {
2705 			if (!GameRunning && Editor.Running != EditorEditing) {
2706 				lua_pushstring(l, type->Sound.Step.Name.c_str());
2707 			} else {
2708 				lua_pushstring(l, type->MapSound.Step.Name.c_str());
2709 			}
2710 		} else if (sound_type == "step-dirt") {
2711 			if (!GameRunning && Editor.Running != EditorEditing) {
2712 				lua_pushstring(l, type->Sound.StepDirt.Name.c_str());
2713 			} else {
2714 				lua_pushstring(l, type->MapSound.StepDirt.Name.c_str());
2715 			}
2716 		} else if (sound_type == "step-grass") {
2717 			if (!GameRunning && Editor.Running != EditorEditing) {
2718 				lua_pushstring(l, type->Sound.StepGrass.Name.c_str());
2719 			} else {
2720 				lua_pushstring(l, type->MapSound.StepGrass.Name.c_str());
2721 			}
2722 		} else if (sound_type == "step-gravel") {
2723 			if (!GameRunning && Editor.Running != EditorEditing) {
2724 				lua_pushstring(l, type->Sound.StepGravel.Name.c_str());
2725 			} else {
2726 				lua_pushstring(l, type->MapSound.StepGravel.Name.c_str());
2727 			}
2728 		} else if (sound_type == "step-mud") {
2729 			if (!GameRunning && Editor.Running != EditorEditing) {
2730 				lua_pushstring(l, type->Sound.StepMud.Name.c_str());
2731 			} else {
2732 				lua_pushstring(l, type->MapSound.StepMud.Name.c_str());
2733 			}
2734 		} else if (sound_type == "step-stone") {
2735 			if (!GameRunning && Editor.Running != EditorEditing) {
2736 				lua_pushstring(l, type->Sound.StepStone.Name.c_str());
2737 			} else {
2738 				lua_pushstring(l, type->MapSound.StepStone.Name.c_str());
2739 			}
2740 		} else if (sound_type == "used") {
2741 			if (!GameRunning && Editor.Running != EditorEditing) {
2742 				lua_pushstring(l, type->Sound.Used.Name.c_str());
2743 			} else {
2744 				lua_pushstring(l, type->MapSound.Used.Name.c_str());
2745 			}
2746 		//Wyrmgus end
2747 		} else if (sound_type == "build") {
2748 			if (!GameRunning && Editor.Running != EditorEditing) {
2749 				lua_pushstring(l, type->Sound.Build.Name.c_str());
2750 			} else {
2751 				lua_pushstring(l, type->MapSound.Build.Name.c_str());
2752 			}
2753 		} else if (sound_type == "ready") {
2754 			if (!GameRunning && Editor.Running != EditorEditing) {
2755 				lua_pushstring(l, type->Sound.Ready.Name.c_str());
2756 			} else {
2757 				lua_pushstring(l, type->MapSound.Ready.Name.c_str());
2758 			}
2759 		} else if (sound_type == "repair") {
2760 			if (!GameRunning && Editor.Running != EditorEditing) {
2761 				lua_pushstring(l, type->Sound.Repair.Name.c_str());
2762 			} else {
2763 				lua_pushstring(l, type->MapSound.Repair.Name.c_str());
2764 			}
2765 		} else if (sound_type == "harvest") {
2766 			LuaCheckArgs(l, 4);
2767 			const std::string sound_subtype = LuaToString(l, 4);
2768 			const int resId = GetResourceIdByName(sound_subtype.c_str());
2769 			if (!GameRunning && Editor.Running != EditorEditing) {
2770 				lua_pushstring(l, type->Sound.Harvest[resId].Name.c_str());
2771 			} else {
2772 				lua_pushstring(l, type->MapSound.Harvest[resId].Name.c_str());
2773 			}
2774 		} else if (sound_type == "help") {
2775 			if (!GameRunning && Editor.Running != EditorEditing) {
2776 				lua_pushstring(l, type->Sound.Help.Name.c_str());
2777 			} else {
2778 				lua_pushstring(l, type->MapSound.Help.Name.c_str());
2779 			}
2780 		} else if (sound_type == "dead") {
2781 			if (lua_gettop(l) < 4) {
2782 				if (!GameRunning && Editor.Running != EditorEditing) {
2783 					lua_pushstring(l, type->Sound.Dead[ANIMATIONS_DEATHTYPES].Name.c_str());
2784 				} else {
2785 					lua_pushstring(l, type->MapSound.Dead[ANIMATIONS_DEATHTYPES].Name.c_str());
2786 				}
2787 			} else {
2788 				int death;
2789 				const std::string sound_subtype = LuaToString(l, 4);
2790 
2791 				for (death = 0; death < ANIMATIONS_DEATHTYPES; ++death) {
2792 					if (sound_subtype == ExtraDeathTypes[death]) {
2793 						break;
2794 					}
2795 				}
2796 				if (death == ANIMATIONS_DEATHTYPES) {
2797 					if (!GameRunning && Editor.Running != EditorEditing) {
2798 						lua_pushstring(l, type->Sound.Dead[ANIMATIONS_DEATHTYPES].Name.c_str());
2799 					} else {
2800 						lua_pushstring(l, type->MapSound.Dead[ANIMATIONS_DEATHTYPES].Name.c_str());
2801 					}
2802 				} else {
2803 					if (!GameRunning && Editor.Running != EditorEditing) {
2804 						lua_pushstring(l, type->Sound.Dead[death].Name.c_str());
2805 					} else {
2806 						lua_pushstring(l, type->MapSound.Dead[death].Name.c_str());
2807 					}
2808 				}
2809 			}
2810 		}
2811 		return 1;
2812 	//Wyrmgus start
2813 	} else if (!strcmp(data, "Drops")) {
2814 		lua_createtable(l, type->Drops.size(), 0);
2815 		for (size_t i = 1; i <= type->Drops.size(); ++i)
2816 		{
2817 			lua_pushstring(l, type->Drops[i-1]->Ident.c_str());
2818 			lua_rawseti(l, -2, i);
2819 		}
2820 		return 1;
2821 	} else if (!strcmp(data, "AiDrops")) {
2822 		bool is_mod = false;
2823 		if (lua_gettop(l) >= 3) {
2824 			is_mod = true;
2825 		}
2826 
2827 		std::string mod_file;
2828 		if (is_mod) {
2829 			mod_file = LuaToString(l, 3);
2830 		}
2831 
2832 		std::map<std::string, std::vector<CUnitType *>>::const_iterator mod_find_iterator = type->ModAiDrops.find(mod_file);
2833 		if (is_mod && mod_find_iterator != type->ModAiDrops.end()) {
2834 			lua_createtable(l, mod_find_iterator->second.size(), 0);
2835 			for (size_t i = 1; i <= mod_find_iterator->second.size(); ++i)
2836 			{
2837 				lua_pushstring(l, mod_find_iterator->second[i-1]->Ident.c_str());
2838 				lua_rawseti(l, -2, i);
2839 			}
2840 			return 1;
2841 		} else {
2842 			lua_createtable(l, type->AiDrops.size(), 0);
2843 			for (size_t i = 1; i <= type->AiDrops.size(); ++i)
2844 			{
2845 				lua_pushstring(l, type->AiDrops[i-1]->Ident.c_str());
2846 				lua_rawseti(l, -2, i);
2847 			}
2848 			return 1;
2849 		}
2850 	} else if (!strcmp(data, "Affixes")) {
2851 		lua_createtable(l, type->Affixes.size(), 0);
2852 		for (size_t i = 1; i <= type->Affixes.size(); ++i)
2853 		{
2854 			lua_pushstring(l, type->Affixes[i-1]->Ident.c_str());
2855 			lua_rawseti(l, -2, i);
2856 		}
2857 		return 1;
2858 	} else if (!strcmp(data, "Droppers")) { // unit types which can drop this one
2859 		std::vector<CUnitType *> droppers;
2860 		for (size_t i = 0; i < UnitTypes.size(); ++i) {
2861 			if (
2862 				std::find(UnitTypes[i]->Drops.begin(), UnitTypes[i]->Drops.end(), type) != UnitTypes[i]->Drops.end()
2863 				|| std::find(UnitTypes[i]->AiDrops.begin(), UnitTypes[i]->AiDrops.end(), type) != UnitTypes[i]->AiDrops.end()
2864 			) {
2865 				droppers.push_back(UnitTypes[i]);
2866 			}
2867 		}
2868 
2869 		lua_createtable(l, droppers.size(), 0);
2870 		for (size_t i = 1; i <= droppers.size(); ++i)
2871 		{
2872 			lua_pushstring(l, droppers[i-1]->Ident.c_str());
2873 			lua_rawseti(l, -2, i);
2874 		}
2875 		return 1;
2876 	} else if (!strcmp(data, "Traits")) {
2877 		lua_createtable(l, type->Traits.size(), 0);
2878 		for (size_t i = 1; i <= type->Traits.size(); ++i)
2879 		{
2880 			lua_pushstring(l, type->Traits[i-1]->Ident.c_str());
2881 			lua_rawseti(l, -2, i);
2882 		}
2883 		return 1;
2884 	} else if (!strcmp(data, "StartingAbilities")) {
2885 		lua_createtable(l, type->StartingAbilities.size(), 0);
2886 		for (size_t i = 1; i <= type->StartingAbilities.size(); ++i)
2887 		{
2888 			lua_pushstring(l, type->StartingAbilities[i-1]->Ident.c_str());
2889 			lua_rawseti(l, -2, i);
2890 		}
2891 		return 1;
2892 	} else if (!strcmp(data, "Prefixes")) {
2893 		std::vector<CUpgrade *> prefixes;
2894 		for (size_t i = 0; i < type->Affixes.size(); ++i)
2895 		{
2896 			if (type->Affixes[i]->MagicPrefix) {
2897 				prefixes.push_back(type->Affixes[i]);
2898 			}
2899 		}
2900 		if (type->ItemClass != -1) {
2901 			for (size_t i = 0; i < AllUpgrades.size(); ++i) {
2902 				if (AllUpgrades[i]->MagicPrefix && AllUpgrades[i]->ItemPrefix[type->ItemClass]) {
2903 					prefixes.push_back(AllUpgrades[i]);
2904 				}
2905 			}
2906 		}
2907 
2908 		lua_createtable(l, prefixes.size(), 0);
2909 		for (size_t i = 1; i <= prefixes.size(); ++i)
2910 		{
2911 			lua_pushstring(l, prefixes[i-1]->Ident.c_str());
2912 			lua_rawseti(l, -2, i);
2913 		}
2914 		return 1;
2915 	} else if (!strcmp(data, "Suffixes")) {
2916 		std::vector<CUpgrade *> suffixes;
2917 		for (size_t i = 0; i < type->Affixes.size(); ++i)
2918 		{
2919 			if (type->Affixes[i]->MagicSuffix) {
2920 				suffixes.push_back(type->Affixes[i]);
2921 			}
2922 		}
2923 		if (type->ItemClass != -1) {
2924 			for (size_t i = 0; i < AllUpgrades.size(); ++i) {
2925 				if (AllUpgrades[i]->MagicSuffix && AllUpgrades[i]->ItemSuffix[type->ItemClass]) {
2926 					suffixes.push_back(AllUpgrades[i]);
2927 				}
2928 			}
2929 		}
2930 
2931 		lua_createtable(l, suffixes.size(), 0);
2932 		for (size_t i = 1; i <= suffixes.size(); ++i)
2933 		{
2934 			lua_pushstring(l, suffixes[i-1]->Ident.c_str());
2935 			lua_rawseti(l, -2, i);
2936 		}
2937 		return 1;
2938 	} else if (!strcmp(data, "Works")) {
2939 		std::vector<CUpgrade *> works;
2940 		if (type->ItemClass != -1) {
2941 			for (size_t i = 0; i < AllUpgrades.size(); ++i) {
2942 				if (AllUpgrades[i]->Work == type->ItemClass && !AllUpgrades[i]->UniqueOnly) {
2943 					works.push_back(AllUpgrades[i]);
2944 				}
2945 			}
2946 		}
2947 
2948 		lua_createtable(l, works.size(), 0);
2949 		for (size_t i = 1; i <= works.size(); ++i)
2950 		{
2951 			lua_pushstring(l, works[i-1]->Ident.c_str());
2952 			lua_rawseti(l, -2, i);
2953 		}
2954 		return 1;
2955 	} else if (!strcmp(data, "Uniques")) {
2956 		std::vector<CUniqueItem *> uniques;
2957 		for (size_t i = 0; i < UniqueItems.size(); ++i)
2958 		{
2959 			if (UniqueItems[i]->Type == type) {
2960 				uniques.push_back(UniqueItems[i]);
2961 			}
2962 		}
2963 
2964 		lua_createtable(l, uniques.size(), 0);
2965 		for (size_t i = 1; i <= uniques.size(); ++i)
2966 		{
2967 			lua_pushstring(l, uniques[i-1]->Ident.c_str());
2968 			lua_rawseti(l, -2, i);
2969 		}
2970 		return 1;
2971 	} else if (!strcmp(data, "Variations")) {
2972 		std::vector<std::string> variation_idents;
2973 		for (CUnitTypeVariation *variation : type->Variations) {
2974 			if (std::find(variation_idents.begin(), variation_idents.end(), variation->VariationId) == variation_idents.end()) {
2975 				variation_idents.push_back(variation->VariationId);
2976 			}
2977 		}
2978 
2979 		lua_createtable(l, variation_idents.size(), 0);
2980 		for (size_t i = 1; i <= variation_idents.size(); ++i)
2981 		{
2982 			lua_pushstring(l, variation_idents[i-1].c_str());
2983 			lua_rawseti(l, -2, i);
2984 		}
2985 		return 1;
2986 	} else if (!strcmp(data, "LayerVariations")) {
2987 		LuaCheckArgs(l, 3);
2988 		const std::string image_layer_name = LuaToString(l, 3);
2989 		const int image_layer = GetImageLayerIdByName(image_layer_name);
2990 
2991 		std::vector<std::string> variation_idents;
2992 		for (CUnitTypeVariation *layer_variation : type->LayerVariations[image_layer]) {
2993 			if (std::find(variation_idents.begin(), variation_idents.end(), layer_variation->VariationId) == variation_idents.end()) {
2994 				variation_idents.push_back(layer_variation->VariationId);
2995 			}
2996 		}
2997 
2998 		lua_createtable(l, variation_idents.size(), 0);
2999 		for (size_t i = 1; i <= variation_idents.size(); ++i)
3000 		{
3001 			lua_pushstring(l, variation_idents[i-1].c_str());
3002 			lua_rawseti(l, -2, i);
3003 		}
3004 		return 1;
3005 	} else if (!strcmp(data, "Trains")) {
3006 		bool is_mod = false;
3007 		if (lua_gettop(l) >= 3) {
3008 			is_mod = true;
3009 		}
3010 
3011 		std::string mod_file;
3012 		if (is_mod) {
3013 			mod_file = LuaToString(l, 3);
3014 		}
3015 
3016 		std::map<std::string, std::vector<CUnitType *>>::const_iterator mod_find_iterator = type->ModTrains.find(mod_file);
3017 		if (is_mod && mod_find_iterator != type->ModTrains.end()) {
3018 			lua_createtable(l, mod_find_iterator->second.size(), 0);
3019 			for (size_t i = 1; i <= mod_find_iterator->second.size(); ++i)
3020 			{
3021 				lua_pushstring(l, mod_find_iterator->second[i-1]->Ident.c_str());
3022 				lua_rawseti(l, -2, i);
3023 			}
3024 			return 1;
3025 		} else {
3026 			lua_createtable(l, type->Trains.size(), 0);
3027 			for (size_t i = 1; i <= type->Trains.size(); ++i)
3028 			{
3029 				lua_pushstring(l, type->Trains[i-1]->Ident.c_str());
3030 				lua_rawseti(l, -2, i);
3031 			}
3032 			return 1;
3033 		}
3034 	//Wyrmgus end
3035 	} else {
3036 		int index = UnitTypeVar.VariableNameLookup[data];
3037 		if (index != -1) { // valid index
3038 			if (!GameRunning && Editor.Running != EditorEditing) {
3039 				lua_pushnumber(l, type->DefaultStat.Variables[index].Value);
3040 			} else {
3041 				lua_pushnumber(l, type->MapDefaultStat.Variables[index].Value);
3042 			}
3043 			return 1;
3044 		}
3045 
3046 		index = UnitTypeVar.BoolFlagNameLookup[data];
3047 		if (index != -1) {
3048 			lua_pushboolean(l, type->BoolFlag[index].value);
3049 			return 1;
3050 		} else {
3051 			LuaError(l, "Invalid field: %s" _C_ data);
3052 		}
3053 	}
3054 
3055 	return 0;
3056 }
3057 
3058 // ----------------------------------------------------------------------------
3059 
3060 /**
3061 **  Define the field of the UserDefined variables.
3062 **
3063 **  @param l          Lua state.
3064 **  @param var        Variable to set.
3065 **  @param lua_index  Index of the table where are the infos
3066 **
3067 **  @internal Use to not duplicate code.
3068 */
DefineVariableField(lua_State * l,CVariable * var,int lua_index)3069 void DefineVariableField(lua_State *l, CVariable *var, int lua_index)
3070 {
3071 	if (lua_index < 0) { // relative index
3072 		--lua_index;
3073 	}
3074 	lua_pushnil(l);
3075 	while (lua_next(l, lua_index)) {
3076 		const char *key = LuaToString(l, -2);
3077 
3078 		if (!strcmp(key, "Value")) {
3079 			var->Value = LuaToNumber(l, -1);
3080 		} else if (!strcmp(key, "Max")) {
3081 			var->Max = LuaToNumber(l, -1);
3082 		} else if (!strcmp(key, "Increase")) {
3083 			var->Increase = LuaToNumber(l, -1);
3084 		} else if (!strcmp(key, "Enable")) {
3085 			var->Enable = LuaToBoolean(l, -1);
3086 		} else { // Error.
3087 			LuaError(l, "incorrect field '%s' for variable\n" _C_ key);
3088 		}
3089 		lua_pop(l, 1); // pop the value;
3090 	}
3091 }
3092 
3093 /**
3094 **  Define user variables.
3095 **
3096 **  @param l  Lua state.
3097 */
CclDefineVariables(lua_State * l)3098 static int CclDefineVariables(lua_State *l)
3099 {
3100 	int old = UnitTypeVar.GetNumberVariable();
3101 
3102 	const int args = lua_gettop(l);
3103 	for (int j = 0; j < args; ++j) {
3104 		const char *str = LuaToString(l, j + 1);
3105 
3106 		const int index = UnitTypeVar.VariableNameLookup.AddKey(str);
3107 		if (index == old) {
3108 			old++;
3109 			UnitTypeVar.Variable.resize(old);
3110 		} else {
3111 			DebugPrint("Warning, User Variable \"%s\" redefined\n" _C_ str);
3112 		}
3113 		if (!lua_istable(l, j + 2)) { // No change => default value.
3114 			continue;
3115 		}
3116 		++j;
3117 		DefineVariableField(l, &(UnitTypeVar.Variable[index]), j + 1);
3118 	}
3119 	return 0;
3120 }
3121 
3122 /**
3123 **  Define boolean flag.
3124 **
3125 **  @param l  Lua state.
3126 */
CclDefineBoolFlags(lua_State * l)3127 static int CclDefineBoolFlags(lua_State *l)
3128 {
3129 	const unsigned int old = UnitTypeVar.GetNumberBoolFlag();
3130 	const int args = lua_gettop(l);
3131 	for (int j = 0; j < args; ++j) {
3132 		const char *str = LuaToString(l, j + 1);
3133 
3134 		UnitTypeVar.BoolFlagNameLookup.AddKey(str);
3135 
3136 	}
3137 
3138 	if (0 < old && old != UnitTypeVar.GetNumberBoolFlag()) {
3139 		size_t new_size = UnitTypeVar.GetNumberBoolFlag();
3140 		for (std::vector<CUnitType *>::size_type i = 0; i < UnitTypes.size(); ++i) { // adjust array for unit already defined
3141 			UnitTypes[i]->BoolFlag.resize(new_size);
3142 		}
3143 	}
3144 	return 0;
3145 }
3146 
3147 /**
3148 **  Define Decorations for user variables
3149 **
3150 **  @param l  Lua state.
3151 **
3152 **  @todo modify Assert with luastate with User Error.
3153 **  @todo continue to add configuration.
3154 */
CclDefineDecorations(lua_State * l)3155 static int CclDefineDecorations(lua_State *l)
3156 {
3157 	struct {
3158 		int Index;
3159 		//Wyrmgus start
3160 		int MinValue;
3161 		//Wyrmgus end
3162 		int OffsetX;
3163 		int OffsetY;
3164 		int OffsetXPercent;
3165 		int OffsetYPercent;
3166 		bool IsCenteredInX;
3167 		bool IsCenteredInY;
3168 		bool ShowIfNotEnable;
3169 		bool ShowWhenNull;
3170 		bool HideHalf;
3171 		bool ShowWhenMax;
3172 		bool ShowOnlySelected;
3173 		bool HideNeutral;
3174 		bool HideAllied;
3175 		//Wyrmgus start
3176 		bool HideSelf;
3177 		//Wyrmgus end
3178 		bool ShowOpponent;
3179 		bool ShowIfCanCastAnySpell;
3180 	} tmp;
3181 
3182 	const int nargs = lua_gettop(l);
3183 	for (int i = 0; i < nargs; i++) {
3184 		Assert(lua_istable(l, i + 1));
3185 		CDecoVar *decovar = nullptr;
3186 		memset(&tmp, 0, sizeof(tmp));
3187 		lua_pushnil(l);
3188 		while (lua_next(l, i + 1)) {
3189 			const char *key = LuaToString(l, -2);
3190 			if (!strcmp(key, "Index")) {
3191 				const char *const value = LuaToString(l, -1);
3192 				tmp.Index = UnitTypeVar.VariableNameLookup[value];// User variables
3193 				Assert(tmp.Index != -1);
3194 			//Wyrmgus start
3195 			} else if (!strcmp(key, "MinValue")) {
3196 				tmp.MinValue = LuaToNumber(l, -1);
3197 			//Wyrmgus end
3198 			} else if (!strcmp(key, "Offset")) {
3199 				CclGetPos(l, &tmp.OffsetX, &tmp.OffsetY);
3200 			} else if (!strcmp(key, "OffsetPercent")) {
3201 				CclGetPos(l, &tmp.OffsetXPercent, &tmp.OffsetYPercent);
3202 			} else if (!strcmp(key, "CenterX")) {
3203 				tmp.IsCenteredInX = LuaToBoolean(l, -1);
3204 			} else if (!strcmp(key, "CenterY")) {
3205 				tmp.IsCenteredInY = LuaToBoolean(l, -1);
3206 			} else if (!strcmp(key, "ShowIfNotEnable")) {
3207 				tmp.ShowIfNotEnable = LuaToBoolean(l, -1);
3208 			} else if (!strcmp(key, "ShowWhenNull")) {
3209 				tmp.ShowWhenNull = LuaToBoolean(l, -1);
3210 			} else if (!strcmp(key, "HideHalf")) {
3211 				tmp.HideHalf = LuaToBoolean(l, -1);
3212 			} else if (!strcmp(key, "ShowWhenMax")) {
3213 				tmp.ShowWhenMax = LuaToBoolean(l, -1);
3214 			} else if (!strcmp(key, "ShowOnlySelected")) {
3215 				tmp.ShowOnlySelected = LuaToBoolean(l, -1);
3216 			} else if (!strcmp(key, "HideNeutral")) {
3217 				tmp.HideNeutral = LuaToBoolean(l, -1);
3218 			} else if (!strcmp(key, "HideAllied")) {
3219 				tmp.HideAllied = LuaToBoolean(l, -1);
3220 			//Wyrmgus start
3221 			} else if (!strcmp(key, "HideSelf")) {
3222 				tmp.HideSelf = LuaToBoolean(l, -1);
3223 			//Wyrmgus end
3224 			} else if (!strcmp(key, "ShowOpponent")) {
3225 				tmp.ShowOpponent = LuaToBoolean(l, -1);
3226 			} else if (!strcmp(key, "ShowIfCanCastAnySpell")) {
3227 				tmp.ShowIfCanCastAnySpell = LuaToBoolean(l, -1);
3228 			} else if (!strcmp(key, "Method")) {
3229 				Assert(lua_istable(l, -1));
3230 				lua_rawgeti(l, -1, 1); // MethodName
3231 				lua_rawgeti(l, -2, 2); // Data
3232 				Assert(lua_istable(l, -1));
3233 				key = LuaToString(l, -2);
3234 				if (!strcmp(key, "bar")) {
3235 					CDecoVarBar *decovarbar = new CDecoVarBar;
3236 					lua_pushnil(l);
3237 					while (lua_next(l, -2)) {
3238 						key = LuaToString(l, -2);
3239 						if (!strcmp(key, "Height")) {
3240 							decovarbar->Height = LuaToNumber(l, -1);
3241 						} else if (!strcmp(key, "Width")) {
3242 							decovarbar->Width = LuaToNumber(l, -1);
3243 						} else if (!strcmp(key, "Orientation")) {
3244 							key = LuaToString(l, -1);
3245 							if (!strcmp(key, "horizontal")) {
3246 								decovarbar->IsVertical = 0;
3247 							} else if (!strcmp(key, "vertical")) {
3248 								decovarbar->IsVertical = 1;
3249 							} else { // Error
3250 								LuaError(l, "invalid Orientation '%s' for bar in DefineDecorations" _C_ key);
3251 							}
3252 						} else if (!strcmp(key, "SEToNW")) {
3253 							decovarbar->SEToNW = LuaToBoolean(l, -1);
3254 						} else if (!strcmp(key, "BorderSize")) {
3255 							decovarbar->BorderSize = LuaToNumber(l, -1);
3256 						} else if (!strcmp(key, "ShowFullBackground")) {
3257 							decovarbar->ShowFullBackground = LuaToBoolean(l, -1);
3258 #if 0 // FIXME Color configuration
3259 						} else if (!strcmp(key, "Color")) {
3260 							decovar->Color = // FIXME
3261 						} else if (!strcmp(key, "BColor")) {
3262 							decovar->BColor = // FIXME
3263 #endif
3264 						} else {
3265 							LuaError(l, "'%s' invalid for Method bar" _C_ key);
3266 						}
3267 						lua_pop(l, 1); // Pop value
3268 					}
3269 					decovar = decovarbar;
3270 				} else if (!strcmp(key, "text")) {
3271 					CDecoVarText *decovartext = new CDecoVarText;
3272 
3273 					decovartext->Font = CFont::Get(LuaToString(l, -1, 1));
3274 					// FIXME : More arguments ? color...
3275 					decovar = decovartext;
3276 				} else if (!strcmp(key, "sprite")) {
3277 					CDecoVarSpriteBar *decovarspritebar = new CDecoVarSpriteBar;
3278 					decovarspritebar->NSprite = GetSpriteIndex(LuaToString(l, -1, 1));
3279 					if (decovarspritebar->NSprite == -1) {
3280 						LuaError(l, "invalid sprite-name '%s' for Method in DefineDecorations" _C_ LuaToString(l, -1, 1));
3281 					}
3282 					// FIXME : More arguments ?
3283 					decovar = decovarspritebar;
3284 				} else if (!strcmp(key, "static-sprite")) {
3285 					CDecoVarStaticSprite *decovarstaticsprite = new CDecoVarStaticSprite;
3286 					if (lua_rawlen(l, -1) == 2) {
3287 						decovarstaticsprite->NSprite = GetSpriteIndex(LuaToString(l, -1, 1));
3288 						decovarstaticsprite->n = LuaToNumber(l, -1, 2);
3289 					} else {
3290 						decovarstaticsprite->NSprite = GetSpriteIndex(LuaToString(l, -1, 1));
3291 						decovarstaticsprite->n = LuaToNumber(l, -1, 2);
3292 						decovarstaticsprite->FadeValue = LuaToNumber(l, -1, 3);
3293 					}
3294 					decovar = decovarstaticsprite;
3295 				} else { // Error
3296 					LuaError(l, "invalid method '%s' for Method in DefineDecorations" _C_ key);
3297 				}
3298 				lua_pop(l, 2); // MethodName and data
3299 			} else { // Error
3300 				LuaError(l, "invalid key '%s' for DefineDecorations" _C_ key);
3301 			}
3302 			lua_pop(l, 1); // Pop the value
3303 		}
3304 		decovar->Index = tmp.Index;
3305 		//Wyrmgus start
3306 		decovar->MinValue = tmp.MinValue;
3307 		//Wyrmgus end
3308 		decovar->OffsetX = tmp.OffsetX;
3309 		decovar->OffsetY = tmp.OffsetY;
3310 		decovar->OffsetXPercent = tmp.OffsetXPercent;
3311 		decovar->OffsetYPercent = tmp.OffsetYPercent;
3312 		decovar->IsCenteredInX = tmp.IsCenteredInX;
3313 		decovar->IsCenteredInY = tmp.IsCenteredInY;
3314 		decovar->ShowIfNotEnable = tmp.ShowIfNotEnable;
3315 		decovar->ShowWhenNull = tmp.ShowWhenNull;
3316 		decovar->HideHalf = tmp.HideHalf;
3317 		decovar->ShowWhenMax = tmp.ShowWhenMax;
3318 		decovar->ShowOnlySelected = tmp.ShowOnlySelected;
3319 		decovar->HideNeutral = tmp.HideNeutral;
3320 		decovar->HideAllied = tmp.HideAllied;
3321 		//Wyrmgus start
3322 		decovar->HideSelf = tmp.HideSelf;
3323 		//Wyrmgus end
3324 		decovar->ShowOpponent = tmp.ShowOpponent;
3325 		decovar->ShowIfCanCastAnySpell = tmp.ShowIfCanCastAnySpell;
3326 		//Wyrmgus start
3327 //		UnitTypeVar.DecoVar.push_back(decovar);
3328 		bool already_defined = false;
3329 		for (std::vector<CDecoVar *>::iterator it = UnitTypeVar.DecoVar.begin();
3330 			 it != UnitTypeVar.DecoVar.end(); ++it) {
3331 			if ((*it)->Index == tmp.Index) { // replace other decorations which use the same variable
3332 				*it = decovar;
3333 				already_defined = true;
3334 			}
3335 		}
3336 		if (!already_defined) {
3337 			UnitTypeVar.DecoVar.push_back(decovar);
3338 		}
3339 		//Wyrmgus end
3340 	}
3341 	Assert(lua_gettop(l));
3342 	return 0;
3343 }
3344 
3345 /**
3346 **  Define default extra death types.
3347 **
3348 **  @param l  Lua state.
3349 */
CclDefineExtraDeathTypes(lua_State * l)3350 static int CclDefineExtraDeathTypes(lua_State *l)
3351 {
3352 	unsigned int args;
3353 
3354 	for (unsigned int i = 0; i < ANIMATIONS_DEATHTYPES; ++i) {
3355 		ExtraDeathTypes[i].clear();
3356 	}
3357 	args = lua_gettop(l);
3358 	for (unsigned int i = 0; i < ANIMATIONS_DEATHTYPES && i < args; ++i) {
3359 		ExtraDeathTypes[i] = LuaToString(l, i + 1);
3360 	}
3361 	return 0;
3362 }
3363 
3364 //Wyrmgus start
CclGetUnitTypes(lua_State * l)3365 static int CclGetUnitTypes(lua_State *l)
3366 {
3367 	std::string mod_file;
3368 	if (lua_gettop(l) >= 1) {
3369 		mod_file = LuaToString(l, 1);
3370 	}
3371 
3372 	std::vector<std::string> unit_types;
3373 	for (size_t i = 0; i != UnitTypes.size(); ++i) {
3374 		if (mod_file.empty() || UnitTypes[i]->Mod == mod_file) {
3375 			unit_types.push_back(UnitTypes[i]->Ident);
3376 		}
3377 	}
3378 
3379 	lua_createtable(l, unit_types.size(), 0);
3380 	for (size_t i = 1; i <= unit_types.size(); ++i)
3381 	{
3382 		lua_pushstring(l, unit_types[i-1].c_str());
3383 		lua_rawseti(l, -2, i);
3384 	}
3385 	return 1;
3386 }
3387 
CclGetAnimations(lua_State * l)3388 static int CclGetAnimations(lua_State *l)
3389 {
3390 	std::vector<std::string> animations;
3391 
3392 	std::map<std::string, CAnimations *>::iterator it;
3393 	for (it = AnimationMap.begin(); it != AnimationMap.end(); ++it) {
3394 		animations.push_back((*it).first);
3395 	}
3396 
3397 	lua_createtable(l, animations.size(), 0);
3398 	for (size_t i = 1; i <= animations.size(); ++i)
3399 	{
3400 		lua_pushstring(l, animations[i-1].c_str());
3401 		lua_rawseti(l, -2, i);
3402 	}
3403 	return 1;
3404 }
3405 //Wyrmgus end
3406 // ----------------------------------------------------------------------------
3407 
3408 /**
3409 **  Update unit variables which are not user defined.
3410 */
UpdateUnitVariables(CUnit & unit)3411 void UpdateUnitVariables(CUnit &unit)
3412 {
3413 	const CUnitType *type = unit.Type;
3414 
3415 	//Wyrmgus start
3416 	if (!type) {
3417 		fprintf(stderr, "Error in UpdateUnitVariables: Unit has not type\n");
3418 		return;
3419 	}
3420 	//Wyrmgus end
3421 
3422 	for (int i = 0; i < NVARALREADYDEFINED; i++) { // default values
3423 		if (i == ARMOR_INDEX || i == PIERCINGDAMAGE_INDEX || i == BASICDAMAGE_INDEX
3424 			//Wyrmgus start
3425 			|| i == SUPPLY_INDEX || i == DEMAND_INDEX
3426 			|| i == THORNSDAMAGE_INDEX
3427 			|| i == FIREDAMAGE_INDEX || i == COLDDAMAGE_INDEX || i == ARCANEDAMAGE_INDEX || i == LIGHTNINGDAMAGE_INDEX || i == AIRDAMAGE_INDEX || i == EARTHDAMAGE_INDEX || i == WATERDAMAGE_INDEX || i == ACIDDAMAGE_INDEX
3428 			|| i == SPEED_INDEX
3429 			|| i == FIRERESISTANCE_INDEX || i == COLDRESISTANCE_INDEX || i == ARCANERESISTANCE_INDEX || i == LIGHTNINGRESISTANCE_INDEX || i == AIRRESISTANCE_INDEX || i == EARTHRESISTANCE_INDEX || i == WATERRESISTANCE_INDEX || i == ACIDRESISTANCE_INDEX
3430 			|| i == HACKRESISTANCE_INDEX || i == PIERCERESISTANCE_INDEX || i == BLUNTRESISTANCE_INDEX || i == DEHYDRATIONIMMUNITY_INDEX
3431 			//Wyrmgus end
3432 			|| i == MANA_INDEX || i == KILL_INDEX || i == XP_INDEX || i == GIVERESOURCE_INDEX
3433 			//Wyrmgus start
3434 			|| i == AUTOREPAIRRANGE_INDEX
3435 			//Wyrmgus end
3436 			|| i == BLOODLUST_INDEX || i == HASTE_INDEX || i == SLOW_INDEX
3437 			|| i == INVISIBLE_INDEX || i == UNHOLYARMOR_INDEX || i == HP_INDEX
3438 			|| i == SHIELD_INDEX || i == POINTS_INDEX || i == MAXHARVESTERS_INDEX
3439 			|| i == POISON_INDEX || i == SHIELDPERMEABILITY_INDEX || i == SHIELDPIERCING_INDEX
3440 			//Wyrmgus
3441 //			|| i == ISALIVE_INDEX || i == PLAYER_INDEX) {
3442 			|| i == ISALIVE_INDEX || i == PLAYER_INDEX || i == PRIORITY_INDEX || i == SIGHTRANGE_INDEX || i == ATTACKRANGE_INDEX
3443 			|| i == STRENGTH_INDEX || i == DEXTERITY_INDEX || i == INTELLIGENCE_INDEX || i == CHARISMA_INDEX
3444 			|| i == ACCURACY_INDEX || i == EVASION_INDEX
3445 			|| i == LEVEL_INDEX || i == LEVELUP_INDEX || i == XPREQUIRED_INDEX || i == VARIATION_INDEX || i == HITPOINTHEALING_INDEX || i == HITPOINTBONUS_INDEX || i == CRITICALSTRIKECHANCE_INDEX
3446 			|| i == CHARGEBONUS_INDEX || i == BACKSTAB_INDEX || i == BONUSAGAINSTMOUNTED_INDEX || i == BONUSAGAINSTBUILDINGS_INDEX || i == BONUSAGAINSTAIR_INDEX || i == BONUSAGAINSTGIANTS_INDEX || i == BONUSAGAINSTDRAGONS_INDEX
3447 			|| i == DAYSIGHTRANGEBONUS_INDEX || i == NIGHTSIGHTRANGEBONUS_INDEX
3448 			|| i == KNOWLEDGEMAGIC_INDEX || i == KNOWLEDGEWARFARE_INDEX || i == KNOWLEDGEMINING_INDEX
3449 			|| i == MAGICLEVEL_INDEX || i == TRANSPARENCY_INDEX || i == GENDER_INDEX || i == BIRTHCYCLE_INDEX
3450 			|| i == STUN_INDEX || i == BLEEDING_INDEX || i == LEADERSHIP_INDEX || i == BLESSING_INDEX || i == INSPIRE_INDEX || i == PRECISION_INDEX || i == REGENERATION_INDEX || i == BARKSKIN_INDEX || i == TERROR_INDEX || i == WITHER_INDEX || i == DEHYDRATION_INDEX || i == HYDRATING_INDEX
3451 			|| i == TIMEEFFICIENCYBONUS_INDEX || i == RESEARCHSPEEDBONUS_INDEX || i == GARRISONEDRANGEBONUS_INDEX || i == SPEEDBONUS_INDEX
3452 			|| i == GATHERINGBONUS_INDEX || i == COPPERGATHERINGBONUS_INDEX || i == SILVERGATHERINGBONUS_INDEX || i == GOLDGATHERINGBONUS_INDEX || i == IRONGATHERINGBONUS_INDEX || i == MITHRILGATHERINGBONUS_INDEX || i == LUMBERGATHERINGBONUS_INDEX || i == STONEGATHERINGBONUS_INDEX || i == COALGATHERINGBONUS_INDEX || i == JEWELRYGATHERINGBONUS_INDEX || i == FURNITUREGATHERINGBONUS_INDEX || i == LEATHERGATHERINGBONUS_INDEX || i == GEMSGATHERINGBONUS_INDEX
3453 			|| i == DISEMBARKMENTBONUS_INDEX || i == TRADECOST_INDEX || i == SALVAGEFACTOR_INDEX || i == MUGGING_INDEX || i == RAIDING_INDEX
3454 			|| i == DESERTSTALK_INDEX || i == FORESTSTALK_INDEX || i == SWAMPSTALK_INDEX
3455 			|| i == LEADERSHIPAURA_INDEX || i == REGENERATIONAURA_INDEX || i == HYDRATINGAURA_INDEX || i == ETHEREALVISION_INDEX || i == HERO_INDEX || i == OWNERSHIPINFLUENCERANGE_INDEX) {
3456 			//Wyrmgus end
3457 			continue;
3458 		}
3459 		unit.Variable[i].Value = 0;
3460 		unit.Variable[i].Max = 0;
3461 		unit.Variable[i].Enable = 1;
3462 	}
3463 
3464 	//Wyrmgus
3465 	unit.Variable[VARIATION_INDEX].Max = unit.Type->Variations.size();
3466 	unit.Variable[VARIATION_INDEX].Enable = 1;
3467 	unit.Variable[VARIATION_INDEX].Value = unit.Variation;
3468 
3469 	unit.Variable[TRANSPARENCY_INDEX].Max = 100;
3470 
3471 	unit.Variable[LEVEL_INDEX].Max = 100000;
3472 	if (!IsNetworkGame() && unit.Character != nullptr && unit.Player->AiEnabled == false) {
3473 		if (unit.Variable[LEVEL_INDEX].Value > unit.Character->Level) { //save level, if unit has a persistent character
3474 			unit.Character->Level = unit.Variable[LEVEL_INDEX].Value;
3475 			SaveHero(unit.Character);
3476 			CheckAchievements(); // check achievements to see if any hero now has a high enough level for a particular achievement to be obtained
3477 		}
3478 	}
3479 
3480 	if (unit.Variable[BIRTHCYCLE_INDEX].Value && (GameCycle - unit.Variable[BIRTHCYCLE_INDEX].Value) > 1000 && unit.Type->Species != nullptr && !unit.Type->Species->ChildUpgrade.empty()) { // 1000 cycles until maturation, for all species (should change this to have different maturation times for different species)
3481 		unit.Variable[BIRTHCYCLE_INDEX].Value = 0;
3482 		IndividualUpgradeLost(unit, CUpgrade::Get(unit.Type->Species->ChildUpgrade));
3483 	}
3484 	//Wyrmgus end
3485 
3486 	// Shield permeability
3487 	unit.Variable[SHIELDPERMEABILITY_INDEX].Max = 100;
3488 
3489 	// Transport
3490 	unit.Variable[TRANSPORT_INDEX].Value = unit.BoardCount;
3491 	unit.Variable[TRANSPORT_INDEX].Max = unit.Type->MaxOnBoard;
3492 
3493 	unit.CurrentOrder()->UpdateUnitVariables(unit);
3494 
3495 	// Resources.
3496 	//Wyrmgus start
3497 //	if (unit.Type->GivesResource) {
3498 	if (unit.GivesResource) {
3499 	//Wyrmgus end
3500 		unit.Variable[GIVERESOURCE_INDEX].Value = unit.ResourcesHeld;
3501 		unit.Variable[GIVERESOURCE_INDEX].Max = unit.ResourcesHeld > unit.Variable[GIVERESOURCE_INDEX].Max ? unit.ResourcesHeld : unit.Variable[GIVERESOURCE_INDEX].Max;
3502 		//Wyrmgus start
3503 		unit.Variable[GIVERESOURCE_INDEX].Enable = 1;
3504 		//Wyrmgus end
3505 	}
3506 	if (unit.Type->BoolFlag[HARVESTER_INDEX].value && unit.CurrentResource) {
3507 		unit.Variable[CARRYRESOURCE_INDEX].Value = unit.ResourcesHeld;
3508 		unit.Variable[CARRYRESOURCE_INDEX].Max = unit.Type->ResInfo[unit.CurrentResource]->ResourceCapacity;
3509 	}
3510 
3511 	//Wyrmgus start
3512 	/*
3513 	// SightRange
3514 	unit.Variable[SIGHTRANGE_INDEX].Value = type->MapDefaultStat.Variables[SIGHTRANGE_INDEX].Value;
3515 	unit.Variable[SIGHTRANGE_INDEX].Max = unit.Stats->Variables[SIGHTRANGE_INDEX].Max;
3516 	*/
3517 	//Wyrmgus end
3518 
3519 	// AttackRange
3520 	//Wyrmgus start
3521 //	unit.Variable[ATTACKRANGE_INDEX].Value = type->MapDefaultStat.Variables[ATTACKRANGE_INDEX].Max;
3522 //	unit.Variable[ATTACKRANGE_INDEX].Max = unit.Stats->Variables[ATTACKRANGE_INDEX].Max;
3523 	//Wyrmgus end
3524 
3525 	// Priority
3526 	unit.Variable[PRIORITY_INDEX].Value = type->MapDefaultStat.Variables[PRIORITY_INDEX].Max;
3527 	unit.Variable[PRIORITY_INDEX].Max = unit.Stats->Variables[PRIORITY_INDEX].Max;
3528 
3529 	// Position
3530 	if (unit.MapLayer) {
3531 		unit.Variable[POSX_INDEX].Value = unit.tilePos.x;
3532 		unit.Variable[POSX_INDEX].Max = unit.MapLayer->GetWidth();
3533 		unit.Variable[POSY_INDEX].Value = unit.tilePos.y;
3534 		unit.Variable[POSY_INDEX].Max = unit.MapLayer->GetHeight();
3535 	}
3536 
3537 	// Target Position
3538 	const Vec2i goalPos = unit.CurrentOrder()->GetGoalPos();
3539 	unit.Variable[TARGETPOSX_INDEX].Value = goalPos.x;
3540 	unit.Variable[TARGETPOSX_INDEX].Max = Map.Info.MapWidths[unit.CurrentOrder()->GetGoalMapLayer()];
3541 	unit.Variable[TARGETPOSY_INDEX].Value = goalPos.y;
3542 	unit.Variable[TARGETPOSY_INDEX].Max = Map.Info.MapHeights[unit.CurrentOrder()->GetGoalMapLayer()];
3543 
3544 	// RadarRange
3545 	unit.Variable[RADAR_INDEX].Value = unit.Stats->Variables[RADAR_INDEX].Value;
3546 	unit.Variable[RADAR_INDEX].Max = unit.Stats->Variables[RADAR_INDEX].Value;
3547 
3548 	// RadarJammerRange
3549 	unit.Variable[RADARJAMMER_INDEX].Value = unit.Stats->Variables[RADARJAMMER_INDEX].Value;
3550 	unit.Variable[RADARJAMMER_INDEX].Max = unit.Stats->Variables[RADARJAMMER_INDEX].Value;
3551 
3552 	// SlotNumber
3553 	unit.Variable[SLOT_INDEX].Value = UnitNumber(unit);
3554 	unit.Variable[SLOT_INDEX].Max = UnitManager.GetUsedSlotCount();
3555 
3556 	// Is Alive
3557 	unit.Variable[ISALIVE_INDEX].Value = unit.IsAlive() ? 1 : 0;
3558 	unit.Variable[ISALIVE_INDEX].Max = 1;
3559 
3560 	// Player
3561 	unit.Variable[PLAYER_INDEX].Value = unit.Player->Index;
3562 	unit.Variable[PLAYER_INDEX].Max = PlayerMax;
3563 
3564 	for (int i = 0; i < NVARALREADYDEFINED; i++) { // default values
3565 		unit.Variable[i].Enable &= unit.Variable[i].Max > 0;
3566 		//Wyrmgus start
3567 //		if (unit.Variable[i].Value > unit.Variable[i].Max) {
3568 		if (unit.Variable[i].Value > unit.GetModifiedVariable(i, VariableMax)) {
3569 		//Wyrmgus end
3570 			DebugPrint("Value out of range: '%s'(%d), for variable '%s',"
3571 					   " value = %d, max = %d\n"
3572 					   _C_ type->Ident.c_str() _C_ UnitNumber(unit) _C_ UnitTypeVar.VariableNameLookup[i]
3573 					   //Wyrmgus start
3574 //					   _C_ unit.Variable[i].Value _C_ unit.Variable[i].Max);
3575 					   _C_ unit.Variable[i].Value _C_ unit.GetModifiedVariable(i, VariableMax));
3576 					   //Wyrmgus end
3577 		   //Wyrmgus start
3578 //			clamp(&unit.Variable[i].Value, 0, unit.Variable[i].Max);
3579 			clamp(&unit.Variable[i].Value, 0, unit.GetModifiedVariable(i, VariableMax));
3580 		   //Wyrmgus end
3581 		}
3582 	}
3583 }
3584 
3585 //Wyrmgus start
3586 /**
3587 **  Define a species phylum.
3588 **
3589 **  @param l  Lua state.
3590 */
CclDefineSpeciesPhylum(lua_State * l)3591 static int CclDefineSpeciesPhylum(lua_State *l)
3592 {
3593 	LuaCheckArgs(l, 2);
3594 	if (!lua_istable(l, 2)) {
3595 		LuaError(l, "incorrect argument (expected table)");
3596 	}
3597 
3598 	std::string phylum_ident = LuaToString(l, 1);
3599 	CSpeciesPhylum *phylum = GetSpeciesPhylum(phylum_ident);
3600 	if (!phylum) {
3601 		phylum = new CSpeciesPhylum;
3602 		SpeciesPhylums.push_back(phylum);
3603 		phylum->Ident = phylum_ident;
3604 	}
3605 
3606 	//  Parse the list:
3607 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
3608 		const char *value = LuaToString(l, -2);
3609 
3610 		if (!strcmp(value, "Name")) {
3611 			phylum->Name = LuaToString(l, -1);
3612 		} else if (!strcmp(value, "Kingdom")) {
3613 			phylum->Kingdom = LuaToString(l, -1);
3614 		} else if (!strcmp(value, "Subkingdom")) {
3615 			phylum->Subkingdom = LuaToString(l, -1);
3616 		} else if (!strcmp(value, "Infrakingdom")) {
3617 			phylum->Infrakingdom = LuaToString(l, -1);
3618 		} else {
3619 			LuaError(l, "Unsupported tag: %s" _C_ value);
3620 		}
3621 	}
3622 
3623 	return 0;
3624 }
3625 
3626 /**
3627 **  Define a species class.
3628 **
3629 **  @param l  Lua state.
3630 */
CclDefineSpeciesClass(lua_State * l)3631 static int CclDefineSpeciesClass(lua_State *l)
3632 {
3633 	LuaCheckArgs(l, 2);
3634 	if (!lua_istable(l, 2)) {
3635 		LuaError(l, "incorrect argument (expected table)");
3636 	}
3637 
3638 	std::string class_ident = LuaToString(l, 1);
3639 	CSpeciesClass *species_class = GetSpeciesClass(class_ident);
3640 	if (!species_class) {
3641 		species_class = new CSpeciesClass;
3642 		SpeciesClasses.push_back(species_class);
3643 		species_class->Ident = class_ident;
3644 	}
3645 
3646 	//  Parse the list:
3647 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
3648 		const char *value = LuaToString(l, -2);
3649 
3650 		if (!strcmp(value, "Name")) {
3651 			species_class->Name = LuaToString(l, -1);
3652 		} else if (!strcmp(value, "Phylum")) {
3653 			std::string phylum_ident = LuaToString(l, -1);
3654 			CSpeciesPhylum *phylum = GetSpeciesPhylum(phylum_ident);
3655 			if (phylum) {
3656 				species_class->Phylum = phylum;
3657 			} else {
3658 				LuaError(l, "Species phylum \"%s\" doesn't exist." _C_ phylum_ident.c_str());
3659 			}
3660 		} else if (!strcmp(value, "Subphylum")) {
3661 			species_class->Subphylum = LuaToString(l, -1);
3662 		} else if (!strcmp(value, "Infraphylum")) {
3663 			species_class->Infraphylum = LuaToString(l, -1);
3664 		} else if (!strcmp(value, "Superclass")) {
3665 			species_class->Superclass = LuaToString(l, -1);
3666 		} else {
3667 			LuaError(l, "Unsupported tag: %s" _C_ value);
3668 		}
3669 	}
3670 
3671 	return 0;
3672 }
3673 
3674 /**
3675 **  Define a species order.
3676 **
3677 **  @param l  Lua state.
3678 */
CclDefineSpeciesOrder(lua_State * l)3679 static int CclDefineSpeciesOrder(lua_State *l)
3680 {
3681 	LuaCheckArgs(l, 2);
3682 	if (!lua_istable(l, 2)) {
3683 		LuaError(l, "incorrect argument (expected table)");
3684 	}
3685 
3686 	std::string order_ident = LuaToString(l, 1);
3687 	CSpeciesOrder *order = GetSpeciesOrder(order_ident);
3688 	if (!order) {
3689 		order = new CSpeciesOrder;
3690 		SpeciesOrders.push_back(order);
3691 		order->Ident = order_ident;
3692 	}
3693 
3694 	//  Parse the list:
3695 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
3696 		const char *value = LuaToString(l, -2);
3697 
3698 		if (!strcmp(value, "Name")) {
3699 			order->Name = LuaToString(l, -1);
3700 		} else if (!strcmp(value, "Class")) {
3701 			std::string class_ident = LuaToString(l, -1);
3702 			CSpeciesClass *species_class = GetSpeciesClass(class_ident);
3703 			if (species_class) {
3704 				order->Class = species_class;
3705 			} else {
3706 				LuaError(l, "Species class \"%s\" doesn't exist." _C_ class_ident.c_str());
3707 			}
3708 		} else if (!strcmp(value, "Subclass")) {
3709 			order->Subclass = LuaToString(l, -1);
3710 		} else if (!strcmp(value, "Infraclass")) {
3711 			order->Infraclass = LuaToString(l, -1);
3712 		} else {
3713 			LuaError(l, "Unsupported tag: %s" _C_ value);
3714 		}
3715 	}
3716 
3717 	return 0;
3718 }
3719 
3720 /**
3721 **  Define a species family.
3722 **
3723 **  @param l  Lua state.
3724 */
CclDefineSpeciesFamily(lua_State * l)3725 static int CclDefineSpeciesFamily(lua_State *l)
3726 {
3727 	LuaCheckArgs(l, 2);
3728 	if (!lua_istable(l, 2)) {
3729 		LuaError(l, "incorrect argument (expected table)");
3730 	}
3731 
3732 	std::string family_ident = LuaToString(l, 1);
3733 	CSpeciesFamily *family = GetSpeciesFamily(family_ident);
3734 	if (!family) {
3735 		family = new CSpeciesFamily;
3736 		SpeciesFamilies.push_back(family);
3737 		family->Ident = family_ident;
3738 	}
3739 
3740 	//  Parse the list:
3741 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
3742 		const char *value = LuaToString(l, -2);
3743 
3744 		if (!strcmp(value, "Name")) {
3745 			family->Name = LuaToString(l, -1);
3746 		} else if (!strcmp(value, "Order")) {
3747 			std::string order_ident = LuaToString(l, -1);
3748 			CSpeciesOrder *order = GetSpeciesOrder(order_ident);
3749 			if (order) {
3750 				family->Order = order;
3751 			} else {
3752 				LuaError(l, "Species order \"%s\" doesn't exist." _C_ order_ident.c_str());
3753 			}
3754 		} else if (!strcmp(value, "Suborder")) {
3755 			family->Suborder = LuaToString(l, -1);
3756 		} else if (!strcmp(value, "Infraorder")) {
3757 			family->Infraorder = LuaToString(l, -1);
3758 		} else if (!strcmp(value, "Superfamily")) {
3759 			family->Superfamily = LuaToString(l, -1);
3760 		} else {
3761 			LuaError(l, "Unsupported tag: %s" _C_ value);
3762 		}
3763 	}
3764 
3765 	return 0;
3766 }
3767 
3768 /**
3769 **  Define a species genus.
3770 **
3771 **  @param l  Lua state.
3772 */
CclDefineSpeciesGenus(lua_State * l)3773 static int CclDefineSpeciesGenus(lua_State *l)
3774 {
3775 	LuaCheckArgs(l, 2);
3776 	if (!lua_istable(l, 2)) {
3777 		LuaError(l, "incorrect argument (expected table)");
3778 	}
3779 
3780 	std::string genus_ident = LuaToString(l, 1);
3781 	CSpeciesGenus *genus = GetSpeciesGenus(genus_ident);
3782 	if (!genus) {
3783 		genus = new CSpeciesGenus;
3784 		SpeciesGenuses.push_back(genus);
3785 		genus->Ident = genus_ident;
3786 	}
3787 
3788 	//  Parse the list:
3789 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
3790 		const char *value = LuaToString(l, -2);
3791 
3792 		if (!strcmp(value, "Name")) {
3793 			genus->Name = LuaToString(l, -1);
3794 		} else if (!strcmp(value, "CommonName")) {
3795 			genus->CommonName = LuaToString(l, -1);
3796 		} else if (!strcmp(value, "Family")) {
3797 			std::string family_ident = LuaToString(l, -1);
3798 			CSpeciesFamily *family = GetSpeciesFamily(family_ident);
3799 			if (family) {
3800 				genus->Family = family;
3801 			} else {
3802 				LuaError(l, "Species family \"%s\" doesn't exist." _C_ family_ident.c_str());
3803 			}
3804 		} else if (!strcmp(value, "Subfamily")) {
3805 			genus->Subfamily = LuaToString(l, -1);
3806 		} else if (!strcmp(value, "Tribe")) {
3807 			genus->Tribe = LuaToString(l, -1);
3808 		} else {
3809 			LuaError(l, "Unsupported tag: %s" _C_ value);
3810 		}
3811 	}
3812 
3813 	return 0;
3814 }
3815 
3816 /**
3817 **  Define a species.
3818 **
3819 **  @param l  Lua state.
3820 */
CclDefineSpecies(lua_State * l)3821 static int CclDefineSpecies(lua_State *l)
3822 {
3823 	LuaCheckArgs(l, 2);
3824 	if (!lua_istable(l, 2)) {
3825 		LuaError(l, "incorrect argument (expected table)");
3826 	}
3827 
3828 	std::string species_ident = LuaToString(l, 1);
3829 	CSpecies *species = GetSpecies(species_ident);
3830 	if (!species) {
3831 		species = new CSpecies;
3832 		Species.push_back(species);
3833 		species->Ident = species_ident;
3834 	}
3835 
3836 	//  Parse the list:
3837 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
3838 		const char *value = LuaToString(l, -2);
3839 
3840 		if (!strcmp(value, "Name")) {
3841 			species->Name = LuaToString(l, -1);
3842 		} else if (!strcmp(value, "Description")) {
3843 			species->Description = LuaToString(l, -1);
3844 		} else if (!strcmp(value, "Quote")) {
3845 			species->Quote = LuaToString(l, -1);
3846 		} else if (!strcmp(value, "Background")) {
3847 			species->Background = LuaToString(l, -1);
3848 		} else if (!strcmp(value, "Era")) {
3849 			std::string era_ident = LuaToString(l, -1);
3850 			int era_id = GetEraIdByName(era_ident);
3851 			if (era_id != -1) {
3852 				species->Era = era_id;
3853 			} else {
3854 				LuaError(l, "Era \"%s\" doesn't exist." _C_ era_ident.c_str());
3855 			}
3856 		} else if (!strcmp(value, "Sapient")) {
3857 			species->Sapient = LuaToBoolean(l, -1);
3858 		} else if (!strcmp(value, "Prehistoric")) {
3859 			species->Prehistoric = LuaToBoolean(l, -1);
3860 		} else if (!strcmp(value, "Genus")) {
3861 			std::string genus_ident = LuaToString(l, -1);
3862 			CSpeciesGenus *genus = GetSpeciesGenus(genus_ident);
3863 			if (genus) {
3864 				species->Genus = genus;
3865 			} else {
3866 				LuaError(l, "Species genus \"%s\" doesn't exist." _C_ genus_ident.c_str());
3867 			}
3868 		} else if (!strcmp(value, "Species")) {
3869 			species->Species = LuaToString(l, -1);
3870 		} else if (!strcmp(value, "ChildUpgrade")) {
3871 			species->ChildUpgrade = LuaToString(l, -1);
3872 		} else if (!strcmp(value, "HomePlane")) {
3873 			std::string plane_ident = LuaToString(l, -1);
3874 			CPlane *plane = CPlane::GetPlane(plane_ident);
3875 			if (plane) {
3876 				species->HomePlane = plane;
3877 				plane->Species.push_back(species);
3878 			} else {
3879 				LuaError(l, "Plane \"%s\" doesn't exist." _C_ plane_ident.c_str());
3880 			}
3881 		} else if (!strcmp(value, "Homeworld")) {
3882 			std::string world_ident = LuaToString(l, -1);
3883 			CWorld *world = CWorld::GetWorld(world_ident);
3884 			if (world) {
3885 				species->Homeworld = world;
3886 				world->Species.push_back(species);
3887 			} else {
3888 				LuaError(l, "World \"%s\" doesn't exist." _C_ world_ident.c_str());
3889 			}
3890 		} else if (!strcmp(value, "Terrains")) {
3891 			if (!lua_istable(l, -1)) {
3892 				LuaError(l, "incorrect argument");
3893 			}
3894 			const int subargs = lua_rawlen(l, -1);
3895 			for (int j = 0; j < subargs; ++j) {
3896 				CTerrainType *terrain = CTerrainType::GetTerrainType(LuaToString(l, -1, j + 1));
3897 				if (terrain == nullptr) {
3898 					LuaError(l, "Terrain doesn't exist.");
3899 				}
3900 				species->Terrains.push_back(terrain);
3901 			}
3902 		} else if (!strcmp(value, "EvolvesFrom")) {
3903 			species->EvolvesFrom.clear();
3904 			const int args = lua_rawlen(l, -1);
3905 			for (int j = 0; j < args; ++j) {
3906 				std::string evolves_from_ident = LuaToString(l, -1, j + 1);
3907 				CSpecies *evolves_from = GetSpecies(evolves_from_ident);
3908 				if (evolves_from) {
3909 					species->EvolvesFrom.push_back(evolves_from);
3910 					evolves_from->EvolvesTo.push_back(species);
3911 				} else {
3912 					LuaError(l, "Species \"%s\" doesn't exist." _C_ evolves_from_ident.c_str());
3913 				}
3914 			}
3915 		} else {
3916 			LuaError(l, "Unsupported tag: %s" _C_ value);
3917 		}
3918 	}
3919 
3920 	for (size_t i = 0; i < species->EvolvesFrom.size(); ++i) {
3921 		if (species->Era != -1 && species->EvolvesFrom[i]->Era != -1 && species->Era <= species->EvolvesFrom[i]->Era) {
3922 			LuaError(l, "Species \"%s\" is set to evolve from \"%s\", but is from the same or an earlier era than the latter." _C_ species->Ident.c_str() _C_ species->EvolvesFrom[i]->Ident.c_str());
3923 		}
3924 	}
3925 
3926 	return 0;
3927 }
3928 
CclGetSpecies(lua_State * l)3929 static int CclGetSpecies(lua_State *l)
3930 {
3931 	lua_createtable(l, Species.size(), 0);
3932 	for (size_t i = 1; i <= Species.size(); ++i)
3933 	{
3934 		lua_pushstring(l, Species[i-1]->Ident.c_str());
3935 		lua_rawseti(l, -2, i);
3936 	}
3937 	return 1;
3938 }
3939 
3940 /**
3941 **  Get species data.
3942 **
3943 **  @param l  Lua state.
3944 */
CclGetSpeciesData(lua_State * l)3945 static int CclGetSpeciesData(lua_State *l)
3946 {
3947 	if (lua_gettop(l) < 2) {
3948 		LuaError(l, "incorrect argument");
3949 	}
3950 	std::string species_ident = LuaToString(l, 1);
3951 	const CSpecies *species = GetSpecies(species_ident);
3952 	if (!species) {
3953 		LuaError(l, "Species \"%s\" doesn't exist." _C_ species_ident.c_str());
3954 	}
3955 	const char *data = LuaToString(l, 2);
3956 
3957 	if (!strcmp(data, "Name")) {
3958 		lua_pushstring(l, species->Name.c_str());
3959 		return 1;
3960 	} else if (!strcmp(data, "Description")) {
3961 		lua_pushstring(l, species->Description.c_str());
3962 		return 1;
3963 	} else if (!strcmp(data, "Quote")) {
3964 		lua_pushstring(l, species->Quote.c_str());
3965 		return 1;
3966 	} else if (!strcmp(data, "Background")) {
3967 		lua_pushstring(l, species->Background.c_str());
3968 		return 1;
3969 	} else if (!strcmp(data, "Family")) {
3970 		if (species->Genus != nullptr && species->Genus->Family != nullptr) {
3971 			lua_pushstring(l, species->Genus->Family->Ident.c_str());
3972 		} else {
3973 			lua_pushstring(l, "");
3974 		}
3975 		return 1;
3976 	} else if (!strcmp(data, "Genus")) {
3977 		if (species->Genus != nullptr) {
3978 			lua_pushstring(l, species->Genus->Ident.c_str());
3979 		} else {
3980 			lua_pushstring(l, "");
3981 		}
3982 		return 1;
3983 	} else if (!strcmp(data, "Era")) {
3984 		lua_pushnumber(l, species->Era);
3985 		return 1;
3986 	} else if (!strcmp(data, "Sapient")) {
3987 		lua_pushboolean(l, species->Sapient);
3988 		return 1;
3989 	} else if (!strcmp(data, "Prehistoric")) {
3990 		lua_pushboolean(l, species->Prehistoric);
3991 		return 1;
3992 	} else if (!strcmp(data, "ChildUpgrade")) {
3993 		lua_pushstring(l, species->ChildUpgrade.c_str());
3994 		return 1;
3995 	} else if (!strcmp(data, "HomePlane")) {
3996 		if (species->HomePlane != nullptr) {
3997 			lua_pushstring(l, species->HomePlane->Ident.c_str());
3998 		} else {
3999 			lua_pushstring(l, "");
4000 		}
4001 		return 1;
4002 	} else if (!strcmp(data, "Homeworld")) {
4003 		if (species->Homeworld != nullptr) {
4004 			lua_pushstring(l, species->Homeworld->Ident.c_str());
4005 		} else {
4006 			lua_pushstring(l, "");
4007 		}
4008 		return 1;
4009 	} else if (!strcmp(data, "Type")) {
4010 		if (species->Type != nullptr) {
4011 			lua_pushstring(l, species->Type->Ident.c_str());
4012 		} else {
4013 			lua_pushstring(l, "");
4014 		}
4015 		return 1;
4016 	} else if (!strcmp(data, "Terrains")) {
4017 		lua_createtable(l, species->Terrains.size(), 0);
4018 		for (size_t i = 1; i <= species->Terrains.size(); ++i)
4019 		{
4020 			lua_pushstring(l, species->Terrains[i-1]->Ident.c_str());
4021 			lua_rawseti(l, -2, i);
4022 		}
4023 		return 1;
4024 	} else if (!strcmp(data, "EvolvesFrom")) {
4025 		lua_createtable(l, species->EvolvesFrom.size(), 0);
4026 		for (size_t i = 1; i <= species->EvolvesFrom.size(); ++i)
4027 		{
4028 			lua_pushstring(l, species->EvolvesFrom[i-1]->Ident.c_str());
4029 			lua_rawseti(l, -2, i);
4030 		}
4031 		return 1;
4032 	} else if (!strcmp(data, "EvolvesTo")) {
4033 		lua_createtable(l, species->EvolvesTo.size(), 0);
4034 		for (size_t i = 1; i <= species->EvolvesTo.size(); ++i)
4035 		{
4036 			lua_pushstring(l, species->EvolvesTo[i-1]->Ident.c_str());
4037 			lua_rawseti(l, -2, i);
4038 		}
4039 		return 1;
4040 	} else {
4041 		LuaError(l, "Invalid field: %s" _C_ data);
4042 	}
4043 
4044 	return 0;
4045 }
4046 
4047 /**
4048 **  Get species genus data.
4049 **
4050 **  @param l  Lua state.
4051 */
CclGetSpeciesGenusData(lua_State * l)4052 static int CclGetSpeciesGenusData(lua_State *l)
4053 {
4054 	if (lua_gettop(l) < 2) {
4055 		LuaError(l, "incorrect argument");
4056 	}
4057 	std::string genus_ident = LuaToString(l, 1);
4058 	const CSpeciesGenus *genus = GetSpeciesGenus(genus_ident);
4059 	if (!genus) {
4060 		LuaError(l, "Species genus \"%s\" doesn't exist." _C_ genus_ident.c_str());
4061 	}
4062 	const char *data = LuaToString(l, 2);
4063 
4064 	if (!strcmp(data, "Name")) {
4065 		lua_pushstring(l, genus->Name.c_str());
4066 		return 1;
4067 	} else if (!strcmp(data, "CommonName")) {
4068 		lua_pushstring(l, genus->CommonName.c_str());
4069 		return 1;
4070 	} else if (!strcmp(data, "Family")) {
4071 		lua_pushstring(l, genus->Family->Ident.c_str());
4072 		return 1;
4073 	} else {
4074 		LuaError(l, "Invalid field: %s" _C_ data);
4075 	}
4076 
4077 	return 0;
4078 }
4079 //Wyrmgus end
4080 
4081 //Wyrmgus start
4082 /**
4083 **  Define settlement site unit
4084 **
4085 **  @param l  Lua state.
4086 */
CclSetSettlementSiteUnit(lua_State * l)4087 static int CclSetSettlementSiteUnit(lua_State *l)
4088 {
4089 	LuaCheckArgs(l, 1);
4090 	SettlementSiteUnitType = UnitTypeByIdent(LuaToString(l, 1));
4091 
4092 	return 0;
4093 }
4094 //Wyrmgus end
4095 
4096 /**
4097 **  Set the map default stat for a unit type
4098 **
4099 **  @param ident			Unit type ident
4100 **  @param variable_key		Key of the desired variable
4101 **  @param value			Value to set to
4102 **  @param variable_type	Type to be modified (i.e. "Value", "Max", etc.); alternatively, resource type if variable_key equals "Costs"
4103 */
SetModStat(const std::string & mod_file,const std::string & ident,const std::string & variable_key,const int value,const std::string & variable_type)4104 void SetModStat(const std::string &mod_file, const std::string &ident, const std::string &variable_key, const int value, const std::string &variable_type)
4105 {
4106 	CUnitType *type = UnitTypeByIdent(ident.c_str());
4107 
4108 	if (type->ModDefaultStats.find(mod_file) == type->ModDefaultStats.end()) {
4109 		type->ModDefaultStats[mod_file].Variables = new CVariable[UnitTypeVar.GetNumberVariable()];
4110 	}
4111 
4112 	if (variable_key == "Costs") {
4113 		const int resId = GetResourceIdByName(variable_type.c_str());
4114 		if (GameRunning || Editor.Running == EditorEditing) {
4115 			type->MapDefaultStat.Costs[resId] -= type->ModDefaultStats[mod_file].Costs[resId];
4116 			for (int player = 0; player < PlayerMax; ++player) {
4117 				type->Stats[player].Costs[resId] -= type->ModDefaultStats[mod_file].Costs[resId];
4118 			}
4119 		}
4120 		type->ModDefaultStats[mod_file].Costs[resId] = value;
4121 		if (GameRunning || Editor.Running == EditorEditing) {
4122 			type->MapDefaultStat.Costs[resId] += type->ModDefaultStats[mod_file].Costs[resId];
4123 			for (int player = 0; player < PlayerMax; ++player) {
4124 				type->Stats[player].Costs[resId] += type->ModDefaultStats[mod_file].Costs[resId];
4125 			}
4126 		}
4127 	} else if (variable_key == "ImproveProduction") {
4128 		const int resId = GetResourceIdByName(variable_type.c_str());
4129 		if (GameRunning || Editor.Running == EditorEditing) {
4130 			type->MapDefaultStat.ImproveIncomes[resId] -= type->ModDefaultStats[mod_file].ImproveIncomes[resId];
4131 			for (int player = 0; player < PlayerMax; ++player) {
4132 				type->Stats[player].ImproveIncomes[resId] -= type->ModDefaultStats[mod_file].ImproveIncomes[resId];
4133 			}
4134 		}
4135 		type->ModDefaultStats[mod_file].ImproveIncomes[resId] = value;
4136 		if (GameRunning || Editor.Running == EditorEditing) {
4137 			type->MapDefaultStat.ImproveIncomes[resId] += type->ModDefaultStats[mod_file].ImproveIncomes[resId];
4138 			for (int player = 0; player < PlayerMax; ++player) {
4139 				type->Stats[player].ImproveIncomes[resId] += type->ModDefaultStats[mod_file].ImproveIncomes[resId];
4140 			}
4141 		}
4142 	} else if (variable_key == "UnitStock") {
4143 		CUnitType *unit_type = UnitTypeByIdent(variable_type);
4144 		if (GameRunning || Editor.Running == EditorEditing) {
4145 			type->MapDefaultStat.ChangeUnitStock(unit_type, - type->ModDefaultStats[mod_file].GetUnitStock(unit_type));
4146 			for (int player = 0; player < PlayerMax; ++player) {
4147 				type->Stats[player].ChangeUnitStock(unit_type, - type->ModDefaultStats[mod_file].GetUnitStock(unit_type));
4148 			}
4149 		}
4150 		type->ModDefaultStats[mod_file].SetUnitStock(unit_type, value);
4151 		if (GameRunning || Editor.Running == EditorEditing) {
4152 			type->MapDefaultStat.ChangeUnitStock(unit_type, type->ModDefaultStats[mod_file].GetUnitStock(unit_type));
4153 			for (int player = 0; player < PlayerMax; ++player) {
4154 				type->Stats[player].ChangeUnitStock(unit_type, type->ModDefaultStats[mod_file].GetUnitStock(unit_type));
4155 			}
4156 		}
4157 	} else {
4158 		int variable_index = UnitTypeVar.VariableNameLookup[variable_key.c_str()];
4159 		if (variable_index != -1) { // valid index
4160 			if (variable_type == "Value") {
4161 				if (GameRunning || Editor.Running == EditorEditing) {
4162 					type->MapDefaultStat.Variables[variable_index].Value -= type->ModDefaultStats[mod_file].Variables[variable_index].Value;
4163 					for (int player = 0; player < PlayerMax; ++player) {
4164 						type->Stats[player].Variables[variable_index].Value -= type->ModDefaultStats[mod_file].Variables[variable_index].Value;
4165 					}
4166 				}
4167 				type->ModDefaultStats[mod_file].Variables[variable_index].Value = value;
4168 				if (GameRunning || Editor.Running == EditorEditing) {
4169 					type->MapDefaultStat.Variables[variable_index].Value += type->ModDefaultStats[mod_file].Variables[variable_index].Value;
4170 					for (int player = 0; player < PlayerMax; ++player) {
4171 						type->Stats[player].Variables[variable_index].Value += type->ModDefaultStats[mod_file].Variables[variable_index].Value;
4172 					}
4173 				}
4174 			} else if (variable_type == "Max") {
4175 				if (GameRunning || Editor.Running == EditorEditing) {
4176 					type->MapDefaultStat.Variables[variable_index].Max -= type->ModDefaultStats[mod_file].Variables[variable_index].Max;
4177 					for (int player = 0; player < PlayerMax; ++player) {
4178 						type->Stats[player].Variables[variable_index].Max -= type->ModDefaultStats[mod_file].Variables[variable_index].Max;
4179 					}
4180 				}
4181 				type->ModDefaultStats[mod_file].Variables[variable_index].Max = value;
4182 				if (GameRunning || Editor.Running == EditorEditing) {
4183 					type->MapDefaultStat.Variables[variable_index].Max += type->ModDefaultStats[mod_file].Variables[variable_index].Max;
4184 					for (int player = 0; player < PlayerMax; ++player) {
4185 						type->Stats[player].Variables[variable_index].Max += type->ModDefaultStats[mod_file].Variables[variable_index].Max;
4186 					}
4187 				}
4188 			} else if (variable_type == "Increase") {
4189 				if (GameRunning || Editor.Running == EditorEditing) {
4190 					type->MapDefaultStat.Variables[variable_index].Increase -= type->ModDefaultStats[mod_file].Variables[variable_index].Increase;
4191 					for (int player = 0; player < PlayerMax; ++player) {
4192 						type->Stats[player].Variables[variable_index].Increase -= type->ModDefaultStats[mod_file].Variables[variable_index].Increase;
4193 					}
4194 				}
4195 				type->ModDefaultStats[mod_file].Variables[variable_index].Increase = value;
4196 				if (GameRunning || Editor.Running == EditorEditing) {
4197 					type->MapDefaultStat.Variables[variable_index].Increase += type->ModDefaultStats[mod_file].Variables[variable_index].Increase;
4198 					for (int player = 0; player < PlayerMax; ++player) {
4199 						type->Stats[player].Variables[variable_index].Increase += type->ModDefaultStats[mod_file].Variables[variable_index].Increase;
4200 					}
4201 				}
4202 			} else if (variable_type == "Enable") {
4203 				type->ModDefaultStats[mod_file].Variables[variable_index].Enable = value;
4204 				if (GameRunning || Editor.Running == EditorEditing) {
4205 					type->MapDefaultStat.Variables[variable_index].Enable = type->ModDefaultStats[mod_file].Variables[variable_index].Enable;
4206 					for (int player = 0; player < PlayerMax; ++player) {
4207 						type->Stats[player].Variables[variable_index].Enable = type->ModDefaultStats[mod_file].Variables[variable_index].Enable;
4208 					}
4209 				}
4210 			} else {
4211 				fprintf(stderr, "Invalid type: %s\n", variable_type.c_str());
4212 				return;
4213 			}
4214 		} else {
4215 			fprintf(stderr, "Invalid variable: %s\n", variable_key.c_str());
4216 			return;
4217 		}
4218 	}
4219 }
4220 
4221 /**
4222 **  Set the map sound for a unit type
4223 **
4224 **  @param ident			Unit type ident
4225 **  @param sound_type		Type of the sound
4226 **  @param sound			The sound to be set for that type
4227 */
SetModSound(const std::string & mod_file,const std::string & ident,const std::string & sound,const std::string & sound_type,const std::string & sound_subtype)4228 void SetModSound(const std::string &mod_file, const std::string &ident, const std::string &sound, const std::string &sound_type, const std::string &sound_subtype)
4229 {
4230 	if (sound.empty()) {
4231 		return;
4232 	}
4233 	CUnitType *type = UnitTypeByIdent(ident.c_str());
4234 
4235 	if (sound_type == "selected") {
4236 		type->ModSounds[mod_file].Selected.Name = sound;
4237 	} else if (sound_type == "acknowledge") {
4238 		type->ModSounds[mod_file].Acknowledgement.Name = sound;
4239 	} else if (sound_type == "attack") {
4240 		type->ModSounds[mod_file].Attack.Name = sound;
4241 	//Wyrmgus start
4242 	} else if (sound_type == "idle") {
4243 		type->ModSounds[mod_file].Idle.Name = sound;
4244 	} else if (sound_type == "hit") {
4245 		type->ModSounds[mod_file].Hit.Name = sound;
4246 	} else if (sound_type == "miss") {
4247 		type->ModSounds[mod_file].Miss.Name = sound;
4248 	} else if (sound_type == "fire-missile") {
4249 		type->ModSounds[mod_file].FireMissile.Name = sound;
4250 	} else if (sound_type == "step") {
4251 		type->ModSounds[mod_file].Step.Name = sound;
4252 	} else if (sound_type == "step-dirt") {
4253 		type->ModSounds[mod_file].StepDirt.Name = sound;
4254 	} else if (sound_type == "step-grass") {
4255 		type->ModSounds[mod_file].StepGrass.Name = sound;
4256 	} else if (sound_type == "step-gravel") {
4257 		type->ModSounds[mod_file].StepGravel.Name = sound;
4258 	} else if (sound_type == "step-mud") {
4259 		type->ModSounds[mod_file].StepMud.Name = sound;
4260 	} else if (sound_type == "step-stone") {
4261 		type->ModSounds[mod_file].StepStone.Name = sound;
4262 	} else if (sound_type == "used") {
4263 		type->ModSounds[mod_file].Used.Name = sound;
4264 	//Wyrmgus end
4265 	} else if (sound_type == "build") {
4266 		type->ModSounds[mod_file].Build.Name = sound;
4267 	} else if (sound_type == "ready") {
4268 		type->ModSounds[mod_file].Ready.Name = sound;
4269 	} else if (sound_type == "repair") {
4270 		type->ModSounds[mod_file].Repair.Name = sound;
4271 	} else if (sound_type == "harvest") {
4272 		const int resId = GetResourceIdByName(sound_subtype.c_str());
4273 		type->ModSounds[mod_file].Harvest[resId].Name = sound;
4274 	} else if (sound_type == "help") {
4275 		type->ModSounds[mod_file].Help.Name = sound;
4276 	} else if (sound_type == "dead") {
4277 		int death;
4278 
4279 		for (death = 0; death < ANIMATIONS_DEATHTYPES; ++death) {
4280 			if (sound_subtype == ExtraDeathTypes[death]) {
4281 				break;
4282 			}
4283 		}
4284 		if (death == ANIMATIONS_DEATHTYPES) {
4285 			type->ModSounds[mod_file].Dead[ANIMATIONS_DEATHTYPES].Name = sound;
4286 		} else {
4287 			type->ModSounds[mod_file].Dead[death].Name = sound;
4288 		}
4289 	}
4290 
4291 	if (GameRunning || Editor.Running == EditorEditing) {
4292 		if (sound_type == "selected") {
4293 			type->MapSound.Selected.Name = sound;
4294 		} else if (sound_type == "acknowledge") {
4295 			type->MapSound.Acknowledgement.Name = sound;
4296 		} else if (sound_type == "attack") {
4297 			type->MapSound.Attack.Name = sound;
4298 		//Wyrmgus start
4299 		} else if (sound_type == "idle") {
4300 			type->MapSound.Idle.Name = sound;
4301 		} else if (sound_type == "hit") {
4302 			type->MapSound.Hit.Name = sound;
4303 		} else if (sound_type == "miss") {
4304 			type->MapSound.Miss.Name = sound;
4305 		} else if (sound_type == "fire-missile") {
4306 			type->MapSound.FireMissile.Name = sound;
4307 		} else if (sound_type == "step") {
4308 			type->MapSound.Step.Name = sound;
4309 		} else if (sound_type == "step-dirt") {
4310 			type->MapSound.StepDirt.Name = sound;
4311 		} else if (sound_type == "step-grass") {
4312 			type->MapSound.StepGrass.Name = sound;
4313 		} else if (sound_type == "step-gravel") {
4314 			type->MapSound.StepGravel.Name = sound;
4315 		} else if (sound_type == "step-mud") {
4316 			type->MapSound.StepMud.Name = sound;
4317 		} else if (sound_type == "step-stone") {
4318 			type->MapSound.StepStone.Name = sound;
4319 		} else if (sound_type == "used") {
4320 			type->MapSound.Used.Name = sound;
4321 		//Wyrmgus end
4322 		} else if (sound_type == "build") {
4323 			type->MapSound.Build.Name = sound;
4324 		} else if (sound_type == "ready") {
4325 			type->MapSound.Ready.Name = sound;
4326 		} else if (sound_type == "repair") {
4327 			type->MapSound.Repair.Name = sound;
4328 		} else if (sound_type == "harvest") {
4329 			const int resId = GetResourceIdByName(sound_subtype.c_str());
4330 			type->MapSound.Harvest[resId].Name = sound;
4331 		} else if (sound_type == "help") {
4332 			type->MapSound.Help.Name = sound;
4333 		} else if (sound_type == "dead") {
4334 			int death;
4335 
4336 			for (death = 0; death < ANIMATIONS_DEATHTYPES; ++death) {
4337 				if (sound_subtype == ExtraDeathTypes[death]) {
4338 					break;
4339 				}
4340 			}
4341 			if (death == ANIMATIONS_DEATHTYPES) {
4342 				type->MapSound.Dead[ANIMATIONS_DEATHTYPES].Name = sound;
4343 			} else {
4344 				type->MapSound.Dead[death].Name = sound;
4345 			}
4346 		}
4347 	}
4348 }
4349 
4350 //Wyrmgus start
CclSetModTrains(lua_State * l)4351 static int CclSetModTrains(lua_State *l)
4352 {
4353 	LuaCheckArgs(l, 3);
4354 
4355 	std::string mod_file = LuaToString(l, 1);
4356 	CUnitType *type = UnitTypeByIdent(LuaToString(l, 2));
4357 	if (type == nullptr) {
4358 		LuaError(l, "Unit type doesn't exist.");
4359 	}
4360 
4361 	for (size_t i = 0; i < type->ModTrains[mod_file].size(); ++i) {
4362 		if (std::find(type->ModTrains[mod_file][i]->ModTrainedBy[mod_file].begin(), type->ModTrains[mod_file][i]->ModTrainedBy[mod_file].end(), type) != type->ModTrains[mod_file][i]->ModTrainedBy[mod_file].end()) {
4363 			type->ModTrains[mod_file][i]->ModTrainedBy[mod_file].erase(std::remove(type->ModTrains[mod_file][i]->ModTrainedBy[mod_file].begin(), type->ModTrains[mod_file][i]->ModTrainedBy[mod_file].end(), type), type->ModTrains[mod_file][i]->ModTrainedBy[mod_file].end());
4364 		}
4365 	}
4366 	type->ModTrains[mod_file].clear();
4367 	type->RemoveButtons(-1, mod_file);
4368 
4369 	if (!lua_istable(l, 3)) {
4370 		LuaError(l, "incorrect argument");
4371 	}
4372 	int subargs = lua_rawlen(l, 3);
4373 	for (int i = 0; i < subargs; ++i) {
4374 		const char *value = LuaToString(l, 3, i + 1);
4375 		CUnitType *trained_unit = UnitTypeByIdent(value);
4376 		if (trained_unit != nullptr) {
4377 			type->ModTrains[mod_file].push_back(trained_unit);
4378 			trained_unit->ModTrainedBy[mod_file].push_back(type);
4379 		} else {
4380 			LuaError(l, "Unit type \"%s\" doesn't exist." _C_ value);
4381 		}
4382 	}
4383 
4384 	for (size_t i = 0; i < type->ModTrains[mod_file].size(); ++i) {
4385 		std::string button_definition = "DefineButton({\n";
4386 		button_definition += "\tPos = " + std::to_string((long long) type->ModTrains[mod_file][i]->ButtonPos) + ",\n";
4387 		if (type->ModTrains[mod_file][i]->ButtonLevel) {
4388 			button_definition += "\tLevel = " + type->ModTrains[mod_file][i]->ButtonLevel->Ident + ",\n";
4389 		}
4390 		button_definition += "\tAction = ";
4391 		if (type->ModTrains[mod_file][i]->BoolFlag[BUILDING_INDEX].value) {
4392 			button_definition += "\"build\"";
4393 		} else {
4394 			button_definition += "\"train-unit\"";
4395 		}
4396 		button_definition += ",\n";
4397 		button_definition += "\tValue = \"" + type->ModTrains[mod_file][i]->Ident + "\",\n";
4398 		if (!type->ModTrains[mod_file][i]->ButtonPopup.empty()) {
4399 			button_definition += "\tPopup = \"" + type->ModTrains[mod_file][i]->ButtonPopup + "\",\n";
4400 		}
4401 		button_definition += "\tKey = \"" + type->ModTrains[mod_file][i]->ButtonKey + "\",\n";
4402 		button_definition += "\tHint = \"" + type->ModTrains[mod_file][i]->ButtonHint + "\",\n";
4403 		button_definition += "\tMod = \"" + mod_file + "\",\n";
4404 		button_definition += "\tForUnit = {\"" + type->Ident + "\"},\n";
4405 		button_definition += "})";
4406 		CclCommand(button_definition);
4407 	}
4408 
4409 	return 0;
4410 }
4411 
CclSetModAiDrops(lua_State * l)4412 static int CclSetModAiDrops(lua_State *l)
4413 {
4414 	LuaCheckArgs(l, 3);
4415 
4416 	std::string mod_file = LuaToString(l, 1);
4417 	CUnitType *type = UnitTypeByIdent(LuaToString(l, 2));
4418 	if (type == nullptr) {
4419 		LuaError(l, "Unit type doesn't exist.");
4420 	}
4421 
4422 	type->ModAiDrops[mod_file].clear();
4423 
4424 	if (!lua_istable(l, 3)) {
4425 		LuaError(l, "incorrect argument");
4426 	}
4427 	int subargs = lua_rawlen(l, 3);
4428 	for (int i = 0; i < subargs; ++i) {
4429 		const char *value = LuaToString(l, 3, i + 1);
4430 		CUnitType *dropped_unit = UnitTypeByIdent(value);
4431 		if (dropped_unit != nullptr) {
4432 			type->ModAiDrops[mod_file].push_back(dropped_unit);
4433 		} else {
4434 			LuaError(l, "Unit type \"%s\" doesn't exist." _C_ value);
4435 		}
4436 	}
4437 
4438 	return 0;
4439 }
4440 //Wyrmgus end
4441 
4442 /**
4443 **  Register CCL features for unit-type.
4444 */
UnitTypeCclRegister()4445 void UnitTypeCclRegister()
4446 {
4447 	lua_register(Lua, "DefineUnitType", CclDefineUnitType);
4448 	lua_register(Lua, "DefineUnitStats", CclDefineUnitStats);
4449 	lua_register(Lua, "DefineBoolFlags", CclDefineBoolFlags);
4450 	lua_register(Lua, "DefineVariables", CclDefineVariables);
4451 	lua_register(Lua, "DefineDecorations", CclDefineDecorations);
4452 
4453 	lua_register(Lua, "DefineExtraDeathTypes", CclDefineExtraDeathTypes);
4454 	//Wyrmgus start
4455 	lua_register(Lua, "GetUnitTypes", CclGetUnitTypes);
4456 	lua_register(Lua, "GetAnimations", CclGetAnimations);
4457 	//Wyrmgus end
4458 
4459 	UnitTypeVar.Init();
4460 
4461 	lua_register(Lua, "UnitType", CclUnitType);
4462 	lua_register(Lua, "UnitTypeArray", CclUnitTypeArray);
4463 	// unit type structure access
4464 	lua_register(Lua, "GetUnitTypeIdent", CclGetUnitTypeIdent);
4465 	lua_register(Lua, "GetUnitTypeName", CclGetUnitTypeName);
4466 	lua_register(Lua, "SetUnitTypeName", CclSetUnitTypeName);
4467 	lua_register(Lua, "GetUnitTypeData", CclGetUnitTypeData);
4468 	//Wyrmgus start
4469 	lua_register(Lua, "DefineSpeciesPhylum", CclDefineSpeciesPhylum);
4470 	lua_register(Lua, "DefineSpeciesClass", CclDefineSpeciesClass);
4471 	lua_register(Lua, "DefineSpeciesOrder", CclDefineSpeciesOrder);
4472 	lua_register(Lua, "DefineSpeciesFamily", CclDefineSpeciesFamily);
4473 	lua_register(Lua, "DefineSpeciesGenus", CclDefineSpeciesGenus);
4474 	lua_register(Lua, "DefineSpecies", CclDefineSpecies);
4475 	lua_register(Lua, "GetSpecies", CclGetSpecies);
4476 	lua_register(Lua, "GetSpeciesData", CclGetSpeciesData);
4477 	lua_register(Lua, "GetSpeciesGenusData", CclGetSpeciesGenusData);
4478 	lua_register(Lua, "SetSettlementSiteUnit", CclSetSettlementSiteUnit);
4479 	lua_register(Lua, "SetModTrains", CclSetModTrains);
4480 	lua_register(Lua, "SetModAiDrops", CclSetModAiDrops);
4481 	//Wyrmgus end
4482 }
4483 
4484 //@}
4485