1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 
4 #include <set>
5 #include <list>
6 #include <cctype>
7 
8 #include "LuaSyncedCtrl.h"
9 
10 #include "LuaInclude.h"
11 
12 #include "LuaConfig.h"
13 #include "LuaRules.h" // for MAX_LUA_COB_ARGS
14 #include "LuaHandleSynced.h"
15 #include "LuaHashString.h"
16 #include "LuaMetalMap.h"
17 #include "LuaSyncedMoveCtrl.h"
18 #include "LuaUtils.h"
19 #include "Game/Game.h"
20 #include "Game/Camera.h"
21 #include "Game/GameHelper.h"
22 #include "Game/SelectedUnitsHandler.h"
23 #include "Game/Players/PlayerHandler.h"
24 #include "Game/Players/Player.h"
25 #include "Net/GameServer.h"
26 #include "Map/Ground.h"
27 #include "Map/MapDamage.h"
28 #include "Map/MapInfo.h"
29 #include "Map/ReadMap.h"
30 #include "Rendering/Env/GrassDrawer.h"
31 #include "Rendering/Env/IGroundDecalDrawer.h"
32 #include "Rendering/Env/ITreeDrawer.h"
33 #include "Rendering/Models/IModelParser.h"
34 #include "Sim/Features/Feature.h"
35 #include "Sim/Features/FeatureHandler.h"
36 #include "Sim/Misc/CollisionVolume.h"
37 #include "Sim/Misc/DamageArray.h"
38 #include "Sim/Misc/LosHandler.h"
39 #include "Sim/Misc/ModInfo.h"
40 #include "Sim/Misc/SmoothHeightMesh.h"
41 #include "Sim/Misc/Team.h"
42 #include "Sim/Misc/TeamHandler.h"
43 #include "Sim/Misc/QuadField.h"
44 #include "Sim/MoveTypes/AAirMoveType.h"
45 #include "Sim/Path/IPathManager.h"
46 #include "Sim/Projectiles/ExplosionGenerator.h"
47 #include "Sim/Projectiles/Projectile.h"
48 #include "Sim/Projectiles/PieceProjectile.h"
49 #include "Sim/Projectiles/WeaponProjectiles/WeaponProjectile.h"
50 #include "Sim/Projectiles/WeaponProjectiles/WeaponProjectileFactory.h"
51 #include "Sim/Projectiles/ProjectileHandler.h"
52 #include "Sim/Units/Unit.h"
53 #include "Sim/Units/UnitDef.h"
54 #include "Sim/Units/UnitHandler.h"
55 #include "Sim/Units/UnitDefHandler.h"
56 #include "Sim/Units/UnitLoader.h"
57 #include "Sim/Units/Scripts/CobInstance.h"
58 #include "Sim/Units/Scripts/LuaUnitScript.h"
59 #include "Sim/Units/UnitTypes/Builder.h"
60 #include "Sim/Units/UnitTypes/Factory.h"
61 #include "Sim/Units/UnitTypes/TransportUnit.h"
62 #include "Sim/Units/CommandAI/Command.h"
63 #include "Sim/Units/CommandAI/CommandAI.h"
64 #include "Sim/Units/CommandAI/FactoryCAI.h"
65 #include "Sim/Units/UnitTypes/ExtractorBuilding.h"
66 #include "Sim/Weapons/PlasmaRepulser.h"
67 #include "Sim/Weapons/Weapon.h"
68 #include "Sim/Weapons/WeaponDefHandler.h"
69 #include "System/EventHandler.h"
70 #include "System/myMath.h"
71 #include "System/ObjectDependenceTypes.h"
72 #include "System/Log/ILog.h"
73 #include "System/Sync/HsiehHash.h"
74 
75 using std::max;
76 using std::min;
77 
78 
79 /******************************************************************************/
80 /******************************************************************************/
81 
82 bool LuaSyncedCtrl::inCreateUnit = false;
83 bool LuaSyncedCtrl::inDestroyUnit = false;
84 bool LuaSyncedCtrl::inTransferUnit = false;
85 bool LuaSyncedCtrl::inCreateFeature = false;
86 bool LuaSyncedCtrl::inDestroyFeature = false;
87 bool LuaSyncedCtrl::inGiveOrder = false;
88 bool LuaSyncedCtrl::inHeightMap = false;
89 bool LuaSyncedCtrl::inSmoothMesh = false;
90 
91 static int heightMapx1;
92 static int heightMapx2;
93 static int heightMapz1;
94 static int heightMapz2;
95 static float heightMapAmountChanged;
96 
97 static float smoothMeshAmountChanged;
98 
99 
100 /******************************************************************************/
101 
CheckAllowGameChanges(lua_State * L)102 inline void LuaSyncedCtrl::CheckAllowGameChanges(lua_State* L)
103 {
104 	if (!CLuaHandle::GetHandleAllowChanges(L)) {
105 		luaL_error(L, "Unsafe attempt to change game state");
106 	}
107 }
108 
109 
110 /******************************************************************************/
111 /******************************************************************************/
112 
PushEntries(lua_State * L)113 bool LuaSyncedCtrl::PushEntries(lua_State* L)
114 {
115 #define REGISTER_LUA_CFUNC(x) \
116 	lua_pushstring(L, #x);      \
117 	lua_pushcfunction(L, x);    \
118 	lua_rawset(L, -3)
119 
120 	REGISTER_LUA_CFUNC(SetAlly);
121 	REGISTER_LUA_CFUNC(KillTeam);
122 	REGISTER_LUA_CFUNC(AssignPlayerToTeam);
123 	REGISTER_LUA_CFUNC(GameOver);
124 
125 	REGISTER_LUA_CFUNC(AddTeamResource);
126 	REGISTER_LUA_CFUNC(UseTeamResource);
127 	REGISTER_LUA_CFUNC(SetTeamResource);
128 	REGISTER_LUA_CFUNC(SetTeamShareLevel);
129 	REGISTER_LUA_CFUNC(ShareTeamResource);
130 
131 	REGISTER_LUA_CFUNC(SetUnitRulesParam);
132 	REGISTER_LUA_CFUNC(SetTeamRulesParam);
133 	REGISTER_LUA_CFUNC(SetGameRulesParam);
134 
135 	REGISTER_LUA_CFUNC(CreateUnit);
136 	REGISTER_LUA_CFUNC(DestroyUnit);
137 	REGISTER_LUA_CFUNC(TransferUnit);
138 
139 	REGISTER_LUA_CFUNC(CreateFeature);
140 	REGISTER_LUA_CFUNC(DestroyFeature);
141 	REGISTER_LUA_CFUNC(TransferFeature);
142 
143 	REGISTER_LUA_CFUNC(SetUnitCosts);
144 	REGISTER_LUA_CFUNC(SetUnitResourcing);
145 	REGISTER_LUA_CFUNC(SetUnitTooltip);
146 	REGISTER_LUA_CFUNC(SetUnitHealth);
147 	REGISTER_LUA_CFUNC(SetUnitMaxHealth);
148 	REGISTER_LUA_CFUNC(SetUnitStockpile);
149 	REGISTER_LUA_CFUNC(SetUnitWeaponState);
150 	REGISTER_LUA_CFUNC(SetUnitExperience);
151 	REGISTER_LUA_CFUNC(SetUnitArmored);
152 	REGISTER_LUA_CFUNC(SetUnitLosMask);
153 	REGISTER_LUA_CFUNC(SetUnitLosState);
154 	REGISTER_LUA_CFUNC(SetUnitCloak);
155 	REGISTER_LUA_CFUNC(SetUnitStealth);
156 	REGISTER_LUA_CFUNC(SetUnitSonarStealth);
157 	REGISTER_LUA_CFUNC(SetUnitAlwaysVisible);
158 	REGISTER_LUA_CFUNC(SetUnitMetalExtraction);
159 	REGISTER_LUA_CFUNC(SetUnitHarvestStorage);
160 	REGISTER_LUA_CFUNC(SetUnitBuildSpeed);
161 	REGISTER_LUA_CFUNC(SetUnitNanoPieces);
162 	REGISTER_LUA_CFUNC(SetUnitBlocking);
163 	REGISTER_LUA_CFUNC(SetUnitCrashing);
164 	REGISTER_LUA_CFUNC(SetUnitShieldState);
165 	REGISTER_LUA_CFUNC(SetUnitFlanking);
166 	REGISTER_LUA_CFUNC(SetUnitTravel);
167 	REGISTER_LUA_CFUNC(SetUnitFuel);
168 	REGISTER_LUA_CFUNC(SetUnitMoveGoal);
169 	REGISTER_LUA_CFUNC(SetUnitNeutral);
170 	REGISTER_LUA_CFUNC(SetUnitTarget);
171 	REGISTER_LUA_CFUNC(SetUnitMidAndAimPos);
172 	REGISTER_LUA_CFUNC(SetUnitRadiusAndHeight);
173 	REGISTER_LUA_CFUNC(SetUnitCollisionVolumeData);
174 	REGISTER_LUA_CFUNC(SetUnitPieceCollisionVolumeData);
175 	REGISTER_LUA_CFUNC(SetUnitSensorRadius);
176 	REGISTER_LUA_CFUNC(SetUnitPosErrorParams);
177 
178 	REGISTER_LUA_CFUNC(SetUnitPhysics);
179 	REGISTER_LUA_CFUNC(SetUnitPosition);
180 	REGISTER_LUA_CFUNC(SetUnitVelocity);
181 	REGISTER_LUA_CFUNC(SetUnitRotation);
182 	REGISTER_LUA_CFUNC(SetUnitDirection);
183 
184 	REGISTER_LUA_CFUNC(AddUnitDamage);
185 	REGISTER_LUA_CFUNC(AddUnitImpulse);
186 	REGISTER_LUA_CFUNC(AddUnitSeismicPing);
187 
188 	REGISTER_LUA_CFUNC(AddUnitResource);
189 	REGISTER_LUA_CFUNC(UseUnitResource);
190 
191 	REGISTER_LUA_CFUNC(RemoveBuildingDecal);
192 	REGISTER_LUA_CFUNC(AddGrass);
193 	REGISTER_LUA_CFUNC(RemoveGrass);
194 
195 	REGISTER_LUA_CFUNC(SetFeatureAlwaysVisible);
196 	REGISTER_LUA_CFUNC(SetFeatureHealth);
197 	REGISTER_LUA_CFUNC(SetFeatureReclaim);
198 	REGISTER_LUA_CFUNC(SetFeatureResurrect);
199 	REGISTER_LUA_CFUNC(SetFeaturePhysics);
200 	REGISTER_LUA_CFUNC(SetFeaturePosition);
201 	REGISTER_LUA_CFUNC(SetFeatureVelocity);
202 	REGISTER_LUA_CFUNC(SetFeatureDirection);
203 	REGISTER_LUA_CFUNC(SetFeatureBlocking);
204 	REGISTER_LUA_CFUNC(SetFeatureNoSelect);
205 	REGISTER_LUA_CFUNC(SetFeatureMidAndAimPos);
206 	REGISTER_LUA_CFUNC(SetFeatureRadiusAndHeight);
207 	REGISTER_LUA_CFUNC(SetFeatureCollisionVolumeData);
208 
209 
210 	REGISTER_LUA_CFUNC(SetProjectileAlwaysVisible);
211 	REGISTER_LUA_CFUNC(SetProjectileMoveControl);
212 	REGISTER_LUA_CFUNC(SetProjectilePosition);
213 	REGISTER_LUA_CFUNC(SetProjectileVelocity);
214 	REGISTER_LUA_CFUNC(SetProjectileCollision);
215 	REGISTER_LUA_CFUNC(SetProjectileTarget);
216 
217 	REGISTER_LUA_CFUNC(SetProjectileGravity);
218 	REGISTER_LUA_CFUNC(SetProjectileSpinAngle);
219 	REGISTER_LUA_CFUNC(SetProjectileSpinSpeed);
220 	REGISTER_LUA_CFUNC(SetProjectileSpinVec);
221 	REGISTER_LUA_CFUNC(SetPieceProjectileParams);
222 
223 	REGISTER_LUA_CFUNC(SetProjectileCEG);
224 
225 
226 	REGISTER_LUA_CFUNC(CallCOBScript);
227 	REGISTER_LUA_CFUNC(GetCOBScriptID);
228 	//FIXME: REGISTER_LUA_CFUNC(GetUnitCOBValue);
229 	//FIXME: REGISTER_LUA_CFUNC(SetUnitCOBValue);
230 
231 	REGISTER_LUA_CFUNC(GiveOrderToUnit);
232 	REGISTER_LUA_CFUNC(GiveOrderToUnitMap);
233 	REGISTER_LUA_CFUNC(GiveOrderToUnitArray);
234 	REGISTER_LUA_CFUNC(GiveOrderArrayToUnitMap);
235 	REGISTER_LUA_CFUNC(GiveOrderArrayToUnitArray);
236 
237 	REGISTER_LUA_CFUNC(LevelHeightMap);
238 	REGISTER_LUA_CFUNC(AdjustHeightMap);
239 	REGISTER_LUA_CFUNC(RevertHeightMap);
240 
241 	REGISTER_LUA_CFUNC(AddHeightMap);
242 	REGISTER_LUA_CFUNC(SetHeightMap);
243 	REGISTER_LUA_CFUNC(SetHeightMapFunc);
244 
245 	REGISTER_LUA_CFUNC(LevelSmoothMesh);
246 	REGISTER_LUA_CFUNC(AdjustSmoothMesh);
247 	REGISTER_LUA_CFUNC(RevertSmoothMesh);
248 
249 	REGISTER_LUA_CFUNC(AddSmoothMesh);
250 	REGISTER_LUA_CFUNC(SetSmoothMesh);
251 	REGISTER_LUA_CFUNC(SetSmoothMeshFunc);
252 
253 	REGISTER_LUA_CFUNC(SetMapSquareTerrainType);
254 	REGISTER_LUA_CFUNC(SetTerrainTypeData);
255 
256 	REGISTER_LUA_CFUNC(UnitWeaponFire);
257 	REGISTER_LUA_CFUNC(UnitWeaponHoldFire);
258 
259 	REGISTER_LUA_CFUNC(SpawnProjectile);
260 	REGISTER_LUA_CFUNC(SpawnCEG);
261 
262 	REGISTER_LUA_CFUNC(EditUnitCmdDesc);
263 	REGISTER_LUA_CFUNC(InsertUnitCmdDesc);
264 	REGISTER_LUA_CFUNC(RemoveUnitCmdDesc);
265 
266 	REGISTER_LUA_CFUNC(SetNoPause);
267 	REGISTER_LUA_CFUNC(SetUnitToFeature);
268 	REGISTER_LUA_CFUNC(SetExperienceGrade);
269 
270 	REGISTER_LUA_CFUNC(SetRadarErrorParams);
271 
272 	if (!LuaSyncedMoveCtrl::PushMoveCtrl(L))
273 		return false;
274 
275 	if (!CLuaUnitScript::PushEntries(L))
276 		return false;
277 
278 	if (!LuaMetalMap::PushCtrlEntries(L))
279 		return false;
280 
281 	return true;
282 }
283 
284 
285 /******************************************************************************/
286 /******************************************************************************/
287 //
288 //  Parsing helpers
289 //
290 
ParseRawUnit(lua_State * L,const char * caller,int index)291 static inline CUnit* ParseRawUnit(lua_State* L, const char* caller, int index)
292 {
293 	if (!lua_isnumber(L, index)) {
294 		luaL_error(L, "%s(): Bad unitID", caller);
295 	}
296 	const int unitID = lua_toint(L, index);
297 	if ((unitID < 0) || (static_cast<size_t>(unitID) >= unitHandler->MaxUnits())) {
298 		luaL_error(L, "%s(): Bad unitID: %d\n", caller, unitID);
299 	}
300 	CUnit* unit = unitHandler->units[unitID];
301 	if (unit == NULL) {
302 		return NULL;
303 	}
304 	return unit;
305 }
306 
307 
ParseUnit(lua_State * L,const char * caller,int index)308 static inline CUnit* ParseUnit(lua_State* L, const char* caller, int index)
309 {
310 	CUnit* unit = ParseRawUnit(L, caller, index);
311 	if (unit == NULL) {
312 		return NULL;
313 	}
314 	if (!CanControlUnit(L, unit)) {
315 		return NULL;
316 	}
317 	return unit;
318 }
319 
320 
ParseFeature(lua_State * L,const char * caller,int index)321 static inline CFeature* ParseFeature(lua_State* L,
322                                      const char* caller, int index)
323 {
324 	if (!lua_isnumber(L, index)) {
325 		luaL_error(L, "Incorrect arguments to %s(featureID)", caller);
326 	}
327 	const int featureID = lua_toint(L, index);
328 	CFeature* f = featureHandler->GetFeature(featureID);
329 
330 	if (!f)
331 		return NULL;
332 
333 	if (!CanControlFeature(L, f)) {
334 		return NULL;
335 	}
336 	return f;
337 }
338 
339 
ParseProjectile(lua_State * L,const char * caller,int index)340 static inline CProjectile* ParseProjectile(lua_State* L,
341                                            const char* caller, int index)
342 {
343 	if (!lua_isnumber(L, index)) {
344 		luaL_error(L, "%s(): Bad projectile ID", caller);
345 	}
346 	const ProjectileMapValPair* pmp = projectileHandler->GetMapPairBySyncedID(lua_toint(L, index));
347 	if (pmp == NULL) {
348 		return NULL;
349 	}
350 	if (!CanControlProjectileAllyTeam(L, pmp->second)) {
351 		return NULL;
352 	}
353 	return pmp->first;
354 }
355 
ParseProjectileParams(lua_State * L,ProjectileParams & params,const int tblIdx,const char * caller)356 static bool ParseProjectileParams(lua_State* L, ProjectileParams& params, const int tblIdx, const char* caller)
357 {
358 	if (!lua_istable(L, tblIdx)) {
359 		luaL_error(L, "[%s] argument %d must be a table!", caller, tblIdx);
360 		return false;
361 	}
362 
363 	for (lua_pushnil(L); lua_next(L, tblIdx) != 0; lua_pop(L, 1)) {
364 		if (lua_israwstring(L, -2)) {
365 			const std::string& key = lua_tostring(L, -2);
366 
367 			if (lua_istable(L, -1)) {
368 				float array[3] = {0.0f, 0.0f, 0.0f};
369 
370 				if (LuaUtils::ParseFloatArray(L, -1, array, 3) == 3) {
371 				    if (key == "pos") {
372 						params.pos = array;
373 					} else if (key == "end") {
374 						params.end = array;
375 					} else if (key == "speed") {
376 						params.speed = array;
377 					} else if (key == "spread") {
378 						params.spread = array;
379 					} else if (key == "error") {
380 						params.error = array;
381 					}
382 				}
383 
384 				continue;
385 			}
386 
387 			if (lua_isnumber(L, -1)) {
388 				if (key == "owner") {
389 					params.ownerID = lua_toint(L, -1);
390 				} else if (key == "team") {
391 					params.teamID = lua_toint(L, -1);
392 				} else if (key == "ttl") {
393 					params.ttl = lua_tofloat(L, -1);
394 				} else if (key == "gravity") {
395 					params.gravity = lua_tofloat(L, -1);
396 				} else if (key == "tracking") {
397 					params.tracking = lua_tofloat(L, -1);
398 				} else if (key == "maxRange") {
399 					params.maxRange = lua_tofloat(L, -1);
400 				} else if (key == "startAlpha") {
401 					params.startAlpha = lua_tofloat(L, -1);
402 				} else if (key == "endAlpha") {
403 					params.endAlpha = lua_tofloat(L, -1);
404 				}
405 
406 				continue;
407 			}
408 
409 			if (lua_isstring(L, -1)) {
410 				if (key == "model") {
411 					params.model = modelParser->Load3DModel(lua_tostring(L, -1));
412 				} else if (key == "cegtag") {
413 					params.cegID = explGenHandler->LoadGeneratorID(lua_tostring(L, -1));
414 				}
415 
416 				continue;
417 			}
418 		}
419 	}
420 
421 	return true;
422 }
423 
ParseTeam(lua_State * L,const char * caller,int index)424 static CTeam* ParseTeam(lua_State* L, const char* caller, int index)
425 {
426 	if (!lua_isnumber(L, index)) {
427 		luaL_error(L, "%s(): Bad teamID", caller);
428 		return NULL;
429 	}
430 	const int teamID = lua_toint(L, index);
431 	if (!teamHandler->IsValidTeam(teamID)) {
432 		luaL_error(L, "%s(): Bad teamID: %d", caller, teamID);
433 	}
434 	CTeam* team = teamHandler->Team(teamID);
435 	if (team == NULL) {
436 		return NULL;
437 	}
438 	return team;
439 }
440 
SetSolidObjectCollisionVolumeData(lua_State * L,CSolidObject * o)441 static int SetSolidObjectCollisionVolumeData(lua_State* L, CSolidObject* o)
442 {
443 	if (o == NULL)
444 		return 0;
445 
446 	const float xs = luaL_checkfloat(L, 2);
447 	const float ys = luaL_checkfloat(L, 3);
448 	const float zs = luaL_checkfloat(L, 4);
449 	const float xo = luaL_checkfloat(L, 5);
450 	const float yo = luaL_checkfloat(L, 6);
451 	const float zo = luaL_checkfloat(L, 7);
452 	const int vType = luaL_checkint(L,  8);
453 	const int tType = luaL_checkint(L,  9);
454 	const int pAxis = luaL_checkint(L, 10);
455 
456 	const float3 scales(xs, ys, zs);
457 	const float3 offsets(xo, yo, zo);
458 
459 	o->collisionVolume->InitShape(scales, offsets, vType, tType, pAxis);
460 	return 0;
461 }
462 
SetSolidObjectBlocking(lua_State * L,CSolidObject * o)463 static int SetSolidObjectBlocking(lua_State* L, CSolidObject* o)
464 {
465 	if (o == NULL)
466 		return 0;
467 
468 	// update SO-bit of collidable state
469 	if (lua_isboolean(L, 3)) {
470 		if (lua_toboolean(L, 3)) {
471 			o->SetCollidableStateBit(CSolidObject::CSTATE_BIT_SOLIDOBJECTS);
472 		} else {
473 			o->ClearCollidableStateBit(CSolidObject::CSTATE_BIT_SOLIDOBJECTS);
474 		}
475 	}
476 
477 	// update blocking-bit of physical state (do this
478 	// after changing the SO-bit so it is reversable)
479 	if (lua_isboolean(L, 2)) {
480 		if (lua_toboolean(L, 2)) {
481 			o->Block();
482 		} else {
483 			o->UnBlock();
484 		}
485 	}
486 
487 	o->UpdateCollidableStateBit(CSolidObject::CSTATE_BIT_PROJECTILES, luaL_optboolean(L, 4, o->HasCollidableStateBit(CSolidObject::CSTATE_BIT_PROJECTILES)));
488 	o->UpdateCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS, luaL_optboolean(L, 5, o->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)));
489 
490 	o->crushable = luaL_optboolean(L, 6, o->crushable);
491 	o->blockEnemyPushing = luaL_optboolean(L, 7, o->blockEnemyPushing);
492 	o->blockHeightChanges = luaL_optboolean(L, 8, o->blockHeightChanges);
493 
494 	lua_pushboolean(L, o->IsBlocking());
495 	return 1;
496 }
497 
SetSolidObjectDirection(lua_State * L,CSolidObject * o)498 static int SetSolidObjectDirection(lua_State* L, CSolidObject* o)
499 {
500 	if (o == NULL)
501 		return 0;
502 
503 	o->ForcedSpin((float3(luaL_checkfloat(L, 2), luaL_checkfloat(L, 3), luaL_checkfloat(L, 4))).SafeNormalize());
504 	return 0;
505 }
506 
SetWorldObjectVelocity(lua_State * L,CWorldObject * o)507 static int SetWorldObjectVelocity(lua_State* L, CWorldObject* o)
508 {
509 	if (o == NULL)
510 		return 0;
511 
512 	float3 speed;
513 	speed.x = Clamp(luaL_checkfloat(L, 2), -MAX_UNIT_SPEED, MAX_UNIT_SPEED);
514 	speed.y = Clamp(luaL_checkfloat(L, 3), -MAX_UNIT_SPEED, MAX_UNIT_SPEED);
515 	speed.z = Clamp(luaL_checkfloat(L, 4), -MAX_UNIT_SPEED, MAX_UNIT_SPEED);
516 
517 	o->SetVelocityAndSpeed(speed);
518 	return 0;
519 }
520 
SetSolidObjectPhysicalState(lua_State * L,CSolidObject * o)521 static int SetSolidObjectPhysicalState(lua_State* L, CSolidObject* o)
522 {
523 	if (o == NULL)
524 		return 0;
525 
526 	float3 pos;
527 	float3 rot;
528 	float3 speed;
529 	float3& drag = o->dragScales;
530 
531 	pos.x = luaL_checknumber(L, 2);
532 	pos.y = luaL_checknumber(L, 3);
533 	pos.z = luaL_checknumber(L, 4);
534 
535 	speed.x = luaL_checknumber(L, 5);
536 	speed.y = luaL_checknumber(L, 6);
537 	speed.z = luaL_checknumber(L, 7);
538 
539 	rot.x = luaL_checknumber(L, 8);
540 	rot.y = luaL_checknumber(L, 9);
541 	rot.z = luaL_checknumber(L, 10);
542 
543 	drag.x = Clamp(luaL_optnumber(L, 11, drag.x), 0.0f, 1.0f);
544 	drag.y = Clamp(luaL_optnumber(L, 12, drag.y), 0.0f, 1.0f);
545 	drag.z = Clamp(luaL_optnumber(L, 13, drag.z), 0.0f, 1.0f);
546 
547 	CMatrix44f matrix;
548 	matrix.RotateZ(rot.z);
549 	matrix.RotateX(rot.x);
550 	matrix.RotateY(rot.y);
551 
552 	o->Move(pos, false);
553 	o->SetDirVectors(matrix);
554 	o->UpdateMidAndAimPos();
555 	o->SetHeadingFromDirection();
556 	// do not need ForcedSpin, above three calls cover it
557 	o->ForcedMove(pos);
558 	o->SetVelocityAndSpeed(speed);
559 	return 0;
560 }
561 
SetWorldObjectAlwaysVisible(lua_State * L,CWorldObject * o,const char * caller)562 static int SetWorldObjectAlwaysVisible(lua_State* L, CWorldObject* o, const char* caller)
563 {
564 	if (o == NULL)
565 		return 0;
566 	o->alwaysVisible = luaL_checkboolean(L, 2);
567 	return 0;
568 }
569 
570 /******************************************************************************/
571 /******************************************************************************/
572 //
573 // The call-outs
574 //
575 
SetAlly(lua_State * L)576 int LuaSyncedCtrl::SetAlly(lua_State* L)
577 {
578 	const int firstAllyTeamID = luaL_checkint(L, 1);
579 	const int secondAllyTeamID = luaL_checkint(L, 2);
580 
581 	if (!teamHandler->IsValidAllyTeam(firstAllyTeamID))
582 		return 0;
583 	if (!teamHandler->IsValidAllyTeam(secondAllyTeamID))
584 		return 0;
585 
586 	teamHandler->SetAlly(firstAllyTeamID, secondAllyTeamID, luaL_checkboolean(L, 3));
587 	return 0;
588 }
589 
KillTeam(lua_State * L)590 int LuaSyncedCtrl::KillTeam(lua_State* L)
591 {
592 	const int teamID = luaL_checkint(L, 1);
593 
594 	if (!teamHandler->IsValidTeam(teamID))
595 		return 0;
596 
597 	//FIXME either we disallow it here or it needs modifications in GameServer.cpp (it creates a `teams` vector w/o gaia)
598 	//  possible fix would be to always create the Gaia team (currently it's conditional on gs->useLuaGaia)
599 	if (teamID == teamHandler->GaiaTeamID())
600 		return 0;
601 
602 	CTeam* team = teamHandler->Team(teamID);
603 
604 	if (team == NULL)
605 		return 0;
606 
607 	team->Died();
608 	return 0;
609 }
610 
AssignPlayerToTeam(lua_State * L)611 int LuaSyncedCtrl::AssignPlayerToTeam(lua_State* L)
612 {
613 	const int playerID = luaL_checkint(L, 1);
614 	const int teamID = luaL_checkint(L, 2);
615 
616 	if (!playerHandler->IsValidPlayer(playerID))
617 		return 0;
618 	if (!teamHandler->IsValidTeam(teamID))
619 		return 0;
620 
621 	teamHandler->Team(teamID)->AddPlayer(playerID);
622 	return 0;
623 }
624 
625 
GameOver(lua_State * L)626 int LuaSyncedCtrl::GameOver(lua_State* L)
627 {
628 	if (!lua_istable(L, 1)) {
629 		luaL_error(L, "Incorrect arguments to GameOver()");
630 		return 0;
631 	}
632 
633 	std::vector<unsigned char> winningAllyTeams;
634 
635 	static const int tableIdx = 1;
636 
637 	for (lua_pushnil(L); lua_next(L, tableIdx) != 0; lua_pop(L, 1)) {
638 		if (!lua_israwnumber(L, -1)) {
639 			continue;
640 		}
641 
642 		const unsigned char allyTeamID = lua_toint(L, -1);
643 
644 		if (!teamHandler->ValidAllyTeam(allyTeamID)) {
645 			continue;
646 		}
647 
648 		winningAllyTeams.push_back(allyTeamID);
649 	}
650 
651 	game->GameEnd(winningAllyTeams);
652 	// push the number of accepted allyteams
653 	lua_pushnumber(L, winningAllyTeams.size());
654 	return 1;
655 }
656 
657 
AddTeamResource(lua_State * L)658 int LuaSyncedCtrl::AddTeamResource(lua_State* L)
659 {
660 	const int teamID = luaL_checkint(L, 1);
661 	if (!teamHandler->IsValidTeam(teamID)) {
662 		return 0;
663 	}
664 	if (!CanControlTeam(L, teamID)) {
665 		return 0;
666 	}
667 	CTeam* team = teamHandler->Team(teamID);
668 	if (team == NULL) {
669 		return 0;
670 	}
671 
672 	const string type = luaL_checkstring(L, 2);
673 
674 	const float value = max(0.0f, luaL_checkfloat(L, 3));
675 
676 	if ((type == "m") || (type == "metal")) {
677 		team->AddMetal(value);
678 	}
679 	else if ((type == "e") || (type == "energy")) {
680 		team->AddEnergy(value);
681 	}
682 	return 0;
683 }
684 
685 
UseTeamResource(lua_State * L)686 int LuaSyncedCtrl::UseTeamResource(lua_State* L)
687 {
688 	const int teamID = luaL_checkint(L, 1);
689 	if (!teamHandler->IsValidTeam(teamID)) {
690 		return 0;
691 	}
692 	if (!CanControlTeam(L, teamID)) {
693 		return 0;
694 	}
695 	CTeam* team = teamHandler->Team(teamID);
696 	if (team == NULL) {
697 		return 0;
698 	}
699 
700 	if (lua_isstring(L, 2)) {
701 		const string type = lua_tostring(L, 2);
702 
703 		const float value = max(0.0f, luaL_checkfloat(L, 3));
704 
705 		if ((type == "m") || (type == "metal")) {
706 			team->metalPull += value;
707 			lua_pushboolean(L, team->UseMetal(value));
708 			return 1;
709 		}
710 		else if ((type == "e") || (type == "energy")) {
711 			team->energyPull += value;
712 			lua_pushboolean(L, team->UseEnergy(value));
713 			return 1;
714 		}
715 	}
716 	else if (lua_istable(L, 2)) {
717 		float metal  = 0.0f;
718 		float energy = 0.0f;
719 		const int table = 2;
720 		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
721 			if (lua_israwstring(L, -2) && lua_isnumber(L, -1)) {
722 				const string key = lua_tostring(L, -2);
723 				const float value = max(0.0f, lua_tofloat(L, -1));
724 				if ((key == "m") || (key == "metal")) {
725 					metal = value;
726 				} else if ((key == "e") || (key == "energy")) {
727 					energy = value;
728 				}
729 			}
730 		}
731 		team->metalPull  += metal;
732 		team->energyPull += energy;
733 		if ((team->metal >= metal) && (team->energy >= energy)) {
734 			team->UseMetal(metal);
735 			team->UseEnergy(energy);
736 			lua_pushboolean(L, true);
737 		} else {
738 			lua_pushboolean(L, false);
739 		}
740 		return 1;
741 	}
742 	else {
743 		luaL_error(L, "bad arguments");
744 	}
745 	return 0;
746 }
747 
748 
SetTeamResource(lua_State * L)749 int LuaSyncedCtrl::SetTeamResource(lua_State* L)
750 {
751 	const int teamID = luaL_checkint(L, 1);
752 	if (!teamHandler->IsValidTeam(teamID)) {
753 		return 0;
754 	}
755 	if (!CanControlTeam(L, teamID)) {
756 		return 0;
757 	}
758 	CTeam* team = teamHandler->Team(teamID);
759 	if (team == NULL) {
760 		return 0;
761 	}
762 
763 	const string type = luaL_checkstring(L, 2);
764 
765 	const float value = max(0.0f, luaL_checkfloat(L, 3));
766 
767 	if ((type == "m") || (type == "metal")) {
768 		team->metal = min<float>(team->metalStorage, value);
769 	}
770 	else if ((type == "e") || (type == "energy")) {
771 		team->energy = min<float>(team->energyStorage, value);
772 	}
773 	else if ((type == "ms") || (type == "metalStorage")) {
774 		team->metalStorage = value;
775 		team->metal = min<float>(team->metal, team->metalStorage);
776 	}
777 	else if ((type == "es") || (type == "energyStorage")) {
778 		team->energyStorage = value;
779 		team->energy = min<float>(team->energy, team->energyStorage);
780 	}
781 	return 0;
782 }
783 
784 
SetTeamShareLevel(lua_State * L)785 int LuaSyncedCtrl::SetTeamShareLevel(lua_State* L)
786 {
787 	const int teamID = luaL_checkint(L, 1);
788 	if (!teamHandler->IsValidTeam(teamID)) {
789 		return 0;
790 	}
791 	if (!CanControlTeam(L, teamID)) {
792 		return 0;
793 	}
794 	CTeam* team = teamHandler->Team(teamID);
795 	if (team == NULL) {
796 		return 0;
797 	}
798 
799 	const string type = luaL_checkstring(L, 2);
800 
801 	const float value = luaL_checkfloat(L, 3);
802 
803 	if ((type == "m") || (type == "metal")) {
804 		team->metalShare = max(0.0f, min(1.0f, value));
805 	}
806 	else if ((type == "e") || (type == "energy")) {
807 		team->energyShare = max(0.0f, min(1.0f, value));
808 	}
809 	return 0;
810 }
811 
812 
ShareTeamResource(lua_State * L)813 int LuaSyncedCtrl::ShareTeamResource(lua_State* L)
814 {
815 	const int teamID1 = luaL_checkint(L, 1);
816 	if (!teamHandler->IsValidTeam(teamID1)) {
817 		luaL_error(L, "Incorrect arguments to ShareTeamResource(teamID1, teamID2, type, amount)");
818 	}
819 	if (!CanControlTeam(L, teamID1)) {
820 		return 0;
821 	}
822 	CTeam* team1 = teamHandler->Team(teamID1);
823 	if (team1 == NULL) {
824 		return 0;
825 	}
826 
827 	const int teamID2 = luaL_checkint(L, 2);
828 	if (!teamHandler->IsValidTeam(teamID2)) {
829 		luaL_error(L, "Incorrect arguments to ShareTeamResource(teamID1, teamID2, type, amount)");
830 	}
831 	CTeam* team2 = teamHandler->Team(teamID2);
832 	if (team2 == NULL) {
833 		return 0;
834 	}
835 
836 	const string type = luaL_checkstring(L, 3);
837 	float amount = luaL_checkfloat(L, 4);
838 
839 	if (type == "metal") {
840 		amount = std::min(amount, (float)team1->metal);
841 		if (eventHandler.AllowResourceTransfer(teamID1, teamID2, "m", amount)) { //FIXME can cause an endless loop
842 			team1->metal                       -= amount;
843 			team1->metalSent                   += amount;
844 			team1->currentStats->metalSent     += amount;
845 			team2->metal                       += amount;
846 			team2->metalReceived               += amount;
847 			team2->currentStats->metalReceived += amount;
848 		}
849 	} else if (type == "energy") {
850 		amount = std::min(amount, (float)team1->energy);
851 		if (eventHandler.AllowResourceTransfer(teamID1, teamID2, "e", amount)) { //FIXME can cause an endless loop
852 			team1->energy                       -= amount;
853 			team1->energySent                   += amount;
854 			team1->currentStats->energySent     += amount;
855 			team2->energy                       += amount;
856 			team2->energyReceived               += amount;
857 			team2->currentStats->energyReceived += amount;
858 		}
859 	}
860 	return 0;
861 }
862 
863 
864 
865 /******************************************************************************/
866 
SetRulesParam(lua_State * L,const char * caller,int offset,LuaRulesParams::Params & params,LuaRulesParams::HashMap & paramsMap)867 void SetRulesParam(lua_State* L, const char* caller, int offset,
868 				LuaRulesParams::Params& params,
869 				LuaRulesParams::HashMap& paramsMap)
870 {
871 	const int index = offset + 1;
872 	const int valIndex = offset + 2;
873 	const int losIndex = offset + 3;
874 	int pIndex = -1;
875 
876 	if (lua_israwnumber(L, index)) {
877 		pIndex = lua_toint(L, index) - 1;
878 	}
879 	else if (lua_israwstring(L, index)) {
880 		const string pName = lua_tostring(L, index);
881 		map<string, int>::const_iterator it = paramsMap.find(pName);
882 		if (it != paramsMap.end()) {
883 			pIndex = it->second;
884 		}
885 		else {
886 			// create a new parameter
887 			pIndex = params.size();
888 			paramsMap[pName] = pIndex;
889 			params.push_back(LuaRulesParams::Param());
890 		}
891 	}
892 	else {
893 		luaL_error(L, "Incorrect arguments to %s()", caller);
894 	}
895 
896 	if ((pIndex < 0)
897 		|| (pIndex >= (int)params.size())
898 		|| !(lua_isnumber(L, valIndex) || lua_isstring(L, valIndex))
899 	) {
900 		luaL_error(L, "Incorrect arguments to %s()", caller);
901 	}
902 
903 	LuaRulesParams::Param& param = params[pIndex];
904 
905 	//! set the value of the parameter
906 	if (lua_isnumber(L, valIndex)) {
907 		param.valueInt = lua_tofloat(L, valIndex);
908 		param.valueString.resize(0);
909 	} else {
910 		param.valueString = lua_tostring(L, valIndex);
911 	}
912 
913 	//! set the los checking of the parameter
914 	if (lua_istable(L, losIndex)) {
915 		const int table = losIndex;
916 		int losMask = LuaRulesParams::RULESPARAMLOS_PRIVATE;
917 
918 		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
919 			//! ignore if the value is false
920 			if (!luaL_optboolean(L, -1, true)) {
921 				continue;
922 			}
923 
924 			//! read the losType from the key
925 			if (lua_isstring(L, -2)) {
926 				const string losType = lua_tostring(L, -2);
927 
928 				if (losType == "public") {
929 					losMask |= LuaRulesParams::RULESPARAMLOS_PUBLIC;
930 				}
931 				else if (losType == "inlos") {
932 					losMask |= LuaRulesParams::RULESPARAMLOS_INLOS;
933 				}
934 				else if (losType == "inradar") {
935 					losMask |= LuaRulesParams::RULESPARAMLOS_INRADAR;
936 				}
937 				else if (losType == "allied") {
938 					losMask |= LuaRulesParams::RULESPARAMLOS_ALLIED;
939 				}
940 				/*else if (losType == "private") {
941 					losMask |= LuaRulesParams::RULESPARAMLOS_PRIVATE; //! default
942 				}*/
943 			}
944 		}
945 
946 		param.los = losMask;
947 	} else {
948 		param.los = luaL_optint(L, losIndex, param.los);
949 	}
950 
951 	return;
952 }
953 
954 
SetGameRulesParam(lua_State * L)955 int LuaSyncedCtrl::SetGameRulesParam(lua_State* L)
956 {
957 	SetRulesParam(L, __FUNCTION__, 0, CLuaHandleSynced::gameParams, CLuaHandleSynced::gameParamsMap);
958 	return 0;
959 }
960 
961 
SetTeamRulesParam(lua_State * L)962 int LuaSyncedCtrl::SetTeamRulesParam(lua_State* L)
963 {
964 	CTeam* team = ParseTeam(L, __FUNCTION__, 1);
965 	if (team == NULL) {
966 		return 0;
967 	}
968 	SetRulesParam(L, __FUNCTION__, 1, team->modParams, team->modParamsMap);
969 	return 0;
970 }
971 
972 
SetUnitRulesParam(lua_State * L)973 int LuaSyncedCtrl::SetUnitRulesParam(lua_State* L)
974 {
975 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
976 	if (unit == NULL) {
977 		return 0;
978 	}
979 	SetRulesParam(L, __FUNCTION__, 1, unit->modParams, unit->modParamsMap);
980 	return 0;
981 }
982 
983 
984 
985 /******************************************************************************/
986 /******************************************************************************/
987 
ParseCobArgs(lua_State * L,int first,int last,vector<int> & args)988 static inline void ParseCobArgs(lua_State* L,
989                                 int first, int last, vector<int>& args)
990 {
991 	for (int a = first; a <= last; a++) {
992 		if (lua_isnumber(L, a)) {
993 			args.push_back(lua_toint(L, a));
994 		}
995 		else if (lua_istable(L, a)) {
996 			lua_rawgeti(L, a, 1);
997 			lua_rawgeti(L, a, 2);
998 			if (lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
999 				const int x = lua_toint(L, -2);
1000 				const int z = lua_toint(L, -1);
1001 				args.push_back(PACKXZ(x, z));
1002 			} else {
1003 				args.push_back(0);
1004 			}
1005 			lua_pop(L, 2);
1006 		}
1007 		else if (lua_isboolean(L, a)) {
1008 			args.push_back(lua_toboolean(L, a) ? 1 : 0);
1009 		}
1010 		else {
1011 			args.push_back(0);
1012 		}
1013 	}
1014 }
1015 
1016 
CallCOBScript(lua_State * L)1017 int LuaSyncedCtrl::CallCOBScript(lua_State* L)
1018 {
1019 //FIXME?	CheckAllowGameChanges(L);
1020 	const int args = lua_gettop(L); // number of arguments
1021 	if ((args < 3) ||
1022 	    !lua_isnumber(L, 1) || // unitID
1023 	    !lua_isnumber(L, 3)) { // number of returned parameters
1024 		luaL_error(L, "Incorrect arguments to CallCOBScript()");
1025 	}
1026 
1027 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1028 	if (unit == NULL) {
1029 		return 0;
1030 	}
1031 	CCobInstance* cob = dynamic_cast<CCobInstance*>(unit->script);
1032 	if (cob == NULL) {
1033 		luaL_error(L, "CallCOBScript(): unit is not running a COB script");
1034 	}
1035 
1036 	// collect the arguments
1037 	vector<int> cobArgs;
1038 	ParseCobArgs(L, 4, args, cobArgs);
1039 	const int retParams = min(lua_toint(L, 3),
1040 	                          min(MAX_LUA_COB_ARGS, (int)cobArgs.size()));
1041 
1042 	int retval;
1043 	if (lua_israwnumber(L, 2)) {
1044 		const int funcId = lua_toint(L, 2);
1045 		retval = cob->RawCall(funcId, cobArgs);
1046 	}
1047 	else if (lua_israwstring(L, 2)) {
1048 		const string funcName = lua_tostring(L, 2);
1049 		retval = cob->Call(funcName, cobArgs);
1050 	}
1051 	else {
1052 		luaL_error(L, "Incorrect arguments to CallCOBScript()");
1053 		retval = 0;
1054 	}
1055 
1056 	lua_settop(L, 0); // FIXME - ok?
1057 	lua_pushnumber(L, retval);
1058 	for (int i = 0; i < retParams; i++) {
1059 		lua_pushnumber(L, cobArgs[i]);
1060 	}
1061 	return 1 + retParams;
1062 }
1063 
1064 
GetCOBScriptID(lua_State * L)1065 int LuaSyncedCtrl::GetCOBScriptID(lua_State* L)
1066 {
1067 	const int args = lua_gettop(L); // number of arguments
1068 	if ((args < 2) || !lua_isnumber(L, 1) || !lua_isstring(L, 2)) {
1069 		luaL_error(L, "Incorrect arguments to GetCOBScriptID()");
1070 	}
1071 
1072 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1073 	if (unit == NULL) {
1074 		return 0;
1075 	}
1076 	CCobInstance* cob = dynamic_cast<CCobInstance*>(unit->script);
1077 	if (cob == NULL) {
1078 		// no error - allows using this to determine whether unit runs COB or LUS
1079 		return 0;
1080 	}
1081 
1082 	const string funcName = lua_tostring(L, 2);
1083 
1084 	const int funcID = cob->GetFunctionId(funcName);
1085 	if (funcID >= 0) {
1086 		lua_pushnumber(L, funcID);
1087 		return 1;
1088 	}
1089 
1090 	return 0;
1091 }
1092 
1093 /******************************************************************************/
1094 /******************************************************************************/
1095 
CreateUnit(lua_State * L)1096 int LuaSyncedCtrl::CreateUnit(lua_State* L)
1097 {
1098 	CheckAllowGameChanges(L);
1099 
1100 	if (inCreateUnit) {
1101 		luaL_error(L, "[%s()]: recursion is not permitted", __FUNCTION__);
1102 		return 0;
1103 	}
1104 
1105 	const UnitDef* unitDef = NULL;
1106 
1107 	if (lua_israwstring(L, 1)) {
1108 		unitDef = unitDefHandler->GetUnitDefByName(lua_tostring(L, 1));
1109 	} else if (lua_israwnumber(L, 1)) {
1110 		unitDef = unitDefHandler->GetUnitDefByID(lua_toint(L, 1));
1111 	} else {
1112 		luaL_error(L, "[%s()] incorrect type for first argument", __FUNCTION__);
1113 		return 0;
1114 	}
1115 
1116 	if (unitDef == NULL) {
1117 		if (lua_israwstring(L, 1)) {
1118 			luaL_error(L, "[%s()]: bad unitDef name: %s", __FUNCTION__, lua_tostring(L, 1));
1119 		} else {
1120 			luaL_error(L, "[%s()]: bad unitDef ID: %d", __FUNCTION__, lua_toint(L, 1));
1121 		}
1122 		return 0;
1123 	}
1124 
1125 	// CUnit::PreInit will clamp the position
1126 	// TODO: also allow off-map unit creation?
1127 	const float3 pos(
1128 		luaL_checkfloat(L, 2),
1129 		luaL_checkfloat(L, 3),
1130 		luaL_checkfloat(L, 4)
1131 	);
1132 	const int facing = LuaUtils::ParseFacing(L, __FUNCTION__, 5);
1133 	const bool beingBuilt = luaL_optboolean(L, 7, false);
1134 	const bool flattenGround = luaL_optboolean(L, 8, true);
1135 	int teamID = luaL_optint(L, 6, CtrlTeam(L));
1136 
1137 	if (!teamHandler->IsValidTeam(teamID)) {
1138 		luaL_error(L, "[%s()]: invalid team number (%d)", __FUNCTION__, teamID);
1139 		return 0;
1140 	}
1141 	if (!FullCtrl(L) && (CtrlTeam(L) != teamID)) {
1142 		luaL_error(L, "[%s()]: not a controllable team (%d)", __FUNCTION__, teamID);
1143 		return 0;
1144 	}
1145 	if (!unitHandler->CanBuildUnit(unitDef, teamID)) {
1146 		return 0; // unit limit reached
1147 	}
1148 
1149 	ASSERT_SYNCED(pos);
1150 	ASSERT_SYNCED(facing);
1151 
1152 	inCreateUnit = true;
1153 	UnitLoadParams params;
1154 	params.unitDef = unitDef; /// must be non-NULL
1155 	params.builder = unitHandler->GetUnit(luaL_optint(L, 10, -1)); /// may be NULL
1156 	params.pos     = pos;
1157 	params.speed   = ZeroVector;
1158 	params.unitID  = luaL_optint(L, 9, -1);
1159 	params.teamID  = teamID;
1160 	params.facing  = facing;
1161 	params.beingBuilt = beingBuilt;
1162 	params.flattenGround = flattenGround;
1163 
1164 	CUnit* unit = unitLoader->LoadUnit(params);
1165 	inCreateUnit = false;
1166 
1167 	if (unit != NULL) {
1168 		lua_pushnumber(L, unit->id);
1169 		return 1;
1170 	}
1171 
1172 	return 0;
1173 }
1174 
1175 
DestroyUnit(lua_State * L)1176 int LuaSyncedCtrl::DestroyUnit(lua_State* L)
1177 {
1178 	CheckAllowGameChanges(L); // FIXME -- recursion protection
1179 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1180 	if (unit == NULL) {
1181 		return 0;
1182 	}
1183 	const int args = lua_gettop(L); // number of arguments
1184 
1185 	bool selfd = luaL_optboolean(L, 2, false);
1186 	bool reclaimed = luaL_optboolean(L, 3, false);
1187 
1188 	CUnit* attacker = NULL;
1189 	if (args >= 4) {
1190 		attacker = ParseUnit(L, __FUNCTION__, 4);
1191 	}
1192 
1193 	if (inDestroyUnit) {
1194 		luaL_error(L, "DestroyUnit() recursion is not permitted");
1195 	}
1196 	inDestroyUnit = true;
1197 	ASSERT_SYNCED(unit->id);
1198 	unit->KillUnit(attacker, selfd, reclaimed);
1199 	inDestroyUnit = false;
1200 
1201 	return 0;
1202 }
1203 
1204 
TransferUnit(lua_State * L)1205 int LuaSyncedCtrl::TransferUnit(lua_State* L)
1206 {
1207 	CheckAllowGameChanges(L);
1208 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1209 	if (unit == NULL) {
1210 		return 0;
1211 	}
1212 
1213 	const int newTeam = luaL_checkint(L, 2);
1214 	if (!teamHandler->IsValidTeam(newTeam)) {
1215 		return 0;
1216 	}
1217 	const CTeam* team = teamHandler->Team(newTeam);
1218 	if (team == NULL) {
1219 		return 0;
1220 	}
1221 
1222 	bool given = true;
1223 	if (FullCtrl(L) && lua_isboolean(L, 3)) {
1224 		given = lua_toboolean(L, 3);
1225 	}
1226 
1227 	if (inTransferUnit) {
1228 		luaL_error(L, "TransferUnit() recursion is not permitted");
1229 	}
1230 	inTransferUnit = true;
1231 	ASSERT_SYNCED(unit->id);
1232 	ASSERT_SYNCED((int)newTeam);
1233 	ASSERT_SYNCED(given);
1234 	unit->ChangeTeam(newTeam, given ? CUnit::ChangeGiven
1235 	                                : CUnit::ChangeCaptured);
1236 	inTransferUnit = false;
1237 
1238 	return 0;
1239 }
1240 
1241 
1242 /******************************************************************************/
1243 
SetUnitCosts(lua_State * L)1244 int LuaSyncedCtrl::SetUnitCosts(lua_State* L)
1245 {
1246 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1247 	if (unit == NULL) {
1248 		return 0;
1249 	}
1250 	if (!lua_istable(L, 2)) {
1251 		luaL_error(L, "Incorrect arguments to SetUnitCosts");
1252 	}
1253 	const int table = 2;
1254 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
1255 		if (!lua_israwstring(L, -2) || !lua_isnumber(L, -1)) {
1256 			continue;
1257 		}
1258 		const string key = lua_tostring(L, -2);
1259 		const float value = lua_tofloat(L, -1);
1260 		ASSERT_SYNCED((float)value);
1261 
1262 		if (key == "buildTime") {
1263 			unit->buildTime  = max(1.0f, value);
1264 		} else if (key == "metalCost") {
1265 			unit->metalCost  = max(1.0f, value);
1266 		} else if (key == "energyCost") {
1267 			unit->energyCost = max(1.0f, value);
1268 		}
1269 	}
1270 	return 0;
1271 }
1272 
1273 
SetUnitResourceParam(CUnit * unit,const string & name,float value)1274 static bool SetUnitResourceParam(CUnit* unit, const string& name, float value)
1275 {
1276 	if (name.size() != 3) {
1277 		return false;
1278 	}
1279 	// [u|c][u|m][m|e]
1280 	//
1281 	// unconditional | conditional
1282 	//           use | make
1283 	//         metal | energy
1284 
1285 	value *= 0.5f;
1286 
1287 	if (name[0] == 'u') {
1288 		if (name[1] == 'u') {
1289 					 if (name[2] == 'm') { unit->uncondUseMetal = value;  return true; }
1290 			else if (name[2] == 'e') { unit->uncondUseEnergy = value; return true; }
1291 		}
1292 		else if (name[1] == 'm') {
1293 					 if (name[2] == 'm') { unit->uncondMakeMetal = value;  return true; }
1294 			else if (name[2] == 'e') { unit->uncondMakeEnergy = value; return true; }
1295 		}
1296 	}
1297 	else if (name[0] == 'c') {
1298 		if (name[1] == 'u') {
1299 					 if (name[2] == 'm') { unit->condUseMetal = value;  return true; }
1300 			else if (name[2] == 'e') { unit->condUseEnergy = value; return true; }
1301 		}
1302 		else if (name[1] == 'm') {
1303 					 if (name[2] == 'm') { unit->condMakeMetal = value;  return true; }
1304 			else if (name[2] == 'e') { unit->condMakeEnergy = value; return true; }
1305 		}
1306 	}
1307 	return false;
1308 }
1309 
1310 
SetUnitResourcing(lua_State * L)1311 int LuaSyncedCtrl::SetUnitResourcing(lua_State* L)
1312 {
1313 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1314 	if (unit == NULL) {
1315 		return 0;
1316 	}
1317 
1318 	if (lua_israwstring(L, 2)) {
1319 		const string key = luaL_checkstring(L, 2);
1320 		const float value = luaL_checkfloat(L, 3);
1321 		SetUnitResourceParam(unit, key, value);
1322 	}
1323 	else if (lua_istable(L, 2)) {
1324 		const int table = 2;
1325 		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
1326 			if (!lua_israwstring(L, -2) || !lua_isnumber(L, -1)) {
1327 				continue;
1328 			}
1329 			const string key = lua_tostring(L, -2);
1330 			const float value = lua_tofloat(L, -1);
1331 			ASSERT_SYNCED((float)value);
1332 
1333 			SetUnitResourceParam(unit, key, value);
1334 		}
1335 	}
1336 	else {
1337 		luaL_error(L, "Incorrect arguments to SetUnitResourcing");
1338 	}
1339 
1340 	return 0;
1341 }
1342 
1343 
SetUnitTooltip(lua_State * L)1344 int LuaSyncedCtrl::SetUnitTooltip(lua_State* L)
1345 {
1346 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1347 	if (unit == NULL) {
1348 		return 0;
1349 	}
1350 	const char *tmp = luaL_checkstring(L, 2);
1351 	if (tmp)
1352 		unit->tooltip = string(tmp, lua_strlen(L, 2));
1353 	else
1354 		unit->tooltip = "";
1355 	return 0;
1356 }
1357 
1358 
SetUnitHealth(lua_State * L)1359 int LuaSyncedCtrl::SetUnitHealth(lua_State* L)
1360 {
1361 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1362 	if (unit == NULL) {
1363 		return 0;
1364 	}
1365 
1366 	if (lua_isnumber(L, 2)) {
1367 		float health = lua_tofloat(L, 2);
1368 		health = min(unit->maxHealth, health);
1369 		unit->health = health;
1370 	}
1371 	else if (lua_istable(L, 2)) {
1372 		const int table = 2;
1373 		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
1374 			if (lua_israwstring(L, -2) && lua_isnumber(L, -1)) {
1375 				const string key = lua_tostring(L, -2);
1376 				const float value = lua_tofloat(L, -1);
1377 				if (key == "health") {
1378 					unit->health = min(unit->maxHealth, value);
1379 				}
1380 				else if (key == "capture") {
1381 					unit->captureProgress = value;
1382 				}
1383 				else if (key == "paralyze") {
1384 					unit->paralyzeDamage = max(0.0f, value);
1385 					if (unit->paralyzeDamage > (modInfo.paralyzeOnMaxHealth? unit->maxHealth: unit->health)) {
1386 						unit->SetStunned(true);
1387 					} else if (value < 0.0f) {
1388 						unit->SetStunned(false);
1389 					}
1390 				}
1391 				else if (key == "build") {
1392 					unit->buildProgress = value;
1393 
1394 					if (unit->buildProgress >= 1.0f) {
1395 						unit->FinishedBuilding(false);
1396 					}
1397 				}
1398 			}
1399 		}
1400 	}
1401 	else {
1402 		luaL_error(L, "Incorrect arguments to SetUnitHealth()");
1403 	}
1404 
1405 	return 0;
1406 }
1407 
1408 
SetUnitMaxHealth(lua_State * L)1409 int LuaSyncedCtrl::SetUnitMaxHealth(lua_State* L)
1410 {
1411 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1412 	if (unit == NULL) {
1413 		return 0;
1414 	}
1415 	unit->maxHealth = std::max(0.1f, luaL_checkfloat(L, 2));
1416 
1417 	if (unit->health > unit->maxHealth) {
1418 		unit->health = unit->maxHealth;
1419 	}
1420 	return 0;
1421 }
1422 
1423 
SetUnitStockpile(lua_State * L)1424 int LuaSyncedCtrl::SetUnitStockpile(lua_State* L)
1425 {
1426 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1427 	if (unit == NULL) {
1428 		return 0;
1429 	}
1430 
1431 	CWeapon* w = unit->stockpileWeapon;
1432 	if (w == NULL) {
1433 		return 0;
1434 	}
1435 
1436 	if (lua_isnumber(L, 2)) {
1437 		w->numStockpiled = max(0, luaL_checkint(L, 2));
1438 		unit->commandAI->UpdateStockpileIcon();
1439 	}
1440 
1441 	if (lua_isnumber(L, 3)) {
1442 		const float percent = max(0.0f, min(1.0f, lua_tofloat(L, 3)));
1443 		unit->stockpileWeapon->buildPercent = percent;
1444 	}
1445 
1446 	return 0;
1447 }
1448 
1449 
SetSingleUnitWeaponState(lua_State * L,CWeapon * weapon,int index)1450 static int SetSingleUnitWeaponState(lua_State* L, CWeapon* weapon, int index)
1451 {
1452 	const string key = lua_tostring(L, index);
1453 	const float value = lua_tofloat(L, index + 1);
1454 	// FIXME: KDR -- missing checks and updates?
1455 	if (key == "reloadState" || key == "reloadFrame") {
1456 		weapon->reloadStatus = (int)value;
1457 	}
1458 	else if (key == "reloadTime") {
1459 		weapon->reloadTime = (int)(value * GAME_SPEED);
1460 	}
1461 	else if (key == "accuracy") {
1462 		weapon->accuracyError = value;
1463 	}
1464 	else if (key == "sprayAngle") {
1465 		weapon->sprayAngle = value;
1466 	}
1467 	else if (key == "range") {
1468 		weapon->UpdateRange(value);
1469 	}
1470 	else if (key == "projectileSpeed") {
1471 		weapon->projectileSpeed = value;
1472 	}
1473 	else if (key == "burst") {
1474 		weapon->salvoSize = (int)value;
1475 	}
1476 	else if (key == "burstRate") {
1477 		weapon->salvoDelay = (int)(value * GAME_SPEED);
1478 	}
1479 	else if (key == "projectiles") {
1480 		weapon->projectilesPerShot = (int)value;
1481 	}
1482 	else if (key == "aimReady") {
1483 		// HACK, this should be set to result of lua_toboolean
1484 		weapon->angleGood = (value != 0.0f);
1485 	}
1486 	return 0;
1487 }
1488 
1489 
SetUnitWeaponState(lua_State * L)1490 int LuaSyncedCtrl::SetUnitWeaponState(lua_State* L)
1491 {
1492 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1493 	if (unit == NULL) {
1494 		return 0;
1495 	}
1496 
1497 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
1498 
1499 	if (weaponNum >= unit->weapons.size()) {
1500 		return 0;
1501 	}
1502 
1503 	CWeapon* weapon = unit->weapons[weaponNum];
1504 
1505 	if (lua_istable(L, 3)) {
1506 		// {key1 = value1, ...}
1507 		for (lua_pushnil(L); lua_next(L, 3) != 0; lua_pop(L, 1)) {
1508 			if (lua_israwstring(L, -2) && lua_isnumber(L, -1)) {
1509 				SetSingleUnitWeaponState(L, weapon, -2);
1510 			}
1511 		}
1512 	} else {
1513 		// key, value
1514 		if (lua_israwstring(L, 3) && lua_isnumber(L, 4)) {
1515 			SetSingleUnitWeaponState(L, weapon, 3);
1516 		}
1517 	}
1518 
1519 	return 0;
1520 }
1521 
1522 
SetUnitExperience(lua_State * L)1523 int LuaSyncedCtrl::SetUnitExperience(lua_State* L)
1524 {
1525 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1526 	if (unit == NULL) {
1527 		return 0;
1528 	}
1529 	const float experience = max(0.0f, luaL_checkfloat(L, 2));
1530 	unit->AddExperience(experience - unit->experience);
1531 	return 0;
1532 }
1533 
1534 
SetUnitArmored(lua_State * L)1535 int LuaSyncedCtrl::SetUnitArmored(lua_State* L)
1536 {
1537 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1538 	if (unit == NULL) {
1539 		return 0;
1540 	}
1541 	if (lua_isboolean(L, 2)) {
1542 		unit->armoredState = lua_toboolean(L, 2);
1543 	}
1544 
1545 	// armored multiple of 0 will crash spring
1546 	unit->armoredMultiple = std::max(0.0001f, luaL_optfloat(L, 3, unit->armoredMultiple));
1547 
1548 	if (lua_toboolean(L, 2)) {
1549 		unit->curArmorMultiple = unit->armoredMultiple;
1550 	} else {
1551 		unit->curArmorMultiple = 1.0f;
1552 	}
1553 	return 0;
1554 }
1555 
1556 
ParseLosBits(lua_State * L,int index,unsigned char bits)1557 static unsigned char ParseLosBits(lua_State* L, int index, unsigned char bits)
1558 {
1559 	if (lua_isnumber(L, index)) {
1560 		return (unsigned char)lua_tonumber(L, index);
1561 	}
1562 	else if (lua_istable(L, index)) {
1563 		for (lua_pushnil(L); lua_next(L, index) != 0; lua_pop(L, 1)) {
1564 			if (!lua_israwstring(L, -2)) { luaL_error(L, "bad key type");   }
1565 			if (!lua_isboolean(L, -1))   { luaL_error(L, "bad value type"); }
1566 			const string key = lua_tostring(L, -2);
1567 			const bool set = lua_toboolean(L, -1);
1568 			if (key == "los") {
1569 				if (set) { bits |=  LOS_INLOS; }
1570 				else     { bits &= ~LOS_INLOS; }
1571 			}
1572 			else if (key == "radar") {
1573 				if (set) { bits |=  LOS_INRADAR; }
1574 				else     { bits &= ~LOS_INRADAR; }
1575 			}
1576 			else if (key == "prevLos") {
1577 				if (set) { bits |=  LOS_PREVLOS; }
1578 				else     { bits &= ~LOS_PREVLOS; }
1579 			}
1580 			else if (key == "contRadar") {
1581 				if (set) { bits |=  LOS_CONTRADAR; }
1582 				else     { bits &= ~LOS_CONTRADAR; }
1583 			}
1584 		}
1585 		return bits;
1586 	}
1587  	luaL_error(L, "ERROR: expected number or table");
1588 	return 0;
1589 }
1590 
1591 
SetUnitLosMask(lua_State * L)1592 int LuaSyncedCtrl::SetUnitLosMask(lua_State* L)
1593 {
1594 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1595 	if (unit == NULL) {
1596 		return 0;
1597 	}
1598 	const int allyTeam = luaL_checkint(L, 2);
1599 	if (!teamHandler->IsValidAllyTeam(allyTeam)) {
1600 		luaL_error(L, "bad allyTeam");
1601 	}
1602 	const unsigned short losStatus = unit->losStatus[allyTeam];
1603 	const unsigned char  oldMask = losStatus >> 8;
1604 	const unsigned char  newMask = ParseLosBits(L, 3, oldMask);
1605 	const unsigned short state = (newMask << 8) | (losStatus & 0x00FF);
1606 
1607 	unit->losStatus[allyTeam] = state;
1608 	unit->SetLosStatus(allyTeam, unit->CalcLosStatus(allyTeam));
1609 
1610 	return 0;
1611 }
1612 
1613 
SetUnitLosState(lua_State * L)1614 int LuaSyncedCtrl::SetUnitLosState(lua_State* L)
1615 {
1616 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1617 	if (unit == NULL) {
1618 		return 0;
1619 	}
1620 	const int allyTeam = luaL_checkint(L, 2);
1621 	if (!teamHandler->IsValidAllyTeam(allyTeam)) {
1622 		luaL_error(L, "bad allyTeam");
1623 	}
1624 	const unsigned short losStatus = unit->losStatus[allyTeam];
1625 	const unsigned char  oldState = losStatus & 0xFF;
1626 	const unsigned char  newState = ParseLosBits(L, 3, oldState);
1627 	const unsigned short state = (losStatus & 0xFF00) | newState;
1628 
1629 	unit->SetLosStatus(allyTeam, state);
1630 
1631 	return 0;
1632 }
1633 
1634 
SetUnitCloak(lua_State * L)1635 int LuaSyncedCtrl::SetUnitCloak(lua_State* L)
1636 {
1637 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1638 	if (unit == NULL) {
1639 		return 0;
1640 	}
1641 
1642 	if (lua_isboolean(L, 2)) {
1643 		unit->scriptCloak = lua_toboolean(L, 2) ? 1 : 0;
1644 	} else if (lua_isnumber(L, 2)) {
1645 		unit->scriptCloak = lua_toint(L, 2);
1646 	} else if (!lua_isnoneornil(L, 2)) {
1647 		luaL_error(L, "Incorrect arguments to SetUnitCloak()");
1648 	}
1649 
1650 	if (lua_israwnumber(L, 3)) {
1651 		unit->decloakDistance = lua_tofloat(L, 3);
1652 	}
1653 	else if (lua_isboolean(L, 3)) {
1654 		const float defDist = unit->unitDef->decloakDistance;
1655 		if (lua_toboolean(L, 3)) {
1656 			unit->decloakDistance = math::fabsf(defDist);
1657 		} else {
1658 			unit->decloakDistance = defDist;
1659 		}
1660 	}
1661 
1662 	return 0;
1663 }
1664 
1665 
SetUnitStealth(lua_State * L)1666 int LuaSyncedCtrl::SetUnitStealth(lua_State* L)
1667 {
1668 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1669 	if (unit == NULL) {
1670 		return 0;
1671 	}
1672 	unit->stealth = luaL_checkboolean(L, 2);
1673 	return 0;
1674 }
1675 
1676 
SetUnitSonarStealth(lua_State * L)1677 int LuaSyncedCtrl::SetUnitSonarStealth(lua_State* L)
1678 {
1679 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1680 	if (unit == NULL) {
1681 		return 0;
1682 	}
1683 	unit->sonarStealth = luaL_checkboolean(L, 2);
1684 	return 0;
1685 }
1686 
1687 
SetUnitAlwaysVisible(lua_State * L)1688 int LuaSyncedCtrl::SetUnitAlwaysVisible(lua_State* L)
1689 {
1690 	return (SetWorldObjectAlwaysVisible(L, ParseUnit(L, __FUNCTION__, 1), __FUNCTION__));
1691 }
1692 
1693 
SetUnitMetalExtraction(lua_State * L)1694 int LuaSyncedCtrl::SetUnitMetalExtraction(lua_State* L)
1695 {
1696 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1697 	if (unit == NULL) {
1698 		return 0;
1699 	}
1700 	CExtractorBuilding* mex = dynamic_cast<CExtractorBuilding*>(unit);
1701 	if (mex == NULL) {
1702 		return 0;
1703 	}
1704 	const float depth = luaL_checkfloat(L, 2);
1705 	const float range = luaL_optfloat(L, 3, mex->GetExtractionRange());
1706 	mex->ResetExtraction();
1707 	mex->SetExtractionRangeAndDepth(range, depth);
1708 	return 0;
1709 }
1710 
1711 
SetUnitHarvestStorage(lua_State * L)1712 int LuaSyncedCtrl::SetUnitHarvestStorage(lua_State* L)
1713 {
1714 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1715 	if (unit == NULL) {
1716 		return 0;
1717 	}
1718 	unit->harvestStorage = luaL_checkfloat(L, 2);
1719 	return 0;
1720 }
1721 
SetUnitBuildSpeed(lua_State * L)1722 int LuaSyncedCtrl::SetUnitBuildSpeed(lua_State* L)
1723 {
1724 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1725 	if (unit == NULL) {
1726 		return 0;
1727 	}
1728 
1729 	const float buildScale = (1.0f / TEAM_SLOWUPDATE_RATE);
1730 	const float buildSpeed = buildScale * max(0.0f, luaL_checkfloat(L, 2));
1731 
1732 	CFactory* factory = dynamic_cast<CFactory*>(unit);
1733 	if (factory) {
1734 		factory->buildSpeed = buildSpeed;
1735 		return 0;
1736 	}
1737 
1738 	CBuilder* builder = dynamic_cast<CBuilder*>(unit);
1739 	if (!builder) {
1740 		return 0;
1741 	}
1742 	builder->buildSpeed = buildSpeed;
1743 	if (lua_isnumber(L, 3)) {
1744 		builder->repairSpeed    = buildScale * max(0.0f, lua_tofloat(L, 3));
1745 	}
1746 	if (lua_isnumber(L, 4)) {
1747 		builder->reclaimSpeed   = buildScale * max(0.0f, lua_tofloat(L, 4));
1748 	}
1749 	if (lua_isnumber(L, 5)) {
1750 		builder->resurrectSpeed = buildScale * max(0.0f, lua_tofloat(L, 5));
1751 	}
1752 	if (lua_isnumber(L, 6)) {
1753 		builder->captureSpeed   = buildScale * max(0.0f, lua_tofloat(L, 6));
1754 	}
1755 	if (lua_isnumber(L, 7)) {
1756 		builder->terraformSpeed = buildScale * max(0.0f, lua_tofloat(L, 7));
1757 	}
1758 	return 0;
1759 }
1760 
1761 
SetUnitNanoPieces(lua_State * L)1762 int LuaSyncedCtrl::SetUnitNanoPieces(lua_State* L)
1763 {
1764 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1765 	if (unit == NULL) {
1766 		return 0;
1767 	}
1768 
1769 	NanoPieceCache* pieceCache = NULL;
1770 	std::vector<int>* nanoPieces = NULL;
1771 
1772 	{
1773 		CBuilder* builder = dynamic_cast<CBuilder*>(unit);
1774 
1775 		if (builder != NULL) {
1776 			pieceCache = &builder->GetNanoPieceCache();
1777 			nanoPieces = &pieceCache->GetNanoPieces();
1778 		}
1779 
1780 		CFactory* factory = dynamic_cast<CFactory*>(unit);
1781 
1782 		if (factory != NULL) {
1783 			pieceCache = &factory->GetNanoPieceCache();
1784 			nanoPieces = &pieceCache->GetNanoPieces();
1785 		}
1786 	}
1787 
1788 	if (nanoPieces == NULL)
1789 		return 0;
1790 
1791 	nanoPieces->clear();
1792 	pieceCache->StopPolling();
1793 	luaL_checktype(L, 2, LUA_TTABLE);
1794 
1795 	for (lua_pushnil(L); lua_next(L, 2) != 0; lua_pop(L, 1)) {
1796 		if (lua_israwnumber(L, -1)) {
1797 			const int modelPieceNum = lua_toint(L, -1) - 1; //lua 1-indexed, c++ 0-indexed
1798 
1799 			if (unit->localModel->HasPiece(modelPieceNum)) {
1800 				nanoPieces->push_back(modelPieceNum);
1801 			} else {
1802 				luaL_error(L, "[SetUnitNanoPieces] incorrect model-piece number %d", modelPieceNum);
1803 			}
1804 		}
1805 	}
1806 
1807 	return 0;
1808 }
1809 
1810 
SetUnitBlocking(lua_State * L)1811 int LuaSyncedCtrl::SetUnitBlocking(lua_State* L)
1812 {
1813 	return (SetSolidObjectBlocking(L, ParseUnit(L, __FUNCTION__, 1)));
1814 }
1815 
1816 
SetUnitCrashing(lua_State * L)1817 int LuaSyncedCtrl::SetUnitCrashing(lua_State* L) {
1818 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1819 
1820 	if (unit == NULL) {
1821 		return 0;
1822 	}
1823 
1824 	AAirMoveType* amt = dynamic_cast<AAirMoveType*>(unit->moveType);
1825 	bool ret = false;
1826 
1827 	if (amt != NULL) {
1828 		const bool wantCrash = luaL_optboolean(L, 2, false);
1829 		const AAirMoveType::AircraftState aircraftState = amt->aircraftState;
1830 
1831 		// for simplicity, this can only set a flying aircraft to
1832 		// start crashing, or a crashing aircraft to start flying
1833 		if ( wantCrash && (aircraftState == AAirMoveType::AIRCRAFT_FLYING))
1834 			amt->SetState(AAirMoveType::AIRCRAFT_CRASHING);
1835 
1836 		if (!wantCrash && (aircraftState == AAirMoveType::AIRCRAFT_CRASHING))
1837 			amt->SetState(AAirMoveType::AIRCRAFT_FLYING);
1838 
1839 		ret = (amt->aircraftState != aircraftState);
1840 	}
1841 
1842 	lua_pushboolean(L, ret);
1843 	return 1;
1844 }
1845 
1846 
SetUnitShieldState(lua_State * L)1847 int LuaSyncedCtrl::SetUnitShieldState(lua_State* L)
1848 {
1849 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1850 	if (unit == NULL) {
1851 		return 0;
1852 	}
1853 
1854 	int args = lua_gettop(L);
1855 	int arg = 2;
1856 
1857 	CPlasmaRepulser* shield = static_cast<CPlasmaRepulser*>(unit->shieldWeapon);
1858 
1859 	if (lua_isnumber(L, 2) && args > 2) {
1860 		const size_t idx = luaL_optint(L, 2, -1) - LUA_WEAPON_BASE_INDEX;
1861 
1862 		if (idx < unit->weapons.size()) {
1863 			shield = dynamic_cast<CPlasmaRepulser*>(unit->weapons[idx]);
1864 		}
1865 
1866 		arg++;
1867 	}
1868 
1869 	if (shield == NULL) {
1870 		return 0;
1871 	}
1872 
1873 	if (lua_isboolean(L, arg)) { shield->SetEnabled(lua_toboolean(L, arg)); arg++; }
1874 	if (lua_isnumber(L, arg)) { shield->SetCurPower(lua_tofloat(L, arg)); }
1875 	return 0;
1876 }
1877 
1878 
SetUnitFlanking(lua_State * L)1879 int LuaSyncedCtrl::SetUnitFlanking(lua_State* L)
1880 {
1881 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1882 	if (unit == NULL) {
1883 		return 0;
1884 	}
1885 
1886 	const string key = luaL_checkstring(L, 2);
1887 
1888 	if (key == "mode") {
1889 		unit->flankingBonusMode = luaL_checkint(L, 3);
1890 	}
1891 	else if (key == "dir") {
1892 		float3 dir(luaL_checkfloat(L, 3),
1893 		           luaL_checkfloat(L, 4),
1894 		           luaL_checkfloat(L, 5));
1895 		unit->flankingBonusDir = dir.Normalize();
1896 	}
1897 	else if (key == "moveFactor") {
1898 		unit->flankingBonusMobilityAdd = luaL_checkfloat(L, 3);
1899 	}
1900 	else if (key == "minDamage") {
1901 		const float minDamage = luaL_checkfloat(L, 3);
1902 		const float maxDamage = unit->flankingBonusAvgDamage +
1903 		                        unit->flankingBonusDifDamage;
1904 		unit->flankingBonusAvgDamage = (maxDamage + minDamage) * 0.5f;
1905 		unit->flankingBonusDifDamage = (maxDamage - minDamage) * 0.5f;
1906 	}
1907 	else if (key == "maxDamage") {
1908 		const float maxDamage = luaL_checkfloat(L, 3);
1909 		const float minDamage = unit->flankingBonusAvgDamage -
1910 		                        unit->flankingBonusDifDamage;
1911 		unit->flankingBonusAvgDamage = (maxDamage + minDamage) * 0.5f;
1912 		unit->flankingBonusDifDamage = (maxDamage - minDamage) * 0.5f;
1913 	}
1914 	return 0;
1915 }
1916 
1917 
SetUnitTravel(lua_State * L)1918 int LuaSyncedCtrl::SetUnitTravel(lua_State* L)
1919 {
1920 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1921 	if (unit == NULL) {
1922 		return 0;
1923 	}
1924 	if (lua_isnumber(L, 2)) {
1925 		unit->travel = lua_tofloat(L, 2);
1926 	}
1927 	if (lua_isnumber(L, 3)) {
1928 		unit->travelPeriod = lua_tofloat(L, 3);
1929 	}
1930 	return 0;
1931 }
1932 
1933 
SetUnitFuel(lua_State * L)1934 int LuaSyncedCtrl::SetUnitFuel(lua_State* L)
1935 {
1936 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1937 	if (unit == NULL) {
1938 		return 0;
1939 	}
1940 	unit->currentFuel = luaL_checkfloat(L, 2);
1941 	return 0;
1942 }
1943 
1944 
SetUnitNeutral(lua_State * L)1945 int LuaSyncedCtrl::SetUnitNeutral(lua_State* L)
1946 {
1947 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1948 	if (unit == NULL) {
1949 		return 0;
1950 	}
1951 	if (lua_isboolean(L, 2)) {
1952 		unit->neutral = lua_toboolean(L, 2);
1953 	}
1954 	return 0;
1955 }
1956 
1957 
SetUnitTarget(lua_State * L)1958 int LuaSyncedCtrl::SetUnitTarget(lua_State* L)
1959 {
1960 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1961 	if (unit == NULL) {
1962 		return 0;
1963 	}
1964 	const int args = lua_gettop(L);
1965 	if (args >= 4 && !lua_isboolean(L, 3)) {
1966 		const float3 pos(luaL_checkfloat(L, 2),
1967 		                 luaL_checkfloat(L, 3),
1968 		                 luaL_checkfloat(L, 4));
1969 		const bool manualFire = lua_isboolean(L, 5) && lua_toboolean(L, 5);
1970 		const bool userTarget = lua_isboolean(L, 6) && lua_toboolean(L, 6);
1971 		lua_pushboolean(L,unit->AttackGround(pos, userTarget, manualFire));
1972 		return 1;
1973 	}
1974 	else if (args >= 2) {
1975 		CUnit* target = ParseRawUnit(L, __FUNCTION__, 2);
1976 		const bool manualFire = lua_isboolean(L, 3) && lua_toboolean(L, 3);
1977 		const bool userTarget = lua_isboolean(L, 4) && lua_toboolean(L, 4);
1978 		lua_pushboolean(L,unit->AttackUnit(target, userTarget, manualFire));
1979 		return 1;
1980 	}
1981 	else {
1982 		return 0;
1983 	}
1984 }
1985 
1986 
1987 
SetUnitMidAndAimPos(lua_State * L)1988 int LuaSyncedCtrl::SetUnitMidAndAimPos(lua_State* L)
1989 {
1990 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
1991 
1992 	if (unit == NULL) {
1993 		lua_pushboolean(L, false);
1994 		return 1;
1995 	}
1996 
1997 	#define FLOAT(i) luaL_checkfloat(L, i)
1998 	#define FLOAT3(i, j, k) float3(FLOAT(i), FLOAT(j), FLOAT(k))
1999 
2000 	const int argc = lua_gettop(L);
2001 	const float3 newMidPos = (argc >= 4)? FLOAT3(2, 3, 4): float3(unit->midPos);
2002 	const float3 newAimPos = (argc >= 7)? FLOAT3(5, 6, 7): float3(unit->aimPos);
2003 	const bool setRelative = luaL_optboolean(L, 8, false);
2004 	const bool updateQuads = (newMidPos != unit->midPos);
2005 
2006 	#undef FLOAT3
2007 	#undef FLOAT
2008 
2009 	if (updateQuads) {
2010 		// safety, possibly just need MovedUnit
2011 		quadField->RemoveUnit(unit);
2012 	}
2013 
2014 	unit->SetMidAndAimPos(newMidPos, newAimPos, setRelative);
2015 
2016 	if (updateQuads) {
2017 		quadField->MovedUnit(unit);
2018 	}
2019 
2020 	lua_pushboolean(L, true);
2021 	return 1;
2022 }
2023 
SetUnitRadiusAndHeight(lua_State * L)2024 int LuaSyncedCtrl::SetUnitRadiusAndHeight(lua_State* L)
2025 {
2026 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2027 
2028 	if (unit == NULL) {
2029 		lua_pushboolean(L, false);
2030 		return 1;
2031 	}
2032 
2033 	const float newRadius = std::max(1.0f, luaL_optfloat(L, 2, unit->radius));
2034 	const float newHeight = std::max(1.0f, luaL_optfloat(L, 3, unit->height));
2035 	const bool updateQuads = (newRadius != unit->radius);
2036 
2037 	if (updateQuads) {
2038 		// safety, possibly just need MovedUnit
2039 		quadField->RemoveUnit(unit);
2040 	}
2041 
2042 	unit->SetRadiusAndHeight(newRadius, newHeight);
2043 
2044 	if (updateQuads) {
2045 		quadField->MovedUnit(unit);
2046 	}
2047 
2048 	lua_pushboolean(L, true);
2049 	return 1;
2050 }
2051 
SetUnitCollisionVolumeData(lua_State * L)2052 int LuaSyncedCtrl::SetUnitCollisionVolumeData(lua_State* L)
2053 {
2054 	return (SetSolidObjectCollisionVolumeData(L, ParseUnit(L, __FUNCTION__, 1)));
2055 }
2056 
SetUnitPieceCollisionVolumeData(lua_State * L)2057 int LuaSyncedCtrl::SetUnitPieceCollisionVolumeData(lua_State* L)
2058 {
2059 	const CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2060 
2061 	if (unit == NULL)
2062 		return 0;
2063 
2064 	const LocalModel* localModel = unit->localModel;
2065 	const unsigned int pieceIndex = luaL_checkint(L, 2);
2066 
2067 	if (pieceIndex >= localModel->pieces.size())
2068 		luaL_argerror(L, 2, "invalid piece index");
2069 
2070 	LocalModelPiece* lmp = localModel->pieces[pieceIndex];
2071 	CollisionVolume* vol = lmp->GetCollisionVolume();
2072 
2073 	const float3 scales(luaL_checkfloat(L, 4), luaL_checkfloat(L, 5), luaL_checkfloat(L, 6));
2074 	const float3 offset(luaL_checkfloat(L, 7), luaL_checkfloat(L, 8), luaL_checkfloat(L, 9));
2075 
2076 	const unsigned int vType = luaL_optint(L, 10, vol->GetVolumeType());
2077 	const unsigned int pAxis = luaL_optint(L, 11, vol->GetPrimaryAxis());
2078 
2079 	// piece volumes are not allowed to use discrete hit-testing
2080 	vol->InitShape(scales, offset, vType, CollisionVolume::COLVOL_HITTEST_CONT, pAxis);
2081 	vol->SetIgnoreHits(!luaL_checkboolean(L, 3));
2082 	return 0;
2083 }
2084 
2085 
2086 
SetUnitSensorRadius(lua_State * L)2087 int LuaSyncedCtrl::SetUnitSensorRadius(lua_State* L)
2088 {
2089 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2090 	if (unit == NULL) {
2091 		return 0;
2092 	}
2093 
2094 	const string key = luaL_checkstring(L, 2);
2095 	const float radius = luaL_checkfloat(L, 3);
2096 
2097 	const int radarDiv    = radarHandler->radarDiv;
2098 	const int radarRadius = (int)(radius * radarHandler->invRadarDiv);
2099 
2100 	if (key == "los") {
2101 		const int losRange = (int)(radius * losHandler->invLosDiv);
2102 		unit->ChangeLos(losRange, unit->realAirLosRadius);
2103 		unit->realLosRadius = losRange;
2104 		lua_pushnumber(L, unit->losRadius * losHandler->losDiv);
2105 	} else if (key == "airLos") {
2106 		const int airRange = (int)(radius * losHandler->invAirDiv);
2107 		unit->ChangeLos(unit->realLosRadius, airRange);
2108 		unit->realAirLosRadius = airRange;
2109 		lua_pushnumber(L, unit->airLosRadius * losHandler->airDiv);
2110 	} else if (key == "radar") {
2111 		unit->ChangeSensorRadius(&unit->radarRadius, radarRadius);
2112 		lua_pushnumber(L, unit->radarRadius * radarDiv);
2113 	} else if (key == "sonar") {
2114 		unit->ChangeSensorRadius(&unit->sonarRadius, radarRadius);
2115 		lua_pushnumber(L, unit->sonarRadius * radarDiv);
2116 	} else if (key == "seismic") {
2117 		unit->ChangeSensorRadius(&unit->seismicRadius, radarRadius);
2118 		lua_pushnumber(L, unit->seismicRadius * radarDiv);
2119 	} else if (key == "radarJammer") {
2120 		unit->ChangeSensorRadius(&unit->jammerRadius, radarRadius);
2121 		lua_pushnumber(L, unit->jammerRadius * radarDiv);
2122 	} else if (key == "sonarJammer") {
2123 		unit->ChangeSensorRadius(&unit->sonarJamRadius, radarRadius);
2124 		lua_pushnumber(L, unit->sonarJamRadius * radarDiv);
2125 	} else {
2126 		return 0; // unknown sensor type
2127 	}
2128 
2129 	return 1;
2130 }
2131 
SetUnitPosErrorParams(lua_State * L)2132 int LuaSyncedCtrl::SetUnitPosErrorParams(lua_State* L)
2133 {
2134 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2135 
2136 	if (unit == NULL)
2137 		return 0;
2138 
2139 	unit->posErrorVector = float3(luaL_checkfloat(L, 2), luaL_checkfloat(L, 3), luaL_checkfloat(L, 4));
2140 	unit->posErrorDelta = float3(luaL_checkfloat(L, 5), luaL_checkfloat(L, 6), luaL_checkfloat(L, 7));
2141 	unit->nextPosErrorUpdate = luaL_optint(L, 8, unit->nextPosErrorUpdate);
2142 	return 0;
2143 }
2144 
2145 
SetUnitMoveGoal(lua_State * L)2146 int LuaSyncedCtrl::SetUnitMoveGoal(lua_State* L)
2147 {
2148 	CheckAllowGameChanges(L);
2149 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2150 
2151 	if (unit == NULL)
2152 		return 0;
2153 	if (unit->moveType == NULL)
2154 		return 0;
2155 
2156 	const float3 pos(luaL_checkfloat(L, 2), luaL_checkfloat(L, 3), luaL_checkfloat(L, 4));
2157 
2158 	const float radius = luaL_optfloat(L, 5, 0.0f);
2159 	const float speed  = luaL_optfloat(L, 6, unit->moveType->GetMaxSpeed());
2160 
2161 	if (luaL_optboolean(L, 7, false)) {
2162 		unit->moveType->StartMovingRaw(pos, radius);
2163 	} else {
2164 		unit->moveType->StartMoving(pos, radius, speed);
2165 	}
2166 
2167 	return 0;
2168 }
2169 
2170 
SetUnitPhysics(lua_State * L)2171 int LuaSyncedCtrl::SetUnitPhysics(lua_State* L)
2172 {
2173 	return (SetSolidObjectPhysicalState(L, ParseUnit(L, __FUNCTION__, 1)));
2174 }
2175 
SetUnitPosition(lua_State * L)2176 int LuaSyncedCtrl::SetUnitPosition(lua_State* L)
2177 {
2178 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2179 
2180 	if (unit == NULL)
2181 		return 0;
2182 
2183 	float3 pos;
2184 
2185 	if (lua_isnumber(L, 4)) {
2186 		// 2=x, 3=y, 4=z
2187 		pos.x = luaL_checkfloat(L, 2);
2188 		pos.y = luaL_checkfloat(L, 3);
2189 		pos.z = luaL_checkfloat(L, 4);
2190 	} else {
2191 		// 2=x, 3=z, 4=bool
2192 		pos.x = luaL_checkfloat(L, 2);
2193 		pos.z = luaL_checkfloat(L, 3);
2194 
2195 		if (luaL_optboolean(L, 4, false)) {
2196 			pos.y = CGround::GetHeightAboveWater(pos.x, pos.z);
2197 		} else {
2198 			pos.y = CGround::GetHeightReal(pos.x, pos.z);
2199 		}
2200 	}
2201 
2202 	unit->ForcedMove(pos);
2203 	return 0;
2204 }
2205 
2206 
SetUnitRotation(lua_State * L)2207 int LuaSyncedCtrl::SetUnitRotation(lua_State* L)
2208 {
2209 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2210 
2211 	if (unit == NULL)
2212 		return 0;
2213 
2214 	CMatrix44f matrix;
2215 	matrix.RotateZ(ClampRad(luaL_checkfloat(L, 4))); // .z := roll
2216 	matrix.RotateX(ClampRad(luaL_checkfloat(L, 2))); // .x := pitch
2217 	matrix.RotateY(ClampRad(luaL_checkfloat(L, 3))); // .y := yaw
2218 
2219 	assert(matrix.IsOrthoNormal() == 0);
2220 
2221 	// do not need ForcedSpin, below three calls cover it
2222 	unit->SetDirVectors(matrix);
2223 	unit->UpdateMidAndAimPos();
2224 	unit->SetHeadingFromDirection();
2225 	return 0;
2226 }
2227 
2228 
SetUnitDirection(lua_State * L)2229 int LuaSyncedCtrl::SetUnitDirection(lua_State* L)
2230 {
2231 	return (SetSolidObjectDirection(L, ParseUnit(L, __FUNCTION__, 1)));
2232 }
2233 
SetUnitVelocity(lua_State * L)2234 int LuaSyncedCtrl::SetUnitVelocity(lua_State* L)
2235 {
2236 	return (SetWorldObjectVelocity(L, ParseUnit(L, __FUNCTION__, 1)));
2237 }
2238 
2239 
AddUnitDamage(lua_State * L)2240 int LuaSyncedCtrl::AddUnitDamage(lua_State* L)
2241 {
2242 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2243 	if (unit == NULL) {
2244 		return 0;
2245 	}
2246 	const float damage    = luaL_checkfloat(L, 2);
2247 	const int paralyze    = luaL_optint(L, 3, 0);
2248 	const int attackerID  = luaL_optint(L, 4, -1);
2249 	const int weaponDefID = luaL_optint(L, 5, -1);
2250 	const float3 impulse  = float3(Clamp(luaL_optfloat(L, 6, 0.0f), -MAX_EXPLOSION_IMPULSE, MAX_EXPLOSION_IMPULSE),
2251 	                               Clamp(luaL_optfloat(L, 7, 0.0f), -MAX_EXPLOSION_IMPULSE, MAX_EXPLOSION_IMPULSE),
2252 	                               Clamp(luaL_optfloat(L, 8, 0.0f), -MAX_EXPLOSION_IMPULSE, MAX_EXPLOSION_IMPULSE));
2253 
2254 	CUnit* attacker = NULL;
2255 	if (attackerID >= 0) {
2256 		if (static_cast<size_t>(attackerID) >= unitHandler->MaxUnits()) {
2257 			return 0;
2258 		}
2259 		attacker = unitHandler->units[attackerID];
2260 	}
2261 
2262 	if (weaponDefID >= int(weaponDefHandler->weaponDefs.size())) {
2263 		return 0;
2264 	}
2265 
2266 	DamageArray damages;
2267 	damages[unit->armorType] = damage;
2268 	if (paralyze) {
2269 		damages.paralyzeDamageTime = paralyze;
2270 	}
2271 
2272 	unit->DoDamage(damages, impulse, attacker, weaponDefID, -1);
2273 	return 0;
2274 }
2275 
2276 
AddUnitImpulse(lua_State * L)2277 int LuaSyncedCtrl::AddUnitImpulse(lua_State* L)
2278 {
2279 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2280 	if (unit == NULL) {
2281 		return 0;
2282 	}
2283 
2284 	const float3 impulse(Clamp(luaL_checkfloat(L, 2), -MAX_EXPLOSION_IMPULSE, MAX_EXPLOSION_IMPULSE),
2285 	                     Clamp(luaL_checkfloat(L, 3), -MAX_EXPLOSION_IMPULSE, MAX_EXPLOSION_IMPULSE),
2286 	                     Clamp(luaL_checkfloat(L, 4), -MAX_EXPLOSION_IMPULSE, MAX_EXPLOSION_IMPULSE));
2287 
2288 	unit->ApplyImpulse(impulse);
2289 	return 0;
2290 }
2291 
2292 
AddUnitSeismicPing(lua_State * L)2293 int LuaSyncedCtrl::AddUnitSeismicPing(lua_State* L)
2294 {
2295 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2296 	if (unit == NULL) {
2297 		return 0;
2298 	}
2299 	const float pingSize = luaL_checkfloat(L, 2);
2300 	unit->DoSeismicPing(pingSize);
2301 	return 0;
2302 }
2303 
2304 
2305 /******************************************************************************/
2306 
AddUnitResource(lua_State * L)2307 int LuaSyncedCtrl::AddUnitResource(lua_State* L)
2308 {
2309 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2310 	if (unit == NULL) {
2311 		return 0;
2312 	}
2313 
2314 	const string type = luaL_checkstring(L, 2);
2315 
2316 	const float value = max(0.0f, luaL_checkfloat(L, 3));
2317 
2318 	if ((type == "m") || (type == "metal")) {
2319 		unit->AddMetal(value);
2320 	}
2321 	else if ((type == "e") || (type == "energy")) {
2322 		unit->AddEnergy(value);
2323 	}
2324 	return 0;
2325 }
2326 
2327 
UseUnitResource(lua_State * L)2328 int LuaSyncedCtrl::UseUnitResource(lua_State* L)
2329 {
2330 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2331 	if (unit == NULL) {
2332 		return 0;
2333 	}
2334 
2335 	if (lua_isstring(L, 2)) {
2336 		const string type = lua_tostring(L, 2);
2337 		const float value = max(0.0f, lua_tofloat(L, 3));
2338 
2339 		if ((type == "m") || (type == "metal")) {
2340 			lua_pushboolean(L, unit->UseMetal(value));
2341 		}
2342 		else if ((type == "e") || (type == "energy")) {
2343 			lua_pushboolean(L, unit->UseEnergy(value));
2344 		}
2345 		return 1;
2346 	}
2347 	else if (lua_istable(L, 2)) {
2348 		float metal  = 0.0f;
2349 		float energy = 0.0f;
2350 		const int table = 2;
2351 		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
2352 			if (lua_israwstring(L, -2) && lua_isnumber(L, -1)) {
2353 				const string key = lua_tostring(L, -2);
2354 				const float value = max(0.0f, lua_tofloat(L, -1));
2355 				if ((key == "m") || (key == "metal")) {
2356 					metal = value;
2357 				} else if ((key == "e") || (key == "energy")) {
2358 					energy = value;
2359 				}
2360 			}
2361 		}
2362 		CTeam* team = teamHandler->Team(unit->team);
2363 		if ((team->metal >= metal) && (team->energy >= energy)) {
2364 			unit->UseMetal(metal);
2365 			unit->UseEnergy(energy);
2366 			lua_pushboolean(L, true);
2367 		} else {
2368 			team->metalPull  += metal;
2369 			team->energyPull += energy;
2370 			lua_pushboolean(L, false);
2371 		}
2372 		return 1;
2373 	}
2374 	else {
2375 		luaL_error(L, "Incorrect arguments to UseUnitResource()");
2376 	}
2377 
2378 	return 0;
2379 }
2380 
2381 
2382 /******************************************************************************/
2383 
RemoveBuildingDecal(lua_State * L)2384 int LuaSyncedCtrl::RemoveBuildingDecal(lua_State* L)
2385 {
2386 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2387 
2388 	if (unit == NULL)
2389 		return 0;
2390 
2391 	groundDecals->ForceRemoveSolidObject(unit);
2392 	return 0;
2393 }
2394 
2395 
AddGrass(lua_State * L)2396 int LuaSyncedCtrl::AddGrass(lua_State* L)
2397 {
2398 	float3 pos(luaL_checkfloat(L, 1), 0.0f, luaL_checkfloat(L, 2));
2399 	pos.ClampInBounds();
2400 
2401 	grassDrawer->AddGrass(pos);
2402 	return 0;
2403 }
2404 
2405 
RemoveGrass(lua_State * L)2406 int LuaSyncedCtrl::RemoveGrass(lua_State* L)
2407 {
2408 	float3 pos(luaL_checkfloat(L, 1), 0.0f, luaL_checkfloat(L, 2));
2409 	pos.ClampInBounds();
2410 
2411 	grassDrawer->RemoveGrass(pos);
2412 	return 0;
2413 }
2414 
2415 
2416 /******************************************************************************/
2417 
CreateFeature(lua_State * L)2418 int LuaSyncedCtrl::CreateFeature(lua_State* L)
2419 {
2420 	CheckAllowGameChanges(L);
2421 
2422 	const FeatureDef* featureDef = NULL;
2423 
2424 	if (lua_israwstring(L, 1)) {
2425 		featureDef = featureHandler->GetFeatureDef(lua_tostring(L, 1));
2426 	} else if (lua_israwnumber(L, 1)) {
2427 		featureDef = featureHandler->GetFeatureDefByID(lua_toint(L, 1));
2428 	}
2429 	if (featureDef == NULL) {
2430 		return 0; // do not error  (featureDefs are dynamic)
2431 	}
2432 
2433 	const float3 pos(luaL_checkfloat(L, 2),
2434 	                 luaL_checkfloat(L, 3),
2435 	                 luaL_checkfloat(L, 4));
2436 
2437 	short int heading = 0;
2438 	if (lua_isnumber(L, 5)) {
2439 		heading = lua_toint(L, 5);
2440 	}
2441 
2442 	int facing = GetFacingFromHeading(heading);
2443 	int team = CtrlTeam(L);
2444 	if (team < 0) {
2445 		team = -1; // default to global for AllAccessTeam
2446 	}
2447 
2448 	// FIXME -- separate teamcolor/allyteam arguments
2449 
2450 	if (lua_isnumber(L, 6)) {
2451 		team = lua_toint(L, 6);
2452 		if (team < -1) {
2453 			team = -1;
2454 		} else if (team >= teamHandler->ActiveTeams()) {
2455 			return 0;
2456 		}
2457 	}
2458 
2459 	const int allyTeam = (team < 0) ? -1 : teamHandler->AllyTeam(team);
2460 	if (!CanControlFeatureAllyTeam(L, allyTeam)) {
2461 		luaL_error(L, "CreateFeature() bad team permission %d", team);
2462 	}
2463 
2464 	if (inCreateFeature) {
2465 		luaL_error(L, "CreateFeature() recursion is not permitted");
2466 	}
2467 
2468 	// use SetFeatureResurrect() to fill in the missing bits
2469 	inCreateFeature = true;
2470 	FeatureLoadParams params;
2471 	params.featureDef = featureDef;
2472 	params.unitDef    = NULL;
2473 	params.pos        = pos;
2474 	params.speed      = ZeroVector;
2475 	params.featureID  = luaL_optint(L, 7, -1);
2476 	params.teamID     = team;
2477 	params.allyTeamID = allyTeam;
2478 	params.heading    = heading,
2479 	params.facing     = facing,
2480 	params.smokeTime  = 0; // smokeTime
2481 
2482 	CFeature* feature = featureHandler->LoadFeature(params);
2483 	inCreateFeature = false;
2484 
2485 	if (feature != NULL) {
2486 		lua_pushnumber(L, feature->id);
2487 		return 1;
2488 	}
2489 
2490 	return 0;
2491 }
2492 
2493 
DestroyFeature(lua_State * L)2494 int LuaSyncedCtrl::DestroyFeature(lua_State* L)
2495 {
2496 	CheckAllowGameChanges(L);
2497 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2498 	if (feature == NULL) {
2499 		return 0;
2500 	}
2501 
2502 	if (inDestroyFeature) {
2503 		luaL_error(L, "DestroyFeature() recursion is not permitted");
2504 	}
2505 
2506 	inDestroyFeature = true;
2507 	featureHandler->DeleteFeature(feature);
2508 	inDestroyFeature = false;
2509 
2510 	return 0;
2511 }
2512 
2513 
TransferFeature(lua_State * L)2514 int LuaSyncedCtrl::TransferFeature(lua_State* L)
2515 {
2516 	CheckAllowGameChanges(L);
2517 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2518 	if (feature == NULL) {
2519 		return 0;
2520 	}
2521 	const int teamId = luaL_checkint(L, 2);
2522 	if (!teamHandler->IsValidTeam(teamId)) {
2523 		return 0;
2524 	}
2525 	feature->ChangeTeam(teamId);
2526 	return 0;
2527 }
2528 
2529 
SetFeatureAlwaysVisible(lua_State * L)2530 int LuaSyncedCtrl::SetFeatureAlwaysVisible(lua_State* L)
2531 {
2532 	return (SetWorldObjectAlwaysVisible(L, ParseFeature(L, __FUNCTION__, 1), __FUNCTION__));
2533 }
2534 
2535 
SetFeatureHealth(lua_State * L)2536 int LuaSyncedCtrl::SetFeatureHealth(lua_State* L)
2537 {
2538 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2539 	if (feature == NULL) {
2540 		return 0;
2541 	}
2542 	feature->health = luaL_checkfloat(L, 2);
2543 	return 0;
2544 }
2545 
2546 
SetFeatureReclaim(lua_State * L)2547 int LuaSyncedCtrl::SetFeatureReclaim(lua_State* L)
2548 {
2549 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2550 	if (feature == NULL) {
2551 		return 0;
2552 	}
2553 	feature->reclaimLeft = luaL_checkfloat(L, 2);
2554 	return 0;
2555 }
2556 
2557 
SetFeaturePhysics(lua_State * L)2558 int LuaSyncedCtrl::SetFeaturePhysics(lua_State* L)
2559 {
2560 	return (SetSolidObjectPhysicalState(L, ParseFeature(L, __FUNCTION__, 1)));
2561 }
2562 
SetFeaturePosition(lua_State * L)2563 int LuaSyncedCtrl::SetFeaturePosition(lua_State* L)
2564 {
2565 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2566 	if (feature == NULL) {
2567 		return 0;
2568 	}
2569 
2570 	const float3 pos(luaL_checkfloat(L, 2), luaL_checkfloat(L, 3), luaL_checkfloat(L, 4));
2571 	const bool snap(luaL_optboolean(L, 5, true));
2572 
2573 	feature->ForcedMove(pos);
2574 	feature->UpdateFinalHeight(snap);
2575 
2576 	return 0;
2577 }
2578 
2579 
SetFeatureDirection(lua_State * L)2580 int LuaSyncedCtrl::SetFeatureDirection(lua_State* L)
2581 {
2582 	return (SetSolidObjectDirection(L, ParseFeature(L, __FUNCTION__, 1)));
2583 }
2584 
SetFeatureVelocity(lua_State * L)2585 int LuaSyncedCtrl::SetFeatureVelocity(lua_State* L)
2586 {
2587 	return (SetWorldObjectVelocity(L, ParseFeature(L, __FUNCTION__, 1)));
2588 }
2589 
2590 
SetFeatureResurrect(lua_State * L)2591 int LuaSyncedCtrl::SetFeatureResurrect(lua_State* L)
2592 {
2593 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2594 	if (feature == NULL) {
2595 		return 0;
2596 	}
2597 
2598 	const UnitDef* ud = unitDefHandler->GetUnitDefByName(luaL_checkstring(L, 2));
2599 
2600 	if (ud != NULL) {
2601 		feature->udef = ud;
2602 	}
2603 
2604 	const int args = lua_gettop(L); // number of arguments
2605 	if (args >= 3) {
2606 		feature->buildFacing = LuaUtils::ParseFacing(L, __FUNCTION__, 3);
2607 	}
2608 	return 0;
2609 }
2610 
2611 
SetFeatureBlocking(lua_State * L)2612 int LuaSyncedCtrl::SetFeatureBlocking(lua_State* L)
2613 {
2614 	return (SetSolidObjectBlocking(L, ParseFeature(L, __FUNCTION__, 1)));
2615 }
2616 
SetFeatureNoSelect(lua_State * L)2617 int LuaSyncedCtrl::SetFeatureNoSelect(lua_State* L)
2618 {
2619 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2620 	if (feature == NULL) {
2621 		return 0;
2622 	}
2623 	feature->noSelect = !!luaL_checkboolean(L, 2);
2624 	return 0;
2625 }
2626 
2627 
2628 
SetFeatureMidAndAimPos(lua_State * L)2629 int LuaSyncedCtrl::SetFeatureMidAndAimPos(lua_State* L)
2630 {
2631 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2632 
2633 	if (feature == NULL) {
2634 		lua_pushboolean(L, false);
2635 		return 1;
2636 	}
2637 
2638 	#define FLOAT(i) luaL_checkfloat(L, i)
2639 	#define FLOAT3(i, j, k) float3(FLOAT(i), FLOAT(j), FLOAT(k))
2640 
2641 	const int argc = lua_gettop(L);
2642 	const float3 newMidPos = (argc >= 4)? FLOAT3(2, 3, 4): float3(feature->midPos);
2643 	const float3 newAimPos = (argc >= 7)? FLOAT3(5, 6, 7): float3(feature->aimPos);
2644 	const bool setRelative = luaL_optboolean(L, 8, false);
2645 	const bool updateQuads = (newMidPos != feature->midPos);
2646 
2647 	#undef FLOAT3
2648 	#undef FLOAT
2649 
2650 	if (updateQuads) {
2651 		quadField->RemoveFeature(feature);
2652 	}
2653 
2654 	feature->SetMidAndAimPos(newMidPos, newAimPos, setRelative);
2655 
2656 	if (updateQuads) {
2657 		quadField->AddFeature(feature);
2658 	}
2659 
2660 	lua_pushboolean(L, true);
2661 	return 1;
2662 }
2663 
SetFeatureRadiusAndHeight(lua_State * L)2664 int LuaSyncedCtrl::SetFeatureRadiusAndHeight(lua_State* L)
2665 {
2666 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
2667 
2668 	if (feature == NULL) {
2669 		lua_pushboolean(L, false);
2670 		return 1;
2671 	}
2672 
2673 	const float newRadius = std::max(1.0f, luaL_optfloat(L, 2, feature->radius));
2674 	const float newHeight = std::max(1.0f, luaL_optfloat(L, 3, feature->height));
2675 	const bool updateQuads = (newRadius != feature->radius);
2676 
2677 	if (updateQuads) {
2678 		quadField->RemoveFeature(feature);
2679 	}
2680 
2681 	feature->SetRadiusAndHeight(newRadius, newHeight);
2682 
2683 	if (updateQuads) {
2684 		quadField->AddFeature(feature);
2685 	}
2686 
2687 	lua_pushboolean(L, true);
2688 	return 1;
2689 }
2690 
SetFeatureCollisionVolumeData(lua_State * L)2691 int LuaSyncedCtrl::SetFeatureCollisionVolumeData(lua_State* L)
2692 {
2693 	return (SetSolidObjectCollisionVolumeData(L, ParseFeature(L, __FUNCTION__, 1)));
2694 }
2695 
2696 /******************************************************************************/
2697 /******************************************************************************/
2698 
SetProjectileAlwaysVisible(lua_State * L)2699 int LuaSyncedCtrl::SetProjectileAlwaysVisible(lua_State* L)
2700 {
2701 	return (SetWorldObjectAlwaysVisible(L, ParseProjectile(L, __FUNCTION__, 1), __FUNCTION__));
2702 }
2703 
SetProjectileMoveControl(lua_State * L)2704 int LuaSyncedCtrl::SetProjectileMoveControl(lua_State* L)
2705 {
2706 	CProjectile* proj = ParseProjectile(L, __FUNCTION__, 1);
2707 
2708 	if (proj == NULL)
2709 		return 0;
2710 	if (!proj->weapon && !proj->piece)
2711 		return 0;
2712 
2713 	proj->luaMoveCtrl = luaL_optboolean(L, 2, false);
2714 	return 0;
2715 }
2716 
SetProjectilePosition(lua_State * L)2717 int LuaSyncedCtrl::SetProjectilePosition(lua_State* L)
2718 {
2719 	CProjectile* proj = ParseProjectile(L, __FUNCTION__, 1);
2720 
2721 	if (proj == NULL)
2722 		return 0;
2723 
2724 	proj->pos.x = luaL_optfloat(L, 2, 0.0f);
2725 	proj->pos.y = luaL_optfloat(L, 3, 0.0f);
2726 	proj->pos.z = luaL_optfloat(L, 4, 0.0f);
2727 
2728 	return 0;
2729 }
2730 
SetProjectileVelocity(lua_State * L)2731 int LuaSyncedCtrl::SetProjectileVelocity(lua_State* L)
2732 {
2733 	return (SetWorldObjectVelocity(L, ParseProjectile(L, __FUNCTION__, 1)));
2734 }
2735 
SetProjectileCollision(lua_State * L)2736 int LuaSyncedCtrl::SetProjectileCollision(lua_State* L)
2737 {
2738 	CProjectile* proj = ParseProjectile(L, __FUNCTION__, 1);
2739 
2740 	if (proj == NULL)
2741 		return 0;
2742 
2743 	proj->Collision();
2744 	return 0;
2745 }
2746 
SetProjectileTarget(lua_State * L)2747 int LuaSyncedCtrl::SetProjectileTarget(lua_State* L)
2748 {
2749 	CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
2750 	CWeaponProjectile* wpro = NULL;
2751 
2752 	if (pro == NULL)
2753 		return 0;
2754 	if (!pro->weapon)
2755 		return 0;
2756 
2757 	struct ProjectileTarget {
2758 		static DependenceType GetObjectDepType(const CWorldObject* o) {
2759 			if (dynamic_cast<const      CSolidObject*>(o) != NULL) return DEPENDENCE_WEAPONTARGET;
2760 			if (dynamic_cast<const CWeaponProjectile*>(o) != NULL) return DEPENDENCE_INTERCEPTTARGET;
2761 			return DEPENDENCE_NONE;
2762 		}
2763 	};
2764 
2765 	wpro = static_cast<CWeaponProjectile*>(pro);
2766 
2767 	switch (lua_gettop(L)) {
2768 		case 3: {
2769 			const int id = luaL_checkint(L, 2);
2770 			const int type = luaL_checkint(L, 3);
2771 
2772 			CWorldObject* oldTargetObject = wpro->GetTargetObject();
2773 			CWorldObject* newTargetObject = NULL;
2774 
2775 			switch (type) {
2776 				case 'u': { newTargetObject = ParseUnit(L, __FUNCTION__, 2); } break;
2777 				case 'f': { newTargetObject = ParseFeature(L, __FUNCTION__, 2); } break;
2778 				case 'p': { newTargetObject = ParseProjectile(L, __FUNCTION__, 2); } break;
2779 				case 'g': { /* fall-through, needs four arguments (todo: or a table?) */ }
2780 				default: { /* if invalid type-argument, current target will be cleared */ } break;
2781 			}
2782 
2783 			const DependenceType oldDepType = ProjectileTarget::GetObjectDepType(oldTargetObject);
2784 			const DependenceType newDepType = ProjectileTarget::GetObjectDepType(newTargetObject);
2785 
2786 			if (oldTargetObject != NULL) {
2787 				wpro->DeleteDeathDependence(oldTargetObject, oldDepType);
2788 				wpro->SetTargetObject(NULL);
2789 			}
2790 			if (newTargetObject != NULL) {
2791 				wpro->AddDeathDependence(newTargetObject, newDepType);
2792 				wpro->SetTargetObject(newTargetObject);
2793 			}
2794 
2795 			assert(newTargetObject == NULL || newTargetObject->id == id);
2796 			lua_pushboolean(L, oldTargetObject != NULL || newTargetObject != NULL);
2797 			return 1;
2798 		} break;
2799 
2800 		case 4: {
2801 			if (wpro->GetTargetObject() != NULL) {
2802 				wpro->DeleteDeathDependence(wpro->GetTargetObject(), ProjectileTarget::GetObjectDepType(wpro->GetTargetObject()));
2803 			}
2804 
2805 			wpro->SetTargetObject(NULL);
2806 			wpro->SetTargetPos(float3(luaL_checkfloat(L, 2), luaL_checkfloat(L, 3), luaL_checkfloat(L, 4)));
2807 
2808 			lua_pushboolean(L, wpro->GetTargetObject() == NULL);
2809 			return 1;
2810 		} break;
2811 	}
2812 
2813 	return 0;
2814 }
2815 
2816 
SetProjectileGravity(lua_State * L)2817 int LuaSyncedCtrl::SetProjectileGravity(lua_State* L)
2818 {
2819 	CProjectile* proj = ParseProjectile(L, __FUNCTION__, 1);
2820 
2821 	if (proj == NULL)
2822 		return 0;
2823 
2824 	proj->mygravity = luaL_optfloat(L, 2, 0.0f);
2825 	return 0;
2826 }
2827 
SetProjectileSpinAngle(lua_State * L)2828 int LuaSyncedCtrl::SetProjectileSpinAngle(lua_State* L) { return 0; } // DEPRECATED
SetProjectileSpinSpeed(lua_State * L)2829 int LuaSyncedCtrl::SetProjectileSpinSpeed(lua_State* L) { return 0; } // DEPRECATED
SetProjectileSpinVec(lua_State * L)2830 int LuaSyncedCtrl::SetProjectileSpinVec(lua_State* L) { return 0; } // DEPRECATED
2831 
SetPieceProjectileParams(lua_State * L)2832 int LuaSyncedCtrl::SetPieceProjectileParams(lua_State* L)
2833 {
2834 	CProjectile* proj = ParseProjectile(L, __FUNCTION__, 1);
2835 
2836 	if (proj == NULL || !proj->piece)
2837 		return 0;
2838 
2839 	CPieceProjectile* pproj = static_cast<CPieceProjectile*>(proj);
2840 
2841 	pproj->explFlags = luaL_optint(L, 2, pproj->explFlags);
2842 	pproj->spinAngle = luaL_optfloat(L, 3, pproj->spinAngle);
2843 	pproj->spinSpeed = luaL_optfloat(L, 4, pproj->spinSpeed);
2844 	pproj->spinVec.x = luaL_optfloat(L, 5, pproj->spinVec.x);
2845 	pproj->spinVec.y = luaL_optfloat(L, 6, pproj->spinVec.y);
2846 	pproj->spinVec.z = luaL_optfloat(L, 7, pproj->spinVec.z);
2847 	return 0;
2848 }
2849 
2850 //
2851 // TODO: move this and SpawnCEG to LuaUnsyncedCtrl
2852 //
SetProjectileCEG(lua_State * L)2853 int LuaSyncedCtrl::SetProjectileCEG(lua_State* L)
2854 {
2855 	CProjectile* proj = ParseProjectile(L, __FUNCTION__, 1);
2856 
2857 	if (proj == NULL)
2858 		return 0;
2859 	if (!proj->weapon && !proj->piece)
2860 		return 0;
2861 
2862 	unsigned int cegID = CExplosionGeneratorHandler::EXPGEN_ID_INVALID;
2863 
2864 	if (lua_isstring(L, 2)) {
2865 		cegID = explGenHandler->LoadGeneratorID(std::string(CEG_PREFIX_STRING) + lua_tostring(L, 2));
2866 	} else {
2867 		cegID = luaL_checknumber(L, 2);
2868 	}
2869 
2870 	// if cegID is EXPGEN_ID_INVALID, this also returns NULL
2871 	if (explGenHandler->GetGenerator(cegID) != NULL) {
2872 		proj->SetCustomExplosionGeneratorID(cegID);
2873 	}
2874 
2875 	lua_pushnumber(L, cegID);
2876 	return 1;
2877 }
2878 
2879 
2880 /******************************************************************************/
2881 /******************************************************************************/
2882 
ParseUnitMap(lua_State * L,const char * caller,int table,vector<CUnit * > & unitIDs)2883 static void ParseUnitMap(lua_State* L, const char* caller,
2884                          int table, vector<CUnit*>& unitIDs)
2885 {
2886 	if (!lua_istable(L, table)) {
2887 		luaL_error(L, "%s(): error parsing unit map", caller);
2888 	}
2889 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
2890 		if (lua_israwnumber(L, -2)) {
2891 			CUnit* unit = ParseUnit(L, __FUNCTION__, -2);
2892 			if (unit == NULL) {
2893 				continue; // bad pointer
2894 			}
2895 			unitIDs.push_back(unit);
2896 		}
2897 	}
2898 }
2899 
2900 
ParseUnitArray(lua_State * L,const char * caller,int table,vector<CUnit * > & unitIDs)2901 static void ParseUnitArray(lua_State* L, const char* caller,
2902                            int table, vector<CUnit*>& unitIDs)
2903 {
2904 	if (!lua_istable(L, table)) {
2905 		luaL_error(L, "%s(): error parsing unit array", caller);
2906 	}
2907 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
2908 		if (lua_israwnumber(L, -2) && lua_isnumber(L, -1)) { // avoid 'n'
2909 			CUnit* unit = ParseUnit(L, __FUNCTION__, -1);
2910 			if (unit == NULL) {
2911 				continue; // bad pointer
2912 			}
2913 			unitIDs.push_back(unit);
2914 		}
2915 	}
2916 }
2917 
2918 
2919 /******************************************************************************/
2920 
GiveOrderToUnit(lua_State * L)2921 int LuaSyncedCtrl::GiveOrderToUnit(lua_State* L)
2922 {
2923 	CheckAllowGameChanges(L);
2924 
2925 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2926 	if (unit == NULL) {
2927 		luaL_error(L, "Invalid unitID given to GiveOrderToUnit()");
2928 	}
2929 
2930 	Command cmd = LuaUtils::ParseCommand(L, __FUNCTION__, 2);
2931 
2932 	if (!CanControlUnit(L, unit)) {
2933 		lua_pushboolean(L, false);
2934 		return 1;
2935 	}
2936 
2937 	if (inGiveOrder) {
2938 		luaL_error(L, "GiveOrderToUnit() recursion is not permitted");
2939 	}
2940 
2941 	inGiveOrder = true;
2942 	unit->commandAI->GiveCommand(cmd);
2943 	inGiveOrder = false;
2944 
2945 	lua_pushboolean(L, true);
2946 	return 1;
2947 }
2948 
2949 
GiveOrderToUnitMap(lua_State * L)2950 int LuaSyncedCtrl::GiveOrderToUnitMap(lua_State* L)
2951 {
2952 	CheckAllowGameChanges(L);
2953 
2954 	// units
2955 	vector<CUnit*> units;
2956 	ParseUnitMap(L, __FUNCTION__, 1, units);
2957 	const int unitCount = (int)units.size();
2958 
2959 	if (unitCount <= 0) {
2960 		lua_pushnumber(L, 0);
2961 		return 1;
2962 	}
2963 
2964 	Command cmd = LuaUtils::ParseCommand(L, __FUNCTION__, 2);
2965 
2966 	if (inGiveOrder) {
2967 		luaL_error(L, "GiveOrderToUnitMap() recursion is not permitted");
2968 	}
2969 
2970 	inGiveOrder = true;
2971 	int count = 0;
2972 	for (int i = 0; i < unitCount; i++) {
2973 		CUnit* unit = units[i];
2974 		if (CanControlUnit(L, unit)) {
2975 			unit->commandAI->GiveCommand(cmd);
2976 			count++;
2977 		}
2978 	}
2979 	inGiveOrder = false;
2980 
2981 	lua_pushnumber(L, count);
2982 	return 1;
2983 }
2984 
2985 
GiveOrderToUnitArray(lua_State * L)2986 int LuaSyncedCtrl::GiveOrderToUnitArray(lua_State* L)
2987 {
2988 	CheckAllowGameChanges(L);
2989 
2990 	// units
2991 	vector<CUnit*> units;
2992 	ParseUnitArray(L, __FUNCTION__, 1, units);
2993 	const int unitCount = (int)units.size();
2994 
2995 	if (unitCount <= 0) {
2996 		lua_pushnumber(L, 0);
2997 		return 1;
2998 	}
2999 
3000 	Command cmd = LuaUtils::ParseCommand(L, __FUNCTION__, 2);
3001 
3002 	if (inGiveOrder) {
3003 		luaL_error(L, "GiveOrderToUnitArray() recursion is not permitted");
3004 	}
3005 	inGiveOrder = true;
3006 
3007 	int count = 0;
3008 	for (int i = 0; i < unitCount; i++) {
3009 		CUnit* unit = units[i];
3010 		if (CanControlUnit(L, unit)) {
3011 			unit->commandAI->GiveCommand(cmd);
3012 			count++;
3013 		}
3014 	}
3015 
3016 	inGiveOrder = false;
3017 
3018 	lua_pushnumber(L, count);
3019 	return 1;
3020 }
3021 
3022 
GiveOrderArrayToUnitMap(lua_State * L)3023 int LuaSyncedCtrl::GiveOrderArrayToUnitMap(lua_State* L)
3024 {
3025 	CheckAllowGameChanges(L);
3026 
3027 	// units
3028 	vector<CUnit*> units;
3029 	ParseUnitMap(L, __FUNCTION__, 1, units);
3030 	const int unitCount = (int)units.size();
3031 
3032 	// commands
3033 	vector<Command> commands;
3034 	LuaUtils::ParseCommandArray(L, __FUNCTION__, 2, commands);
3035 	const int commandCount = (int)commands.size();
3036 
3037 	if ((unitCount <= 0) || (commandCount <= 0)) {
3038 		lua_pushnumber(L, 0);
3039 		return 1;
3040 	}
3041 
3042 	if (inGiveOrder) {
3043 		luaL_error(L, "GiveOrderArrayToUnitMap() recursion is not permitted");
3044 	}
3045 	inGiveOrder = true;
3046 
3047 	int count = 0;
3048 	for (int u = 0; u < unitCount; u++) {
3049 		CUnit* unit = units[u];
3050 		if (CanControlUnit(L, unit)) {
3051 			for (int c = 0; c < commandCount; c++) {
3052 				unit->commandAI->GiveCommand(commands[c]);
3053 			}
3054 			count++;
3055 		}
3056 	}
3057 	inGiveOrder = false;
3058 
3059 	lua_pushnumber(L, count);
3060 	return 1;
3061 }
3062 
3063 
GiveOrderArrayToUnitArray(lua_State * L)3064 int LuaSyncedCtrl::GiveOrderArrayToUnitArray(lua_State* L)
3065 {
3066 	CheckAllowGameChanges(L);
3067 
3068 	// units
3069 	vector<CUnit*> units;
3070 	ParseUnitArray(L, __FUNCTION__, 1, units);
3071 	const int unitCount = (int)units.size();
3072 
3073 	// commands
3074 	vector<Command> commands;
3075 	LuaUtils::ParseCommandArray(L, __FUNCTION__, 2, commands);
3076 	const int commandCount = (int)commands.size();
3077 
3078 	bool pairwise = luaL_optboolean(L, 3, false);
3079 
3080 	if ((unitCount <= 0) || (commandCount <= 0)) {
3081 		lua_pushnumber(L, 0);
3082 		return 1;
3083 	}
3084 
3085 	if (inGiveOrder) {
3086 		luaL_error(L, "GiveOrderArrayToUnitArray() recursion is not permitted");
3087 	}
3088 	inGiveOrder = true;
3089 
3090 	int count = 0;
3091 	if (pairwise) {
3092 		for (int x = 0; x < std::min(unitCount, commandCount); ++x) {
3093 			CUnit* unit = units[x];
3094 			if (CanControlUnit(L, unit)) {
3095 				unit->commandAI->GiveCommand(commands[x]);
3096 				count++;
3097 			}
3098 		}
3099 	}
3100 	else {
3101 		for (int u = 0; u < unitCount; u++) {
3102 			CUnit* unit = units[u];
3103 			if (CanControlUnit(L, unit)) {
3104 				for (int c = 0; c < commandCount; c++) {
3105 					unit->commandAI->GiveCommand(commands[c]);
3106 				}
3107 				count++;
3108 			}
3109 		}
3110 	}
3111 	inGiveOrder = false;
3112 
3113 	lua_pushnumber(L, count);
3114 	return 1;
3115 }
3116 
3117 
3118 /******************************************************************************/
3119 /******************************************************************************/
3120 
ParseParams(lua_State * L,const char * caller,float & factor,int & x1,int & z1,int & x2,int & z2,int resolution,int maxX,int maxZ)3121 static void ParseParams(lua_State* L, const char* caller, float& factor,
3122 		int& x1, int& z1, int& x2, int& z2, int resolution, int maxX, int maxZ)
3123 {
3124 	float fx1 = 0.0f;
3125 	float fz1 = 0.0f;
3126 	float fx2 = 0.0f;
3127 	float fz2 = 0.0f;
3128 
3129 	const int args = lua_gettop(L); // number of arguments
3130 	if (args == 3) {
3131 		fx1 = fx2 = luaL_checkfloat(L, 1);
3132 		fz1 = fz2 = luaL_checkfloat(L, 2);
3133 		factor    = luaL_checkfloat(L, 3);
3134 	}
3135 	else if (args == 5) {
3136 		fx1    = luaL_checkfloat(L, 1);
3137 		fz1    = luaL_checkfloat(L, 2);
3138 		fx2    = luaL_checkfloat(L, 3);
3139 		fz2    = luaL_checkfloat(L, 4);
3140 		factor = luaL_checkfloat(L, 5);
3141 		if (fx1 > fx2) {
3142 			std::swap(fx1, fx2);
3143 		}
3144 		if (fz1 > fz2) {
3145 			std::swap(fz1, fz2);
3146 		}
3147 	}
3148 	else {
3149 		luaL_error(L, "Incorrect arguments to %s()", caller);
3150 	}
3151 
3152 	// quantize and clamp
3153 	x1 = Clamp((int)(fx1 / resolution), 0, maxX);
3154 	x2 = Clamp((int)(fx2 / resolution), 0, maxX);
3155 	z1 = Clamp((int)(fz1 / resolution), 0, maxZ);
3156 	z2 = Clamp((int)(fz2 / resolution), 0, maxZ);
3157 
3158 }
3159 
ParseMapParams(lua_State * L,const char * caller,float & factor,int & x1,int & z1,int & x2,int & z2)3160 static inline void ParseMapParams(lua_State* L, const char* caller,
3161 		float& factor, int& x1, int& z1, int& x2, int& z2)
3162 {
3163 	ParseParams(L, caller, factor, x1, z1, x2, z2, SQUARE_SIZE, gs->mapx, gs->mapy);
3164 }
3165 
3166 
LevelHeightMap(lua_State * L)3167 int LuaSyncedCtrl::LevelHeightMap(lua_State* L)
3168 {
3169 	if (mapDamage->disabled) {
3170 		return 0;
3171 	}
3172 	float height;
3173 	int x1, x2, z1, z2;
3174 	ParseMapParams(L, __FUNCTION__, height, x1, z1, x2, z2);
3175 
3176 	for (int z = z1; z <= z2; z++) {
3177 		for (int x = x1; x <= x2; x++) {
3178 			readMap->SetHeight((z * gs->mapxp1) + x, height);
3179 		}
3180 	}
3181 
3182 	mapDamage->RecalcArea(x1, x2, z1, z2);
3183 	return 0;
3184 }
3185 
3186 
AdjustHeightMap(lua_State * L)3187 int LuaSyncedCtrl::AdjustHeightMap(lua_State* L)
3188 {
3189 	if (mapDamage->disabled) {
3190 		return 0;
3191 	}
3192 
3193 	float height;
3194 	int x1, x2, z1, z2;
3195 
3196 	ParseMapParams(L, __FUNCTION__, height, x1, z1, x2, z2);
3197 
3198 	for (int z = z1; z <= z2; z++) {
3199 		for (int x = x1; x <= x2; x++) {
3200 			readMap->AddHeight((z * gs->mapxp1) + x, height);
3201 		}
3202 	}
3203 
3204 	mapDamage->RecalcArea(x1, x2, z1, z2);
3205 	return 0;
3206 }
3207 
3208 
RevertHeightMap(lua_State * L)3209 int LuaSyncedCtrl::RevertHeightMap(lua_State* L)
3210 {
3211 	if (mapDamage->disabled) {
3212 		return 0;
3213 	}
3214 	float origFactor;
3215 	int x1, x2, z1, z2;
3216 	ParseMapParams(L, __FUNCTION__, origFactor, x1, z1, x2, z2);
3217 
3218 	const float* origMap = readMap->GetOriginalHeightMapSynced();
3219 	const float* currMap = readMap->GetCornerHeightMapSynced();
3220 
3221 	if (origFactor == 1.0f) {
3222 		for (int z = z1; z <= z2; z++) {
3223 			for (int x = x1; x <= x2; x++) {
3224 				const int idx = (z * gs->mapxp1) + x;
3225 
3226 				readMap->SetHeight(idx, origMap[idx]);
3227 			}
3228 		}
3229 	}
3230 	else {
3231 		const float currFactor = (1.0f - origFactor);
3232 		for (int z = z1; z <= z2; z++) {
3233 			for (int x = x1; x <= x2; x++) {
3234 				const int index = (z * gs->mapxp1) + x;
3235 				const float ofh = origFactor * origMap[index];
3236 				const float cfh = currFactor * currMap[index];
3237 				readMap->SetHeight(index, ofh + cfh);
3238 			}
3239 		}
3240 	}
3241 
3242 	mapDamage->RecalcArea(x1, x2, z1, z2);
3243 	return 0;
3244 }
3245 
3246 /******************************************************************************/
3247 /******************************************************************************/
3248 
AddHeightMap(lua_State * L)3249 int LuaSyncedCtrl::AddHeightMap(lua_State* L)
3250 {
3251 	if (!inHeightMap) {
3252 		luaL_error(L, "AddHeightMap() can only be called in SetHeightMapFunc()");
3253 	}
3254 
3255 	const float xl = luaL_checkfloat(L, 1);
3256 	const float zl = luaL_checkfloat(L, 2);
3257 	const float h  = luaL_checkfloat(L, 3);
3258 
3259 	// quantize
3260 	const int x = (int)(xl / SQUARE_SIZE);
3261 	const int z = (int)(zl / SQUARE_SIZE);
3262 
3263 	// discard invalid coordinates
3264 	if ((x < 0) || (x > gs->mapx) ||
3265 	    (z < 0) || (z > gs->mapy)) {
3266 		return 0;
3267 	}
3268 
3269 	const int index = (z * gs->mapxp1) + x;
3270 	const float oldHeight = readMap->GetCornerHeightMapSynced()[index];
3271 	heightMapAmountChanged += math::fabsf(h);
3272 
3273 	// update RecalcArea()
3274 	if (x < heightMapx1) { heightMapx1 = x; }
3275 	if (x > heightMapx2) { heightMapx2 = x; }
3276 	if (z < heightMapz1) { heightMapz1 = z; }
3277 	if (z > heightMapz2) { heightMapz2 = z; }
3278 
3279 	readMap->AddHeight(index, h);
3280 	// push the new height
3281 	lua_pushnumber(L, oldHeight + h);
3282 	return 1;
3283 }
3284 
3285 
SetHeightMap(lua_State * L)3286 int LuaSyncedCtrl::SetHeightMap(lua_State* L)
3287 {
3288 	if (!inHeightMap) {
3289 		luaL_error(L, "SetHeightMap() can only be called in SetHeightMapFunc()");
3290 	}
3291 
3292 	const float xl = luaL_checkfloat(L, 1);
3293 	const float zl = luaL_checkfloat(L, 2);
3294 	const float h  = luaL_checkfloat(L, 3);
3295 
3296 	// quantize
3297 	const int x = (int)(xl / SQUARE_SIZE);
3298 	const int z = (int)(zl / SQUARE_SIZE);
3299 
3300 	// discard invalid coordinates
3301 	if ((x < 0) || (x > gs->mapx) ||
3302 	    (z < 0) || (z > gs->mapy)) {
3303 		return 0;
3304 	}
3305 
3306 	const int index = (z * gs->mapxp1) + x;
3307 	const float oldHeight = readMap->GetCornerHeightMapSynced()[index];
3308 	float height = oldHeight;
3309 
3310 	if (lua_israwnumber(L, 4)) {
3311 		const float t = lua_tofloat(L, 4);
3312 		height += (h - oldHeight) * t;
3313 	} else{
3314 		height = h;
3315 	}
3316 
3317 	const float heightDiff = (height - oldHeight);
3318 	heightMapAmountChanged += math::fabsf(heightDiff);
3319 
3320 	// update RecalcArea()
3321 	if (x < heightMapx1) { heightMapx1 = x; }
3322 	if (x > heightMapx2) { heightMapx2 = x; }
3323 	if (z < heightMapz1) { heightMapz1 = z; }
3324 	if (z > heightMapz2) { heightMapz2 = z; }
3325 
3326 	readMap->SetHeight(index, height);
3327 	lua_pushnumber(L, heightDiff);
3328 	return 1;
3329 }
3330 
3331 
SetHeightMapFunc(lua_State * L)3332 int LuaSyncedCtrl::SetHeightMapFunc(lua_State* L)
3333 {
3334 	if (mapDamage->disabled) {
3335 		return 0;
3336 	}
3337 
3338 	const int args = lua_gettop(L); // number of arguments
3339 	if ((args < 1) || !lua_isfunction(L, 1)) {
3340 		luaL_error(L, "Incorrect arguments to Spring.SetHeightMapFunc(func, ...)");
3341 	}
3342 
3343 	if (inHeightMap) {
3344 		luaL_error(L, "SetHeightMapFunc() recursion is not permitted");
3345 	}
3346 
3347 	heightMapx1 = gs->mapx;
3348 	heightMapx2 = -1;
3349 	heightMapz1 = gs->mapy;
3350 	heightMapz2 = 0;
3351 	heightMapAmountChanged = 0.0f;
3352 
3353 	inHeightMap = true;
3354 	const int error = lua_pcall(L, (args - 1), 0, 0);
3355 	inHeightMap = false;
3356 
3357 	if (error != 0) {
3358 		LOG_L(L_ERROR, "Spring.SetHeightMapFunc: error(%i) = %s",
3359 				error, lua_tostring(L, -1));
3360 		lua_error(L);
3361 	}
3362 
3363 	if (heightMapx2 > -1) {
3364 		mapDamage->RecalcArea(heightMapx1, heightMapx2, heightMapz1, heightMapz2);
3365 	}
3366 
3367 	lua_pushnumber(L, heightMapAmountChanged);
3368 	return 1;
3369 }
3370 
3371 /******************************************************************************/
3372 /* smooth mesh manipulation                                                   */
3373 /******************************************************************************/
3374 
ParseSmoothMeshParams(lua_State * L,const char * caller,float & factor,int & x1,int & z1,int & x2,int & z2)3375 static inline void ParseSmoothMeshParams(lua_State* L, const char* caller,
3376 		float& factor, int& x1, int& z1, int& x2, int& z2)
3377 {
3378 	ParseParams(L, caller, factor, x1, z1, x2, z2,
3379 			smoothGround->GetResolution(), smoothGround->GetMaxX(),
3380 			smoothGround->GetMaxY());
3381 }
3382 
3383 
LevelSmoothMesh(lua_State * L)3384 int LuaSyncedCtrl::LevelSmoothMesh(lua_State *L)
3385 {
3386 	float height;
3387 	int x1, x2, z1, z2;
3388 	ParseSmoothMeshParams(L, __FUNCTION__, height, x1, z1, x2, z2);
3389 
3390 	for (int z = z1; z <= z2; z++) {
3391 		for (int x = x1; x <= x2; x++) {
3392 			const int index = (z * smoothGround->GetMaxX()) + x;
3393 			smoothGround->SetHeight(index, height);
3394 		}
3395 	}
3396 	return 0;
3397 }
3398 
AdjustSmoothMesh(lua_State * L)3399 int LuaSyncedCtrl::AdjustSmoothMesh(lua_State *L)
3400 {
3401 	float height;
3402 	int x1, x2, z1, z2;
3403 	ParseSmoothMeshParams(L, __FUNCTION__, height, x1, z1, x2, z2);
3404 
3405 	for (int z = z1; z <= z2; z++) {
3406 		for (int x = x1; x <= x2; x++) {
3407 			const int index = (z * smoothGround->GetMaxX()) + x;
3408 			smoothGround->AddHeight(index, height);
3409 		}
3410 	}
3411 
3412 	return 0;
3413 }
3414 
RevertSmoothMesh(lua_State * L)3415 int LuaSyncedCtrl::RevertSmoothMesh(lua_State *L)
3416 {
3417 	float origFactor;
3418 	int x1, x2, z1, z2;
3419 	ParseSmoothMeshParams(L, __FUNCTION__, origFactor, x1, z1, x2, z2);
3420 
3421 	const float* origMap = smoothGround->GetOriginalMeshData();
3422 	const float* currMap = smoothGround->GetMeshData();
3423 
3424 	if (origFactor == 1.0f) {
3425 		for (int z = z1; z <= z2; z++) {
3426 			for (int x = x1; x <= x2; x++) {
3427 				const int idx = (z * smoothGround->GetMaxX()) + x;
3428 				smoothGround->SetHeight(idx, origMap[idx]);
3429 			}
3430 		}
3431 	}
3432 	else {
3433 		const float currFactor = (1.0f - origFactor);
3434 		for (int z = z1; z <= z2; z++) {
3435 			for (int x = x1; x <= x2; x++) {
3436 				const int index = (z * smoothGround->GetMaxX()) + x;
3437 				const float ofh = origFactor * origMap[index];
3438 				const float cfh = currFactor * currMap[index];
3439 				smoothGround->SetHeight(index, ofh + cfh);
3440 			}
3441 		}
3442 	}
3443 
3444 	return 0;
3445 }
3446 
3447 
AddSmoothMesh(lua_State * L)3448 int LuaSyncedCtrl::AddSmoothMesh(lua_State *L)
3449 {
3450 	if (!inSmoothMesh) {
3451 		luaL_error(L, "AddSmoothMesh() can only be called in SetSmoothMeshFunc()");
3452 	}
3453 
3454 	const float xl = luaL_checkfloat(L, 1);
3455 	const float zl = luaL_checkfloat(L, 2);
3456 	const float h  = luaL_checkfloat(L, 3);
3457 
3458 	// quantize
3459 	const int x = (int)(xl / smoothGround->GetResolution());
3460 	const int z = (int)(zl / smoothGround->GetResolution());
3461 
3462 	// discard invalid coordinates
3463 	if ((x < 0) || (x > smoothGround->GetMaxX()) ||
3464 	    (z < 0) || (z > smoothGround->GetMaxY())) {
3465 		return 0;
3466 	}
3467 
3468 	const int index = (z * smoothGround->GetMaxX()) + x;
3469 	const float oldHeight = smoothGround->GetMeshData()[index];
3470 	smoothMeshAmountChanged += math::fabsf(h);
3471 
3472 	smoothGround->AddHeight(index, h);
3473 	// push the new height
3474 	lua_pushnumber(L, oldHeight + h);
3475 	return 1;
3476 }
3477 
SetSmoothMesh(lua_State * L)3478 int LuaSyncedCtrl::SetSmoothMesh(lua_State *L)
3479 {
3480 	if (!inSmoothMesh) {
3481 		luaL_error(L, "SetSmoothMesh() can only be called in SetSmoothMeshFunc()");
3482 	}
3483 
3484 	const float xl = luaL_checkfloat(L, 1);
3485 	const float zl = luaL_checkfloat(L, 2);
3486 	const float h  = luaL_checkfloat(L, 3);
3487 
3488 	// quantize
3489 	const int x = (int)(xl / smoothGround->GetResolution());
3490 	const int z = (int)(zl / smoothGround->GetResolution());
3491 
3492 	// discard invalid coordinates
3493 	if ((x < 0) || (x > smoothGround->GetMaxX()) ||
3494 	    (z < 0) || (z > smoothGround->GetMaxY())) {
3495 		return 0;
3496 	}
3497 
3498 	const int index = (z * (smoothGround->GetMaxX())) + x;
3499 	const float oldHeight = smoothGround->GetMeshData()[index];
3500 	float height = oldHeight;
3501 
3502 	if (lua_israwnumber(L, 4)) {
3503 		const float t = lua_tofloat(L, 4);
3504 		height += (h - oldHeight) * t;
3505 	} else{
3506 		height = h;
3507 	}
3508 
3509 	const float heightDiff = (height - oldHeight);
3510 	smoothMeshAmountChanged += math::fabsf(heightDiff);
3511 
3512 	smoothGround->SetHeight(index, height);
3513 	lua_pushnumber(L, heightDiff);
3514 	return 1;
3515 }
3516 
SetSmoothMeshFunc(lua_State * L)3517 int LuaSyncedCtrl::SetSmoothMeshFunc(lua_State *L)
3518 {
3519 	const int args = lua_gettop(L); // number of arguments
3520 	if ((args < 1) || !lua_isfunction(L, 1)) {
3521 		luaL_error(L, "Incorrect arguments to Spring.SetSmoothMeshFunc(func, ...)");
3522 	}
3523 
3524 	if (inSmoothMesh) {
3525 		luaL_error(L, "SetHeightMapFunc() recursion is not permitted");
3526 	}
3527 
3528 	heightMapAmountChanged = 0.0f;
3529 
3530 	inSmoothMesh = true;
3531 	const int error = lua_pcall(L, (args - 1), 0, 0);
3532 	inSmoothMesh = false;
3533 
3534 	if (error != 0) {
3535 		LOG_L(L_ERROR, "Spring.SetSmoothMeshFunc: error(%i) = %s",
3536 				error, lua_tostring(L, -1));
3537 		lua_error(L);
3538 	}
3539 
3540 	lua_pushnumber(L, smoothMeshAmountChanged);
3541 	return 1;
3542 }
3543 
3544 /******************************************************************************/
3545 /******************************************************************************/
3546 
SetMapSquareTerrainType(lua_State * L)3547 int LuaSyncedCtrl::SetMapSquareTerrainType(lua_State* L)
3548 {
3549 	const int hx = int(luaL_checkfloat(L, 1) / SQUARE_SIZE);
3550 	const int hz = int(luaL_checkfloat(L, 2) / SQUARE_SIZE);
3551 
3552 	if ((hx < 0) || (hx > gs->mapx) || (hz < 0) || (hz > gs->mapy)) {
3553 		return 0;
3554 	}
3555 
3556 	const int tx = hx >> 1;
3557 	const int tz = hz >> 1;
3558 
3559 	const int ott = readMap->GetTypeMapSynced()[tz * gs->hmapx + tx];
3560 	const int ntt = luaL_checkint(L, 3);
3561 
3562 	readMap->GetTypeMapSynced()[tz * gs->hmapx + tx] = std::max(0, std::min(ntt, (CMapInfo::NUM_TERRAIN_TYPES - 1)));
3563 	pathManager->TerrainChange(hx, hz,  hx + 1, hz + 1,  TERRAINCHANGE_SQUARE_TYPEMAP_INDEX);
3564 
3565 	lua_pushnumber(L, ott);
3566 	return 1;
3567 }
3568 
SetTerrainTypeData(lua_State * L)3569 int LuaSyncedCtrl::SetTerrainTypeData(lua_State* L)
3570 {
3571 	const int args = lua_gettop(L);
3572 	const int tti = luaL_checkint(L, 1);
3573 
3574 	if (tti < 0 || tti >= CMapInfo::NUM_TERRAIN_TYPES) {
3575 		lua_pushboolean(L, false);
3576 		return 1;
3577 	}
3578 
3579 	CMapInfo::TerrainType* tt = const_cast<CMapInfo::TerrainType*>(&mapInfo->terrainTypes[tti]);
3580 
3581 	const int checksumOld = HsiehHash(tt, sizeof(CMapInfo::TerrainType), 0);
3582 
3583 	if (args >= 2 && lua_isnumber(L, 2)) { tt->tankSpeed  = lua_tofloat(L, 2); }
3584 	if (args >= 3 && lua_isnumber(L, 3)) { tt->kbotSpeed  = lua_tofloat(L, 3); }
3585 	if (args >= 4 && lua_isnumber(L, 4)) { tt->hoverSpeed = lua_tofloat(L, 4); }
3586 	if (args >= 5 && lua_isnumber(L, 5)) { tt->shipSpeed  = lua_tofloat(L, 5); }
3587 
3588 	const int checksumNew = HsiehHash(tt, sizeof(CMapInfo::TerrainType), 0);
3589 	if (checksumOld == checksumNew) {
3590 		// no change, no need to repath
3591 		lua_pushboolean(L, true);
3592 		return 1;
3593 	}
3594 
3595 	/*
3596 	if (!mapDamage->disabled) {
3597 		CBasicMapDamage* bmd = dynamic_cast<CBasicMapDamage*>(mapDamage);
3598 
3599 		if (bmd != NULL) {
3600 			tt->hardness = luaL_checkfloat(L, 6);
3601 			bmd->invHardness[tti] = 1.0f / tt->hardness;
3602 		}
3603 	}
3604 	*/
3605 
3606 	const unsigned char* typeMap = readMap->GetTypeMapSynced();
3607 
3608 	// update all map-squares set to this terrain-type (slow)
3609 	for (int tx = 0; tx < gs->hmapx; tx++) {
3610 		for (int tz = 0; tz < gs->hmapy; tz++) {
3611 			if (typeMap[tz * gs->hmapx + tx] == tti) {
3612 				pathManager->TerrainChange((tx << 1), (tz << 1),  (tx << 1) + 1, (tz << 1) + 1,  TERRAINCHANGE_TYPEMAP_SPEED_VALUES);
3613 			}
3614 		}
3615 	}
3616 
3617 	lua_pushboolean(L, true);
3618 	return 1;
3619 }
3620 
3621 /******************************************************************************/
3622 /******************************************************************************/
3623 
UnitWeaponFire(lua_State * L)3624 int LuaSyncedCtrl::UnitWeaponFire(lua_State* L)
3625 {
3626 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
3627 
3628 	if (unit == NULL)
3629 		return 0;
3630 	if (static_cast<uint32_t>(luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX) >= unit->weapons.size())
3631 		return 0;
3632 
3633 	unit->weapons[luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX]->Fire(false);
3634 	return 0;
3635 }
UnitWeaponHoldFire(lua_State * L)3636 int LuaSyncedCtrl::UnitWeaponHoldFire(lua_State* L)
3637 {
3638 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
3639 
3640 	if (unit == NULL)
3641 		return 0;
3642 	if (static_cast<uint32_t>(luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX) >= unit->weapons.size())
3643 		return 0;
3644 
3645 	unit->weapons[luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX]->HoldFire();
3646 	return 0;
3647 }
3648 
SpawnProjectile(lua_State * L)3649 int LuaSyncedCtrl::SpawnProjectile(lua_State* L)
3650 {
3651 	ProjectileParams params;
3652 
3653 	if ((params.weaponDef = weaponDefHandler->GetWeaponDefByID(luaL_checkint(L, 1))) == NULL) {
3654 		return 0;
3655 	}
3656 
3657 	ParseProjectileParams(L, params, 2, __FUNCTION__);
3658 
3659 	lua_pushnumber(L, WeaponProjectileFactory::LoadProjectile(params));
3660 	return 1;
3661 }
3662 
SpawnCEG(lua_State * L)3663 int LuaSyncedCtrl::SpawnCEG(lua_State* L)
3664 {
3665 	const float3 pos(luaL_optfloat(L, 2, 0.0f), luaL_optfloat(L, 3, 0.0f), luaL_optfloat(L, 4, 0.0f));
3666 	const float3 dir(luaL_optfloat(L, 5, 0.0f), luaL_optfloat(L, 6, 0.0f), luaL_optfloat(L, 7, 0.0f));
3667 
3668 	const float rad = luaL_optfloat(L, 8, 0.0f);
3669 	const float dmg = luaL_optfloat(L, 9, 0.0f);
3670 	const float dmgMod = luaL_optfloat(L, 10, 1.0f);
3671 
3672 	unsigned int cegID = CExplosionGeneratorHandler::EXPGEN_ID_INVALID;
3673 
3674 	if (lua_isstring(L, 1)) {
3675 		// args from Lua are assumed not to include the prefix
3676 		// (Spawn*C*EG implies only custom generators can fire)
3677 		cegID = explGenHandler->LoadGeneratorID(std::string(CEG_PREFIX_STRING) + lua_tostring(L, 1));
3678 	} else {
3679 		cegID = luaL_checknumber(L, 1);
3680 	}
3681 
3682 	lua_pushboolean(L, explGenHandler->GenExplosion(cegID, pos, dir, dmg, rad, dmgMod, NULL, NULL));
3683 	lua_pushnumber(L, cegID);
3684 	return 2;
3685 }
3686 
3687 /******************************************************************************/
3688 /******************************************************************************/
3689 
SetNoPause(lua_State * L)3690 int LuaSyncedCtrl::SetNoPause(lua_State* L)
3691 {
3692 	if (!FullCtrl(L)) {
3693 		return 0;
3694 	}
3695 	// Important: only works in server mode, has no effect in client mode
3696 	if (gameServer)
3697 		gameServer->SetGamePausable(!luaL_checkboolean(L, 1));
3698 
3699 	return 0;
3700 }
3701 
3702 
SetUnitToFeature(lua_State * L)3703 int LuaSyncedCtrl::SetUnitToFeature(lua_State* L)
3704 {
3705 	if (!FullCtrl(L)) {
3706 		return 0;
3707 	}
3708 	CUnit::SetSpawnFeature(luaL_checkboolean(L, 1));
3709 	return 0;
3710 }
3711 
3712 
SetExperienceGrade(lua_State * L)3713 int LuaSyncedCtrl::SetExperienceGrade(lua_State* L)
3714 {
3715 	if (!FullCtrl(L)) {
3716 		return 0;
3717 	}
3718 
3719 	CUnit::SetExpGrade(luaL_checkfloat(L, 1));
3720 
3721 	// NOTE: for testing, should be using modrules.tdf
3722 	if (gs->cheatEnabled) {
3723 		if (lua_isnumber(L, 2)) {
3724 			CUnit::SetExpPowerScale(lua_tofloat(L, 2));
3725 		}
3726 		if (lua_isnumber(L, 3)) {
3727 			CUnit::SetExpHealthScale(lua_tofloat(L, 3));
3728 		}
3729 		if (lua_isnumber(L, 4)) {
3730 			CUnit::SetExpReloadScale(lua_tofloat(L, 4));
3731 		}
3732 	}
3733 	return 0;
3734 }
3735 
3736 
SetRadarErrorParams(lua_State * L)3737 int LuaSyncedCtrl::SetRadarErrorParams(lua_State* L)
3738 {
3739 	const int allyTeamID = lua_tonumber(L, 1);
3740 
3741 	if (!teamHandler->IsValidAllyTeam(allyTeamID))
3742 		return 0;
3743 
3744 	radarHandler->SetAllyTeamRadarErrorSize(allyTeamID, luaL_checknumber(L, 2));
3745 	radarHandler->SetBaseRadarErrorSize(luaL_optnumber(L, 3, radarHandler->GetBaseRadarErrorSize()));
3746 	radarHandler->SetBaseRadarErrorMult(luaL_optnumber(L, 4, radarHandler->GetBaseRadarErrorMult()));
3747 	return 0;
3748 }
3749 
3750 
3751 /******************************************************************************/
3752 /******************************************************************************/
3753 
ParseNamedInt(lua_State * L,const string & key,const string & name,int & value)3754 static bool ParseNamedInt(lua_State* L, const string& key,
3755                           const string& name, int& value)
3756 {
3757 	if (key != name) {
3758 		return false;
3759 	}
3760 	if (lua_isnumber(L, -1)) {
3761 		value = lua_toint(L, -1);
3762 	} else {
3763 		luaL_error(L, "bad %s argument", name.c_str());
3764 	}
3765 	return true;
3766 }
3767 
3768 
ParseNamedBool(lua_State * L,const string & key,const string & name,bool & value)3769 static bool ParseNamedBool(lua_State* L, const string& key,
3770                            const string& name, bool& value)
3771 {
3772 	if (key != name) {
3773 		return false;
3774 	}
3775 	if (lua_isboolean(L, -1)) {
3776 		value = (int)lua_toboolean(L, -1);
3777 	} else {
3778 		luaL_error(L, "bad %s argument", name.c_str());
3779 	}
3780 	return true;
3781 }
3782 
3783 
ParseNamedString(lua_State * L,const string & key,const string & name,string & value)3784 static bool ParseNamedString(lua_State* L, const string& key,
3785                              const string& name, string& value)
3786 {
3787 	if (key != name) {
3788 		return false;
3789 	}
3790 	if (lua_isstring(L, -1)) {
3791 		value = lua_tostring(L, -1);
3792 	} else {
3793 		luaL_error(L, "bad %s argument", name.c_str());
3794 	}
3795 	return true;
3796 }
3797 
3798 
ParseStringVector(lua_State * L,int index,vector<string> & strvec)3799 static int ParseStringVector(lua_State* L, int index, vector<string>& strvec)
3800 {
3801 	strvec.clear();
3802 	int i = 1;
3803 	while (true) {
3804 		lua_rawgeti(L, index, i);
3805 		if (lua_isstring(L, -1)) {
3806 			strvec.push_back(lua_tostring(L, -1));
3807 			lua_pop(L, 1);
3808 			i++;
3809 		} else {
3810 			lua_pop(L, 1);
3811 			return (i - 1);
3812 		}
3813 	}
3814 }
3815 
3816 
3817 /******************************************************************************/
3818 
ParseCommandDescription(lua_State * L,int table,CommandDescription & cd)3819 static bool ParseCommandDescription(lua_State* L, int table,
3820                                     CommandDescription& cd)
3821 {
3822 	if (!lua_istable(L, table)) {
3823 		luaL_error(L, "Can not parse CommandDescription");
3824 		return false;
3825 	}
3826 
3827 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
3828 		if (!lua_israwstring(L, -2)) {
3829 			continue;
3830 		}
3831 		const string key = lua_tostring(L, -2);
3832 		if (ParseNamedInt(L,    key, "id",          cd.id)         ||
3833 		    ParseNamedInt(L,    key, "type",        cd.type)       ||
3834 		    ParseNamedString(L, key, "name",        cd.name)       ||
3835 		    ParseNamedString(L, key, "action",      cd.action)     ||
3836 		    ParseNamedString(L, key, "tooltip",     cd.tooltip)    ||
3837 		    ParseNamedString(L, key, "texture",     cd.iconname)   ||
3838 		    ParseNamedString(L, key, "cursor",      cd.mouseicon)  ||
3839 		    ParseNamedBool(L,   key, "hidden",      cd.hidden)     ||
3840 		    ParseNamedBool(L,   key, "disabled",    cd.disabled)   ||
3841 		    ParseNamedBool(L,   key, "showUnique",  cd.showUnique) ||
3842 		    ParseNamedBool(L,   key, "onlyTexture", cd.onlyTexture)) {
3843 			continue; // successfully parsed a parameter
3844 		}
3845 		if ((key != "params") || !lua_istable(L, -1)) {
3846 			luaL_error(L, "Unknown cmdDesc parameter %s", key.c_str());
3847 		}
3848 		// collect the parameters
3849 		const int paramTable = lua_gettop(L);
3850 		ParseStringVector(L, paramTable, cd.params);
3851 	}
3852 
3853 	return true;
3854 }
3855 
3856 
EditUnitCmdDesc(lua_State * L)3857 int LuaSyncedCtrl::EditUnitCmdDesc(lua_State* L)
3858 {
3859 	if (!FullCtrl(L)) {
3860 		return 0;
3861 	}
3862 
3863 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
3864 	if (unit == NULL) {
3865 		return 0;
3866 	}
3867 	vector<CommandDescription>& cmdDescs = unit->commandAI->possibleCommands;
3868 
3869 	const int cmdDescID = luaL_checkint(L, 2) - 1;
3870 	if ((cmdDescID < 0) || (cmdDescID >= (int)cmdDescs.size())) {
3871 		return 0;
3872 	}
3873 
3874 	ParseCommandDescription(L, 3, cmdDescs[cmdDescID]);
3875 
3876 	selectedUnitsHandler.PossibleCommandChange(unit);
3877 
3878 	return 0;
3879 }
3880 
3881 
InsertUnitCmdDesc(lua_State * L)3882 int LuaSyncedCtrl::InsertUnitCmdDesc(lua_State* L)
3883 {
3884 	if (!FullCtrl(L)) {
3885 		return 0;
3886 	}
3887 	const int args = lua_gettop(L); // number of arguments
3888 	if (((args == 2) && !lua_istable(L, 2)) ||
3889 	    ((args >= 3) && (!lua_isnumber(L, 2) || !lua_istable(L, 3)))) {
3890 		luaL_error(L, "Incorrect arguments to InsertUnitCmdDesc");
3891 	}
3892 
3893 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
3894 	if (unit == NULL) {
3895 		return 0;
3896 	}
3897 	vector<CommandDescription>& cmdDescs = unit->commandAI->possibleCommands;
3898 
3899 	int table = 2;
3900 	if (args >= 3) {
3901 		table = 3;
3902 	}
3903 
3904 	int cmdDescID = (int)cmdDescs.size();
3905 	if (args >= 3) {
3906 		cmdDescID = lua_toint(L, 2) - 1;
3907 		if (cmdDescID < 0) {
3908 			cmdDescID = 0;
3909 		}
3910 	}
3911 
3912 	CommandDescription cd;
3913 	ParseCommandDescription(L, table, cd);
3914 
3915 	if (cmdDescID >= (int)cmdDescs.size()) {
3916 		cmdDescs.push_back(cd);
3917 	} else {
3918 		vector<CommandDescription>::iterator it = cmdDescs.begin();
3919 		advance(it, cmdDescID);
3920 		cmdDescs.insert(it, cd);
3921 	}
3922 
3923 	selectedUnitsHandler.PossibleCommandChange(unit);
3924 
3925 	return 0;
3926 }
3927 
3928 
RemoveUnitCmdDesc(lua_State * L)3929 int LuaSyncedCtrl::RemoveUnitCmdDesc(lua_State* L)
3930 {
3931 	if (!FullCtrl(L)) {
3932 		return 0;
3933 	}
3934 
3935 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
3936 	if (unit == NULL) {
3937 		return 0;
3938 	}
3939 	vector<CommandDescription>& cmdDescs = unit->commandAI->possibleCommands;
3940 
3941 	int cmdDescID = (int)cmdDescs.size() - 1;
3942 	if (lua_isnumber(L, 2)) {
3943 		cmdDescID = lua_toint(L, 2) - 1;
3944 	}
3945 
3946 	if ((cmdDescID < 0) || (cmdDescID >= (int)cmdDescs.size())) {
3947 		return 0;
3948 	}
3949 
3950 	vector<CommandDescription>::iterator it = cmdDescs.begin();
3951 	advance(it, cmdDescID);
3952 	cmdDescs.erase(it);
3953 
3954 	selectedUnitsHandler.PossibleCommandChange(unit);
3955 
3956 	return 0;
3957 }
3958 
3959 
3960 /******************************************************************************/
3961 /******************************************************************************/
3962