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