1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "LuaSyncedRead.h"
4 
5 #include "LuaInclude.h"
6 
7 #include "LuaConfig.h"
8 #include "LuaHandle.h"
9 #include "LuaHashString.h"
10 #include "LuaMetalMap.h"
11 #include "LuaPathFinder.h"
12 #include "LuaRules.h"
13 #include "LuaRulesParams.h"
14 #include "LuaUtils.h"
15 #include "ExternalAI/SkirmishAIHandler.h"
16 #include "Game/Game.h"
17 #include "Game/GameSetup.h"
18 #include "Game/Camera.h"
19 #include "Game/GameHelper.h"
20 #include "Game/Players/Player.h"
21 #include "Game/Players/PlayerHandler.h"
22 #include "Map/Ground.h"
23 #include "Map/MapDamage.h"
24 #include "Map/MapInfo.h"
25 #include "Map/ReadMap.h"
26 #include "Rendering/Models/IModelParser.h"
27 #include "Sim/Misc/SideParser.h"
28 #include "Sim/Features/Feature.h"
29 #include "Sim/Features/FeatureHandler.h"
30 #include "Sim/Misc/CollisionVolume.h"
31 #include "Sim/Misc/GroundBlockingObjectMap.h"
32 #include "Sim/Misc/LosHandler.h"
33 #include "Sim/Misc/SmoothHeightMesh.h"
34 #include "Sim/Misc/QuadField.h"
35 #include "Sim/Misc/TeamHandler.h"
36 #include "Sim/Misc/Wind.h"
37 #include "Sim/MoveTypes/StrafeAirMoveType.h"
38 #include "Sim/MoveTypes/GroundMoveType.h"
39 #include "Sim/MoveTypes/HoverAirMoveType.h"
40 #include "Sim/MoveTypes/ScriptMoveType.h"
41 #include "Sim/MoveTypes/StaticMoveType.h"
42 #include "Sim/MoveTypes/MoveDefHandler.h"
43 #include "Sim/Path/IPathManager.h"
44 #include "Sim/Projectiles/Projectile.h"
45 #include "Sim/Projectiles/PieceProjectile.h"
46 #include "Sim/Projectiles/WeaponProjectiles/WeaponProjectile.h"
47 #include "Sim/Projectiles/ProjectileHandler.h"
48 #include "Sim/Units/BuildInfo.h"
49 #include "Sim/Units/Unit.h"
50 #include "Sim/Units/UnitDef.h"
51 #include "Sim/Units/UnitHandler.h"
52 #include "Sim/Units/UnitDefHandler.h"
53 #include "Sim/Units/UnitLoader.h"
54 #include "Sim/Units/Scripts/CobInstance.h"
55 #include "Sim/Units/UnitTypes/Builder.h"
56 #include "Sim/Units/UnitTypes/Factory.h"
57 #include "Sim/Units/UnitTypes/TransportUnit.h"
58 #include "Sim/Units/CommandAI/Command.h"
59 #include "Sim/Units/CommandAI/CommandAI.h"
60 #include "Sim/Units/CommandAI/FactoryCAI.h"
61 #include "Sim/Weapons/PlasmaRepulser.h"
62 #include "Sim/Weapons/Weapon.h"
63 #include "Sim/Weapons/WeaponDefHandler.h"
64 #include "System/bitops.h"
65 #include "System/myMath.h"
66 #include "System/FileSystem/FileHandler.h"
67 #include "System/FileSystem/VFSHandler.h"
68 #include "System/FileSystem/FileSystem.h"
69 #include "System/Util.h"
70 
71 #include <set>
72 #include <list>
73 #include <map>
74 #include <cctype>
75 
76 
77 using std::min;
78 using std::max;
79 using std::map;
80 using std::set;
81 
82 static const LuaHashString hs_n("n");
83 
84 // 0 and positive numbers are teams (not allyTeams)
85 enum UnitAllegiance {
86 	AllUnits   = -1,
87 	MyUnits    = -2,
88 	AllyUnits  = -3,
89 	EnemyUnits = -4
90 };
91 
92 
93 /******************************************************************************/
94 /******************************************************************************/
95 
PushEntries(lua_State * L)96 bool LuaSyncedRead::PushEntries(lua_State* L)
97 {
98 	// allegiance constants
99 	LuaPushNamedNumber(L, "ALL_UNITS",   AllUnits);
100 	LuaPushNamedNumber(L, "MY_UNITS",    MyUnits);
101 	LuaPushNamedNumber(L, "ALLY_UNITS",  AllyUnits);
102 	LuaPushNamedNumber(L, "ENEMY_UNITS", EnemyUnits);
103 
104 #define REGISTER_LUA_CFUNC(x) \
105 	lua_pushstring(L, #x);      \
106 	lua_pushcfunction(L, x);    \
107 	lua_rawset(L, -3)
108 
109 	// READ routines, sync safe
110 
111 	REGISTER_LUA_CFUNC(IsCheatingEnabled);
112 	REGISTER_LUA_CFUNC(IsGodModeEnabled);
113 	REGISTER_LUA_CFUNC(IsDevLuaEnabled);
114 	REGISTER_LUA_CFUNC(IsEditDefsEnabled);
115 	REGISTER_LUA_CFUNC(AreHelperAIsEnabled);
116 	REGISTER_LUA_CFUNC(FixedAllies);
117 
118 	REGISTER_LUA_CFUNC(IsGameOver);
119 
120 	REGISTER_LUA_CFUNC(GetGaiaTeamID);
121 
122 	REGISTER_LUA_CFUNC(GetGameFrame);
123 	REGISTER_LUA_CFUNC(GetGameSeconds);
124 
125 	REGISTER_LUA_CFUNC(GetGameRulesParam);
126 	REGISTER_LUA_CFUNC(GetGameRulesParams);
127 
128 	REGISTER_LUA_CFUNC(GetMapOptions);
129 	REGISTER_LUA_CFUNC(GetModOptions);
130 
131 	REGISTER_LUA_CFUNC(GetWind);
132 
133 	REGISTER_LUA_CFUNC(GetHeadingFromVector);
134 	REGISTER_LUA_CFUNC(GetVectorFromHeading);
135 
136 	REGISTER_LUA_CFUNC(GetSideData);
137 
138 	REGISTER_LUA_CFUNC(GetAllyTeamStartBox);
139 	REGISTER_LUA_CFUNC(GetTeamStartPosition);
140 
141 	REGISTER_LUA_CFUNC(GetPlayerList);
142 	REGISTER_LUA_CFUNC(GetTeamList);
143 	REGISTER_LUA_CFUNC(GetAllyTeamList);
144 
145 	REGISTER_LUA_CFUNC(GetPlayerInfo);
146 	REGISTER_LUA_CFUNC(GetPlayerControlledUnit);
147 	REGISTER_LUA_CFUNC(GetAIInfo);
148 
149 	REGISTER_LUA_CFUNC(GetTeamInfo);
150 	REGISTER_LUA_CFUNC(GetTeamResources);
151 	REGISTER_LUA_CFUNC(GetTeamUnitStats);
152 	REGISTER_LUA_CFUNC(GetTeamResourceStats);
153 	REGISTER_LUA_CFUNC(GetTeamRulesParam);
154 	REGISTER_LUA_CFUNC(GetTeamRulesParams);
155 	REGISTER_LUA_CFUNC(GetTeamStatsHistory);
156 	REGISTER_LUA_CFUNC(GetTeamLuaAI);
157 
158 	REGISTER_LUA_CFUNC(GetAllyTeamInfo);
159 	REGISTER_LUA_CFUNC(AreTeamsAllied);
160 	REGISTER_LUA_CFUNC(ArePlayersAllied);
161 
162 	REGISTER_LUA_CFUNC(ValidUnitID);
163 	REGISTER_LUA_CFUNC(ValidFeatureID);
164 
165 	REGISTER_LUA_CFUNC(GetAllUnits);
166 	REGISTER_LUA_CFUNC(GetTeamUnits);
167 	REGISTER_LUA_CFUNC(GetTeamUnitsSorted);
168 	REGISTER_LUA_CFUNC(GetTeamUnitsCounts);
169 	REGISTER_LUA_CFUNC(GetTeamUnitsByDefs);
170 	REGISTER_LUA_CFUNC(GetTeamUnitDefCount);
171 	REGISTER_LUA_CFUNC(GetTeamUnitCount);
172 
173 	REGISTER_LUA_CFUNC(GetUnitsInRectangle);
174 	REGISTER_LUA_CFUNC(GetUnitsInBox);
175 	REGISTER_LUA_CFUNC(GetUnitsInPlanes);
176 	REGISTER_LUA_CFUNC(GetUnitsInSphere);
177 	REGISTER_LUA_CFUNC(GetUnitsInCylinder);
178 
179 	REGISTER_LUA_CFUNC(GetFeaturesInRectangle);
180 	REGISTER_LUA_CFUNC(GetFeaturesInSphere);
181 	REGISTER_LUA_CFUNC(GetFeaturesInCylinder);
182 	REGISTER_LUA_CFUNC(GetProjectilesInRectangle);
183 
184 	REGISTER_LUA_CFUNC(GetUnitNearestAlly);
185 	REGISTER_LUA_CFUNC(GetUnitNearestEnemy);
186 
187 	REGISTER_LUA_CFUNC(GetUnitTooltip);
188 	REGISTER_LUA_CFUNC(GetUnitDefID);
189 	REGISTER_LUA_CFUNC(GetUnitTeam);
190 	REGISTER_LUA_CFUNC(GetUnitAllyTeam);
191 	REGISTER_LUA_CFUNC(GetUnitNeutral);
192 	REGISTER_LUA_CFUNC(GetUnitHealth);
193 	REGISTER_LUA_CFUNC(GetUnitIsDead);
194 	REGISTER_LUA_CFUNC(GetUnitIsStunned);
195 	REGISTER_LUA_CFUNC(GetUnitResources);
196 	REGISTER_LUA_CFUNC(GetUnitMetalExtraction);
197 	REGISTER_LUA_CFUNC(GetUnitExperience);
198 	REGISTER_LUA_CFUNC(GetUnitStates);
199 	REGISTER_LUA_CFUNC(GetUnitArmored);
200 	REGISTER_LUA_CFUNC(GetUnitIsActive);
201 	REGISTER_LUA_CFUNC(GetUnitIsCloaked);
202 	REGISTER_LUA_CFUNC(GetUnitSelfDTime);
203 	REGISTER_LUA_CFUNC(GetUnitStockpile);
204 	REGISTER_LUA_CFUNC(GetUnitSensorRadius);
205 	REGISTER_LUA_CFUNC(GetUnitPosErrorParams);
206 	REGISTER_LUA_CFUNC(GetUnitHeight);
207 	REGISTER_LUA_CFUNC(GetUnitRadius);
208 	REGISTER_LUA_CFUNC(GetUnitPosition);
209 	REGISTER_LUA_CFUNC(GetUnitBasePosition);
210 	REGISTER_LUA_CFUNC(GetUnitVectors);
211 	REGISTER_LUA_CFUNC(GetUnitRotation);
212 	REGISTER_LUA_CFUNC(GetUnitDirection);
213 	REGISTER_LUA_CFUNC(GetUnitHeading);
214 	REGISTER_LUA_CFUNC(GetUnitVelocity);
215 	REGISTER_LUA_CFUNC(GetUnitBuildFacing);
216 	REGISTER_LUA_CFUNC(GetUnitIsBuilding);
217 	REGISTER_LUA_CFUNC(GetUnitCurrentBuildPower);
218 	REGISTER_LUA_CFUNC(GetUnitHarvestStorage);
219 	REGISTER_LUA_CFUNC(GetUnitNanoPieces);
220 	REGISTER_LUA_CFUNC(GetUnitTransporter);
221 	REGISTER_LUA_CFUNC(GetUnitIsTransporting);
222 	REGISTER_LUA_CFUNC(GetUnitShieldState);
223 	REGISTER_LUA_CFUNC(GetUnitFlanking);
224 	REGISTER_LUA_CFUNC(GetUnitWeaponState);
225 	REGISTER_LUA_CFUNC(GetUnitWeaponVectors);
226 	REGISTER_LUA_CFUNC(GetUnitWeaponTryTarget);
227 	REGISTER_LUA_CFUNC(GetUnitWeaponTestTarget);
228 	REGISTER_LUA_CFUNC(GetUnitWeaponTestRange);
229 	REGISTER_LUA_CFUNC(GetUnitWeaponHaveFreeLineOfFire);
230 	REGISTER_LUA_CFUNC(GetUnitWeaponCanFire);
231 	REGISTER_LUA_CFUNC(GetUnitWeaponTarget);
232 	REGISTER_LUA_CFUNC(GetUnitTravel);
233 	REGISTER_LUA_CFUNC(GetUnitFuel);
234 	REGISTER_LUA_CFUNC(GetUnitEstimatedPath);
235 	REGISTER_LUA_CFUNC(GetUnitLastAttacker);
236 	REGISTER_LUA_CFUNC(GetUnitLastAttackedPiece);
237 	REGISTER_LUA_CFUNC(GetUnitLosState);
238 	REGISTER_LUA_CFUNC(GetUnitSeparation);
239 	REGISTER_LUA_CFUNC(GetUnitDefDimensions);
240 	REGISTER_LUA_CFUNC(GetUnitCollisionVolumeData);
241 	REGISTER_LUA_CFUNC(GetUnitPieceCollisionVolumeData);
242 
243 	REGISTER_LUA_CFUNC(GetUnitBlocking);
244 	REGISTER_LUA_CFUNC(GetUnitMoveTypeData);
245 
246 	REGISTER_LUA_CFUNC(GetUnitCommands);
247 	REGISTER_LUA_CFUNC(GetFactoryCounts);
248 	REGISTER_LUA_CFUNC(GetFactoryCommands);
249 
250 	REGISTER_LUA_CFUNC(GetCommandQueue);
251 	REGISTER_LUA_CFUNC(GetFullBuildQueue);
252 	REGISTER_LUA_CFUNC(GetRealBuildQueue);
253 
254 	REGISTER_LUA_CFUNC(GetUnitCmdDescs);
255 	REGISTER_LUA_CFUNC(FindUnitCmdDesc);
256 
257 	REGISTER_LUA_CFUNC(GetUnitRulesParam);
258 	REGISTER_LUA_CFUNC(GetUnitRulesParams);
259 
260 	REGISTER_LUA_CFUNC(GetAllFeatures);
261 	REGISTER_LUA_CFUNC(GetFeatureDefID);
262 	REGISTER_LUA_CFUNC(GetFeatureTeam);
263 	REGISTER_LUA_CFUNC(GetFeatureAllyTeam);
264 	REGISTER_LUA_CFUNC(GetFeatureHealth);
265 	REGISTER_LUA_CFUNC(GetFeatureHeight);
266 	REGISTER_LUA_CFUNC(GetFeatureRadius);
267 	REGISTER_LUA_CFUNC(GetFeaturePosition);
268 	REGISTER_LUA_CFUNC(GetFeatureDirection);
269 	REGISTER_LUA_CFUNC(GetFeatureHeading);
270 	REGISTER_LUA_CFUNC(GetFeatureResources);
271 	REGISTER_LUA_CFUNC(GetFeatureBlocking);
272 	REGISTER_LUA_CFUNC(GetFeatureNoSelect);
273 	REGISTER_LUA_CFUNC(GetFeatureResurrect);
274 	REGISTER_LUA_CFUNC(GetFeatureCollisionVolumeData);
275 
276 	REGISTER_LUA_CFUNC(GetProjectilePosition);
277 	REGISTER_LUA_CFUNC(GetProjectileDirection);
278 	REGISTER_LUA_CFUNC(GetProjectileVelocity);
279 	REGISTER_LUA_CFUNC(GetProjectileGravity);
280 	REGISTER_LUA_CFUNC(GetProjectileSpinAngle);
281 	REGISTER_LUA_CFUNC(GetProjectileSpinSpeed);
282 	REGISTER_LUA_CFUNC(GetProjectileSpinVec);
283 	REGISTER_LUA_CFUNC(GetPieceProjectileParams);
284 	REGISTER_LUA_CFUNC(GetProjectileTarget);
285 	REGISTER_LUA_CFUNC(GetProjectileType);
286 	REGISTER_LUA_CFUNC(GetProjectileDefID);
287 	REGISTER_LUA_CFUNC(GetProjectileName);
288 
289 	REGISTER_LUA_CFUNC(GetGroundHeight);
290 	REGISTER_LUA_CFUNC(GetGroundOrigHeight);
291 	REGISTER_LUA_CFUNC(GetGroundNormal);
292 	REGISTER_LUA_CFUNC(GetGroundInfo);
293 	REGISTER_LUA_CFUNC(GetGroundBlocked);
294 	REGISTER_LUA_CFUNC(GetGroundExtremes);
295 	REGISTER_LUA_CFUNC(GetTerrainTypeData);
296 
297 	REGISTER_LUA_CFUNC(GetSmoothMeshHeight);
298 
299 	REGISTER_LUA_CFUNC(TestMoveOrder);
300 	REGISTER_LUA_CFUNC(TestBuildOrder);
301 	REGISTER_LUA_CFUNC(Pos2BuildPos);
302 	REGISTER_LUA_CFUNC(GetPositionLosState);
303 	REGISTER_LUA_CFUNC(IsPosInLos);
304 	REGISTER_LUA_CFUNC(IsPosInRadar);
305 	REGISTER_LUA_CFUNC(IsPosInAirLos);
306 	REGISTER_LUA_CFUNC(GetClosestValidPosition);
307 
308 	REGISTER_LUA_CFUNC(GetUnitPieceMap);
309 	REGISTER_LUA_CFUNC(GetUnitPieceList);
310 	REGISTER_LUA_CFUNC(GetUnitPieceInfo);
311 	REGISTER_LUA_CFUNC(GetUnitPiecePosition);
312 	REGISTER_LUA_CFUNC(GetUnitPieceDirection);
313 	REGISTER_LUA_CFUNC(GetUnitPiecePosDir);
314 	REGISTER_LUA_CFUNC(GetUnitPieceMatrix);
315 	REGISTER_LUA_CFUNC(GetUnitScriptPiece);
316 	REGISTER_LUA_CFUNC(GetUnitScriptNames);
317 
318 	REGISTER_LUA_CFUNC(GetRadarErrorParams);
319 
320 	REGISTER_LUA_CFUNC(GetCOBUnitVar);
321 	REGISTER_LUA_CFUNC(GetCOBTeamVar);
322 	REGISTER_LUA_CFUNC(GetCOBAllyTeamVar);
323 	REGISTER_LUA_CFUNC(GetCOBGlobalVar);
324 
325 	if (!LuaMetalMap::PushReadEntries(L))
326 		return false;
327 
328 	if (!LuaPathFinder::PushEntries(L))
329 		return false;
330 
331 	return true;
332 }
333 
334 
335 /******************************************************************************/
336 /******************************************************************************/
337 //
338 //  Access helpers
339 //
340 
IsAlliedTeam(lua_State * L,int team)341 static inline bool IsAlliedTeam(lua_State* L, int team)
342 {
343 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
344 		return CLuaHandle::GetHandleFullRead(L);
345 	}
346 	return (teamHandler->AllyTeam(team) == CLuaHandle::GetHandleReadAllyTeam(L));
347 }
348 
349 
IsAlliedAllyTeam(lua_State * L,int allyTeam)350 static inline bool IsAlliedAllyTeam(lua_State* L, int allyTeam)
351 {
352 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
353 		return CLuaHandle::GetHandleFullRead(L);
354 	}
355 	return (allyTeam == CLuaHandle::GetHandleReadAllyTeam(L));
356 }
357 
358 
IsAllyUnit(lua_State * L,const CUnit * unit)359 static inline bool IsAllyUnit(lua_State* L, const CUnit* unit)
360 {
361 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
362 		return CLuaHandle::GetHandleFullRead(L);
363 	}
364 	return (unit->allyteam == CLuaHandle::GetHandleReadAllyTeam(L));
365 }
366 
367 
IsEnemyUnit(lua_State * L,const CUnit * unit)368 static inline bool IsEnemyUnit(lua_State* L, const CUnit* unit)
369 {
370 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
371 		return !CLuaHandle::GetHandleFullRead(L);
372 	}
373 	return (unit->allyteam != CLuaHandle::GetHandleReadAllyTeam(L));
374 }
375 
376 
IsUnitVisible(lua_State * L,const CUnit * unit)377 static inline bool IsUnitVisible(lua_State* L, const CUnit* unit)
378 {
379 	if (IsAllyUnit(L, unit)) {
380 		return true;
381 	}
382 	return !!(unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)] & (LOS_INLOS | LOS_INRADAR));
383 }
384 
385 
IsUnitInLos(lua_State * L,const CUnit * unit)386 static inline bool IsUnitInLos(lua_State* L, const CUnit* unit)
387 {
388 	if (IsAllyUnit(L, unit)) {
389 		return true;
390 	}
391 	return (unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)] & LOS_INLOS);
392 }
393 
394 
IsUnitTyped(lua_State * L,const CUnit * unit)395 static inline bool IsUnitTyped(lua_State* L, const CUnit* unit)
396 {
397 	if (IsAllyUnit(L, unit)) {
398 		return true;
399 	}
400 	const unsigned short losStatus = unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)];
401 	const unsigned short prevMask = (LOS_PREVLOS | LOS_CONTRADAR);
402 	if ((losStatus & LOS_INLOS) ||
403 	    ((losStatus & prevMask) == prevMask)) {
404 		return true;
405 	}
406 	return false;
407 }
408 
409 
EffectiveUnitDef(lua_State * L,const CUnit * unit)410 static inline const UnitDef* EffectiveUnitDef(lua_State* L, const CUnit* unit)
411 {
412 	const UnitDef* ud = unit->unitDef;
413 	if (IsAllyUnit(L, unit)) {
414 		return ud;
415 	} else if (ud->decoyDef) {
416 		return ud->decoyDef;
417 	} else {
418 		return ud;
419 	}
420 }
421 
422 
IsFeatureVisible(lua_State * L,const CFeature * feature)423 static inline bool IsFeatureVisible(lua_State* L, const CFeature* feature)
424 {
425 	if (CLuaHandle::GetHandleFullRead(L))
426 		return true;
427 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0)
428 		return false;
429 
430 	return feature->IsInLosForAllyTeam(CLuaHandle::GetHandleReadAllyTeam(L));
431 }
432 
IsProjectileVisible(lua_State * L,const ProjectileMapValPair & pp)433 static inline bool IsProjectileVisible(lua_State* L, const ProjectileMapValPair& pp)
434 {
435 	const CProjectile* pro = pp.first;
436 	const int proAllyteam = pp.second;
437 
438 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
439 		return CLuaHandle::GetHandleFullRead(L);
440 	}
441 	if ((CLuaHandle::GetHandleReadAllyTeam(L) != proAllyteam) &&
442 	    (!losHandler->InLos(pro->pos, CLuaHandle::GetHandleReadAllyTeam(L)))) {
443 		return false;
444 	}
445 	return true;
446 }
447 
448 
449 /******************************************************************************/
450 /******************************************************************************/
451 
452 
PushCollisionVolumeData(lua_State * L,const CollisionVolume * vol)453 static int PushCollisionVolumeData(lua_State* L, const CollisionVolume* vol) {
454 	lua_pushnumber(L, vol->GetScales().x);
455 	lua_pushnumber(L, vol->GetScales().y);
456 	lua_pushnumber(L, vol->GetScales().z);
457 	lua_pushnumber(L, vol->GetOffsets().x);
458 	lua_pushnumber(L, vol->GetOffsets().y);
459 	lua_pushnumber(L, vol->GetOffsets().z);
460 	lua_pushnumber(L, vol->GetVolumeType());
461 	lua_pushnumber(L, int(vol->UseContHitTest()));
462 	lua_pushnumber(L, vol->GetPrimaryAxis());
463 	lua_pushboolean(L, vol->IgnoreHits());
464 	return 10;
465 }
466 
PushTerrainTypeData(lua_State * L,const CMapInfo::TerrainType * tt,bool groundInfo)467 static int PushTerrainTypeData(lua_State* L, const CMapInfo::TerrainType* tt, bool groundInfo) {
468 	lua_pushsstring(L, tt->name);
469 
470 	if (groundInfo) {
471 		assert(lua_isnumber(L, 1));
472 		assert(lua_isnumber(L, 2));
473 		// WTF is this still doing here?
474 		LuaMetalMap::GetMetalAmount(L);
475 	}
476 
477 	lua_pushnumber(L, tt->hardness);
478 	lua_pushnumber(L, tt->tankSpeed);
479 	lua_pushnumber(L, tt->kbotSpeed);
480 	lua_pushnumber(L, tt->hoverSpeed);
481 	lua_pushnumber(L, tt->shipSpeed);
482 	lua_pushboolean(L, tt->receiveTracks);
483 	return (7 + int(groundInfo));
484 }
485 
GetWorldObjectVelocity(lua_State * L,const CWorldObject * o,bool isFeature)486 static int GetWorldObjectVelocity(lua_State* L, const CWorldObject* o, bool isFeature)
487 {
488 	if (o == NULL)
489 		return 0;
490 	if (isFeature && !IsFeatureVisible(L, static_cast<const CFeature*>(o)))
491 		return 0;
492 
493 	lua_pushnumber(L, o->speed.x);
494 	lua_pushnumber(L, o->speed.y);
495 	lua_pushnumber(L, o->speed.z);
496 	lua_pushnumber(L, o->speed.w);
497 	return 4;
498 }
499 
GetSolidObjectPosition(lua_State * L,const CSolidObject * o,bool isFeature)500 static int GetSolidObjectPosition(lua_State* L, const CSolidObject* o, bool isFeature)
501 {
502 	if (o == NULL)
503 		return 0;
504 
505 	// no error for features
506 	float3 errorVec;
507 
508 	if (isFeature) {
509 		if (!IsFeatureVisible(L, static_cast<const CFeature*>(o))) {
510 			return 0;
511 		}
512 	} else {
513 		if (!IsAllyUnit(L, static_cast<const CUnit*>(o))) {
514 			errorVec += static_cast<const CUnit*>(o)->GetErrorPos(CLuaHandle::GetHandleReadAllyTeam(L));
515 			errorVec -= o->midPos;
516 		}
517 	}
518 
519 	// NOTE:
520 	//   must be called before any pushing to the stack,
521 	//   else in case of noneornil it will read the pushed items.
522 	const bool returnMidPos = luaL_optboolean(L, 2, false);
523 	const bool returnAimPos = luaL_optboolean(L, 3, false);
524 
525 	// base-position
526 	lua_pushnumber(L, o->pos.x + errorVec.x);
527 	lua_pushnumber(L, o->pos.y + errorVec.y);
528 	lua_pushnumber(L, o->pos.z + errorVec.z);
529 
530 	if (returnMidPos) {
531 		lua_pushnumber(L, o->midPos.x + errorVec.x);
532 		lua_pushnumber(L, o->midPos.y + errorVec.y);
533 		lua_pushnumber(L, o->midPos.z + errorVec.z);
534 	}
535 	if (returnAimPos) {
536 		lua_pushnumber(L, o->aimPos.x + errorVec.x);
537 		lua_pushnumber(L, o->aimPos.y + errorVec.y);
538 		lua_pushnumber(L, o->aimPos.z + errorVec.z);
539 	}
540 
541 	return (3 + (3 * returnMidPos) + (3 * returnAimPos));
542 }
543 
GetSolidObjectBlocking(lua_State * L,const CSolidObject * o)544 static int GetSolidObjectBlocking(lua_State* L, const CSolidObject* o)
545 {
546 	if (o == NULL)
547 		return 0;
548 
549 	lua_pushboolean(L, o->HasPhysicalStateBit(CSolidObject::PSTATE_BIT_BLOCKING));
550 	lua_pushboolean(L, o->HasCollidableStateBit(CSolidObject::CSTATE_BIT_SOLIDOBJECTS));
551 	lua_pushboolean(L, o->HasCollidableStateBit(CSolidObject::CSTATE_BIT_PROJECTILES ));
552 	lua_pushboolean(L, o->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS ));
553 
554 	lua_pushboolean(L, o->crushable);
555 	lua_pushboolean(L, o->blockEnemyPushing);
556 	lua_pushboolean(L, o->blockHeightChanges);
557 
558 	return 7;
559 }
560 
561 
562 /******************************************************************************/
563 /******************************************************************************/
564 //
565 //  Parsing helpers
566 //
567 
ParseRawUnit(lua_State * L,const char * caller,int index)568 static inline CUnit* ParseRawUnit(lua_State* L, const char* caller, int index)
569 {
570 	if (!lua_isnumber(L, index)) {
571 		if (caller != NULL) {
572 			luaL_error(L, "Bad unitID parameter in %s()\n", caller);
573 		} else {
574 			return NULL;
575 		}
576 	}
577 	const int unitID = lua_toint(L, index);
578 	if ((unitID < 0) || (static_cast<size_t>(unitID) >= unitHandler->MaxUnits())) {
579 		if (caller != NULL) {
580 			luaL_error(L, "%s(): Bad unitID: %d\n", caller, unitID);
581 		} else {
582 			return NULL;
583 		}
584 	}
585 
586 	return (unitHandler->GetUnit(unitID));
587 }
588 
589 
ParseUnit(lua_State * L,const char * caller,int index)590 static inline CUnit* ParseUnit(lua_State* L, const char* caller, int index)
591 {
592 	CUnit* unit = ParseRawUnit(L, caller, index);
593 	if (unit == NULL) {
594 		return NULL;
595 	}
596 	return IsUnitVisible(L, unit) ? unit : NULL;
597 }
598 
599 
ParseAllyUnit(lua_State * L,const char * caller,int index)600 static inline CUnit* ParseAllyUnit(lua_State* L, const char* caller, int index)
601 {
602 	CUnit* unit = ParseRawUnit(L, caller, index);
603 	if (unit == NULL) {
604 		return NULL;
605 	}
606 	return IsAllyUnit(L, unit) ? unit : NULL;
607 }
608 
609 
ParseInLosUnit(lua_State * L,const char * caller,int index)610 static inline CUnit* ParseInLosUnit(lua_State* L, const char* caller, int index)
611 {
612 	CUnit* unit = ParseRawUnit(L, caller, index);
613 	if (unit == NULL) {
614 		return NULL;
615 	}
616 	return IsUnitInLos(L, unit) ? unit : NULL;
617 }
618 
619 
ParseTypedUnit(lua_State * L,const char * caller,int index)620 static inline CUnit* ParseTypedUnit(lua_State* L, const char* caller, int index)
621 {
622 	CUnit* unit = ParseRawUnit(L, caller, index);
623 	if (unit == NULL) {
624 		return NULL;
625 	}
626 	return IsUnitTyped(L, unit) ? unit : NULL;
627 }
628 
629 
ParseFeature(lua_State * L,const char * caller,int index)630 static CFeature* ParseFeature(lua_State* L, const char* caller, int index)
631 {
632 	if (!lua_isnumber(L, index)) {
633 		if (caller != NULL) {
634 			luaL_error(L, "Incorrect arguments to %s(featureID)", caller);
635 		} else {
636 			return NULL;
637 		}
638 	}
639 	const int featureID = lua_toint(L, index);
640 	CFeature* feature = featureHandler->GetFeature(featureID);
641 
642 	if (!feature) {
643 		return NULL;
644 	}
645 
646 	if (!IsFeatureVisible(L, feature)) {
647 		return NULL;
648 	}
649 	return feature;
650 }
651 
652 
ParseProjectile(lua_State * L,const char * caller,int index)653 static CProjectile* ParseProjectile(lua_State* L, const char* caller, int index)
654 {
655 	const int proID = luaL_checkint(L, index);
656 	const ProjectileMapValPair* pp = projectileHandler->GetMapPairBySyncedID(proID);
657 	if (!pp) {
658 		return NULL;
659 	}
660 	return IsProjectileVisible(L, *pp)? pp->first: NULL;
661 }
662 
663 
664 /******************************************************************************/
665 
666 
ParseTeam(lua_State * L,const char * caller,int index)667 static inline CTeam* ParseTeam(lua_State* L, const char* caller, int index)
668 {
669 	const int teamID = luaL_checkint(L, index);
670 	if (!teamHandler->IsValidTeam(teamID)) {
671 		luaL_error(L, "Bad teamID in %s\n", caller);
672 	}
673 	return teamHandler->Team(teamID);
674 }
675 
676 
ParseFloatArray(lua_State * L,float * array,int size)677 static int ParseFloatArray(lua_State* L, float* array, int size)
678 {
679 	if (!lua_istable(L, -1)) {
680 		return -1;
681 	}
682 	// FIXME: changed this, test GetUnitsInPlanes() ...
683 	const int table = lua_gettop(L);
684 	for (int i = 0; i < size; i++) {
685 		lua_rawgeti(L, table, (i + 1));
686 		if (lua_isnumber(L, -1)) {
687 			array[i] = lua_tofloat(L, -1);
688 			lua_pop(L, 1);
689 		} else {
690 			lua_pop(L, 1);
691 			return i;
692 		}
693 	}
694 	return size;
695 }
696 
697 
698 /******************************************************************************/
699 
PushRulesParams(lua_State * L,const char * caller,const LuaRulesParams::Params & params,const LuaRulesParams::HashMap & paramsMap,const int losStatus)700 static int PushRulesParams(lua_State* L, const char* caller,
701                           const LuaRulesParams::Params& params,
702                           const LuaRulesParams::HashMap& paramsMap,
703                           const int losStatus)
704 {
705 	lua_newtable(L);
706 	const int pCount = (int)params.size();
707 	for (int i = 0; i < pCount; i++) {
708 		const LuaRulesParams::Param& param = params[i];
709 		if (!(param.los & losStatus))
710 			continue;
711 
712 		lua_pushnumber(L, i + 1);
713 		lua_newtable(L);
714 
715 		LuaRulesParams::HashMap::const_iterator it;
716 		string name = "";
717 		for (it = paramsMap.begin(); it != paramsMap.end(); ++it) {
718 			if (it->second == i) {
719 				name = it->first;
720 				break;
721 			}
722 		}
723 
724 		if (!param.valueString.empty()) {
725 			LuaPushNamedString(L, name, param.valueString);
726 		} else {
727 			LuaPushNamedNumber(L, name, param.valueInt);
728 		}
729 		lua_rawset(L, -3);
730 	}
731 
732 	// <i> is not consecutive due to the "continue"
733 	hs_n.PushNumber(L, pCount);
734 
735 	return 1;
736 }
737 
738 
GetRulesParam(lua_State * L,const char * caller,int index,const LuaRulesParams::Params & params,const LuaRulesParams::HashMap & paramsMap,const int & losStatus)739 static int GetRulesParam(lua_State* L, const char* caller, int index,
740                           const LuaRulesParams::Params& params,
741                           const LuaRulesParams::HashMap& paramsMap,
742                           const int& losStatus)
743 {
744 	int pIndex = -1;
745 
746 	if (lua_israwnumber(L, index)) {
747 		pIndex = lua_toint(L, index) - 1;
748 	}
749 	else if (lua_israwstring(L, index)) {
750 		const string pName = lua_tostring(L, index);
751 		LuaRulesParams::HashMap::const_iterator it = paramsMap.find(pName);
752 		if (it != paramsMap.end()) {
753 			pIndex = it->second;
754 		}
755 	}
756 	else {
757 		luaL_error(L, "Incorrect arguments to %s()", caller);
758 	}
759 
760 	if ((pIndex < 0) || (pIndex >= (int)params.size())) {
761 		return 0;
762 	}
763 
764 	const LuaRulesParams::Param& param = params[pIndex];
765 
766 	if (param.los & losStatus) {
767 		if (!param.valueString.empty()) {
768 			lua_pushsstring(L, param.valueString);
769 		} else {
770 			lua_pushnumber(L, param.valueInt);
771 		}
772 		return 1;
773 	}
774 
775 	return 0;
776 }
777 
778 
779 /******************************************************************************/
780 /******************************************************************************/
781 //
782 // The call-outs
783 //
784 
IsCheatingEnabled(lua_State * L)785 int LuaSyncedRead::IsCheatingEnabled(lua_State* L)
786 {
787 	lua_pushboolean(L, gs->cheatEnabled);
788 	return 1;
789 }
790 
791 
IsGodModeEnabled(lua_State * L)792 int LuaSyncedRead::IsGodModeEnabled(lua_State* L)
793 {
794 	lua_pushboolean(L, gs->godMode);
795 	return 1;
796 }
797 
798 
IsDevLuaEnabled(lua_State * L)799 int LuaSyncedRead::IsDevLuaEnabled(lua_State* L)
800 {
801 	lua_pushboolean(L, CLuaHandle::GetDevMode());
802 	return 1;
803 }
804 
805 
IsEditDefsEnabled(lua_State * L)806 int LuaSyncedRead::IsEditDefsEnabled(lua_State* L)
807 {
808 	lua_pushboolean(L, gs->editDefsEnabled);
809 	return 1;
810 }
811 
812 
AreHelperAIsEnabled(lua_State * L)813 int LuaSyncedRead::AreHelperAIsEnabled(lua_State* L)
814 {
815 	if (!game) {
816 		return 0;
817 	}
818 	lua_pushboolean(L, !gs->noHelperAIs);
819 	return 1;
820 }
821 
822 
FixedAllies(lua_State * L)823 int LuaSyncedRead::FixedAllies(lua_State* L)
824 {
825 	if (!game) {
826 		return 0;
827 	}
828 	lua_pushboolean(L, !(gameSetup != NULL && !gameSetup->fixedAllies));
829 	return 1;
830 }
831 
832 
IsGameOver(lua_State * L)833 int LuaSyncedRead::IsGameOver(lua_State* L)
834 {
835 	lua_pushboolean(L, game->IsGameOver());
836 	return 1;
837 }
838 
839 
GetGaiaTeamID(lua_State * L)840 int LuaSyncedRead::GetGaiaTeamID(lua_State* L)
841 {
842 	if (!gs->useLuaGaia) {
843 		return 0;
844 	}
845 	lua_pushnumber(L, teamHandler->GaiaTeamID());
846 	return 1;
847 }
848 
849 
GetGameFrame(lua_State * L)850 int LuaSyncedRead::GetGameFrame(lua_State* L)
851 {
852 	const int dayFrames = GAME_SPEED * (24 * 60 * 60);
853 	lua_pushnumber(L, gs->frameNum % dayFrames);
854 	lua_pushnumber(L, gs->frameNum / dayFrames);
855 	return 2;
856 }
857 
858 
GetGameSeconds(lua_State * L)859 int LuaSyncedRead::GetGameSeconds(lua_State* L)
860 {
861 	const float seconds = gs->frameNum / (float)GAME_SPEED;
862 	lua_pushnumber(L, seconds);
863 	return 1;
864 }
865 
866 
GetWind(lua_State * L)867 int LuaSyncedRead::GetWind(lua_State* L)
868 {
869 	lua_pushnumber(L, wind.GetCurrentWind().x);
870 	lua_pushnumber(L, wind.GetCurrentWind().y);
871 	lua_pushnumber(L, wind.GetCurrentWind().z);
872 	lua_pushnumber(L, wind.GetCurrentStrength());
873 	lua_pushnumber(L, wind.GetCurrentDirection().x);
874 	lua_pushnumber(L, wind.GetCurrentDirection().y);
875 	lua_pushnumber(L, wind.GetCurrentDirection().z);
876 	return 7;
877 }
878 
879 
880 /******************************************************************************/
881 
GetGameRulesParams(lua_State * L)882 int LuaSyncedRead::GetGameRulesParams(lua_State* L)
883 {
884 	const LuaRulesParams::Params&  params    = CLuaHandleSynced::GetGameParams();
885 	const LuaRulesParams::HashMap& paramsMap = CLuaHandleSynced::GetGameParamsMap();
886 
887 	//! always readable for all
888 	const int losMask = LuaRulesParams::RULESPARAMLOS_PRIVATE_MASK;
889 
890 	return PushRulesParams(L, __FUNCTION__, params, paramsMap, losMask);
891 }
892 
893 
GetGameRulesParam(lua_State * L)894 int LuaSyncedRead::GetGameRulesParam(lua_State* L)
895 {
896 	const LuaRulesParams::Params&  params    = CLuaHandleSynced::GetGameParams();
897 	const LuaRulesParams::HashMap& paramsMap = CLuaHandleSynced::GetGameParamsMap();
898 
899 	//! always readable for all
900 	const int losMask = LuaRulesParams::RULESPARAMLOS_PRIVATE_MASK;
901 
902 	return GetRulesParam(L, __FUNCTION__, 1, params, paramsMap, losMask);
903 }
904 
905 
906 /******************************************************************************/
907 
GetMapOptions(lua_State * L)908 int LuaSyncedRead::GetMapOptions(lua_State* L)
909 {
910 	lua_newtable(L);
911 
912 	const map<string, string>& mapOpts = CGameSetup::GetMapOptions();
913 	map<string, string>::const_iterator it;
914 	for (it = mapOpts.begin(); it != mapOpts.end(); ++it) {
915 		lua_pushsstring(L, it->first);
916 		lua_pushsstring(L, it->second);
917 		lua_rawset(L, -3);
918 	}
919 
920 	return 1;
921 }
922 
923 
GetModOptions(lua_State * L)924 int LuaSyncedRead::GetModOptions(lua_State* L)
925 {
926 	lua_newtable(L);
927 
928 	const map<string, string>& modOpts = CGameSetup::GetModOptions();
929 	map<string, string>::const_iterator it;
930 	for (it = modOpts.begin(); it != modOpts.end(); ++it) {
931 		lua_pushsstring(L, it->first);
932 		lua_pushsstring(L, it->second);
933 		lua_rawset(L, -3);
934 	}
935 
936 	return 1;
937 }
938 
939 
940 /******************************************************************************/
941 
GetHeadingFromVector(lua_State * L)942 int LuaSyncedRead::GetHeadingFromVector(lua_State* L)
943 {
944 	const float x = luaL_checkfloat(L, 1);
945 	const float z = luaL_checkfloat(L, 2);
946 	const short int heading = ::GetHeadingFromVector(x, z);
947 	lua_pushnumber(L, heading);
948 	return 1;
949 }
950 
951 
GetVectorFromHeading(lua_State * L)952 int LuaSyncedRead::GetVectorFromHeading(lua_State* L)
953 {
954 	const short int h = (short int)luaL_checknumber(L, 1);
955 	const float3& vec = ::GetVectorFromHeading(h);
956 	lua_pushnumber(L, vec.x);
957 	lua_pushnumber(L, vec.z);
958 	return 2;
959 }
960 
961 
962 /******************************************************************************/
963 
GetSideData(lua_State * L)964 int LuaSyncedRead::GetSideData(lua_State* L)
965 {
966 	if (lua_israwstring(L, 1)) {
967 		const string sideName = lua_tostring(L, 1);
968 		const string& startUnit = sideParser.GetStartUnit(sideName);
969 		const string& caseName  = sideParser.GetCaseName(sideName);
970 		if (startUnit.empty()) {
971 			return 0;
972 		}
973 		lua_pushsstring(L, startUnit);
974 		lua_pushsstring(L, caseName);
975 		return 2;
976 	}
977 	else if (lua_israwnumber(L, 1)) {
978 		const unsigned int index = lua_toint(L, 1) - 1;
979 		if (!sideParser.ValidSide(index)) {
980 			return 0;
981 		}
982 		lua_pushsstring(L, sideParser.GetSideName(index));
983 		lua_pushsstring(L, sideParser.GetStartUnit(index));
984 		lua_pushsstring(L, sideParser.GetCaseName(index));
985 		return 3;
986 	}
987 	else {
988 		lua_newtable(L);
989 		const unsigned int sideCount = sideParser.GetCount();
990 		for (unsigned int i = 0; i < sideCount; i++) {
991 			lua_newtable(L); {
992 				LuaPushNamedString(L, "sideName",  sideParser.GetSideName(i));
993 				LuaPushNamedString(L, "caseName",  sideParser.GetCaseName(i));
994 				LuaPushNamedString(L, "startUnit", sideParser.GetStartUnit(i));
995 			}
996 			lua_rawseti(L, -2, i + 1);
997 		}
998 		return 1;
999 	}
1000 	return 0;
1001 }
1002 
1003 
1004 /******************************************************************************/
1005 
GetAllyTeamStartBox(lua_State * L)1006 int LuaSyncedRead::GetAllyTeamStartBox(lua_State* L)
1007 {
1008 	const std::vector<AllyTeam>& allyData = CGameSetup::GetAllyStartingData();
1009 	const unsigned int allyTeam = luaL_checkint(L, 1);
1010 
1011 	if (allyTeam >= allyData.size())
1012 		return 0;
1013 
1014 	const float xmin = (gs->mapx * SQUARE_SIZE) * allyData[allyTeam].startRectLeft;
1015 	const float zmin = (gs->mapy * SQUARE_SIZE) * allyData[allyTeam].startRectTop;
1016 	const float xmax = (gs->mapx * SQUARE_SIZE) * allyData[allyTeam].startRectRight;
1017 	const float zmax = (gs->mapy * SQUARE_SIZE) * allyData[allyTeam].startRectBottom;
1018 
1019 	lua_pushnumber(L, xmin);
1020 	lua_pushnumber(L, zmin);
1021 	lua_pushnumber(L, xmax);
1022 	lua_pushnumber(L, zmax);
1023 	return 4;
1024 }
1025 
1026 
GetTeamStartPosition(lua_State * L)1027 int LuaSyncedRead::GetTeamStartPosition(lua_State* L)
1028 {
1029 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1030 
1031 	if (team == NULL)
1032 		return 0;
1033 	if (!IsAlliedTeam(L, team->teamNum))
1034 		return 0;
1035 
1036 	const float3& pos = team->GetStartPos();
1037 
1038 	lua_pushnumber(L, pos.x);
1039 	lua_pushnumber(L, pos.y);
1040 	lua_pushnumber(L, pos.z);
1041 	lua_pushboolean(L, team->HasValidStartPos());
1042 	return 4;
1043 }
1044 
1045 
1046 /******************************************************************************/
1047 
GetAllyTeamList(lua_State * L)1048 int LuaSyncedRead::GetAllyTeamList(lua_State* L)
1049 {
1050 	lua_newtable(L);
1051 	int count = 1;
1052 	for (int at = 0; at < teamHandler->ActiveAllyTeams(); at++) {
1053 		lua_pushnumber(L, at);
1054 		lua_rawseti(L, -2, count++);
1055 	}
1056 
1057 	return 1;
1058 }
1059 
1060 
GetTeamList(lua_State * L)1061 int LuaSyncedRead::GetTeamList(lua_State* L)
1062 {
1063 	const int args = lua_gettop(L); // number of arguments
1064 	if ((args != 0) && ((args != 1) || !lua_isnumber(L, 1))) {
1065 		luaL_error(L, "Incorrect arguments to GetTeamList([allyTeamID])");
1066 	}
1067 
1068 	int allyTeamID = -1;
1069 	if (args == 1) {
1070 		allyTeamID = lua_toint(L, 1);
1071 		if (!teamHandler->IsValidAllyTeam(allyTeamID)) {
1072 			return 0;
1073 		}
1074 	}
1075 
1076 	lua_newtable(L);
1077 	int count = 1;
1078 	for (int t = 0; t < teamHandler->ActiveTeams(); t++) {
1079 		if (teamHandler->Team(t) == NULL) {
1080 			continue;
1081 		}
1082 		if ((allyTeamID < 0) || (allyTeamID == teamHandler->AllyTeam(t))) {
1083 			lua_pushnumber(L, t);
1084 			lua_rawseti(L, -2, count++);
1085 		}
1086 	}
1087 
1088 	return 1;
1089 }
1090 
1091 
GetPlayerList(lua_State * L)1092 int LuaSyncedRead::GetPlayerList(lua_State* L)
1093 {
1094 	int teamID = -1;
1095 	bool active = false;
1096 
1097 	if (lua_isnumber(L, 1)) {
1098 		teamID = lua_toint(L, 1);
1099 		if (lua_isboolean(L, 2)) {
1100 			active = lua_toboolean(L, 2);
1101 		}
1102 	}
1103 	else if (lua_isboolean(L, 1)) {
1104 		active = lua_toboolean(L, 1);
1105 		if (lua_isnumber(L, 2)) {
1106 			teamID = lua_toint(L, 2);
1107 		}
1108 	}
1109 
1110 	if (teamID >= teamHandler->ActiveTeams()) {
1111 		return 0;
1112 	}
1113 
1114 	lua_newtable(L);
1115 	int count = 1;
1116 	for (int p = 0; p < playerHandler->ActivePlayers(); p++) {
1117 		const CPlayer* player = playerHandler->Player(p);
1118 		if (player == NULL) {
1119 			continue;
1120 		}
1121 		if (active && !player->active) {
1122 			continue;
1123 		}
1124 		if ((teamID < 0) || (player->team == teamID)) {
1125 			lua_pushnumber(L, p);
1126 			lua_rawseti(L, -2, count++);
1127 		}
1128 	}
1129 
1130 	return 1;
1131 }
1132 
1133 
1134 /******************************************************************************/
1135 
GetTeamInfo(lua_State * L)1136 int LuaSyncedRead::GetTeamInfo(lua_State* L)
1137 {
1138 	const int teamID = luaL_checkint(L, 1);
1139 	if (!teamHandler->IsValidTeam(teamID)) {
1140 		return 0;
1141 	}
1142 	const CTeam* team = teamHandler->Team(teamID);
1143 	if (team == NULL) {
1144 		return 0;
1145 	}
1146 
1147 	lua_pushnumber(L,  team->teamNum);
1148 	lua_pushnumber(L,  team->GetLeader());
1149 	lua_pushboolean(L, team->isDead);
1150 	lua_pushboolean(L, !skirmishAIHandler.GetSkirmishAIsInTeam(teamID).empty()); // hasAIs
1151 	lua_pushsstring(L, team->GetSide());
1152 	lua_pushnumber(L,  teamHandler->AllyTeam(team->teamNum));
1153 
1154 	lua_newtable(L);
1155 	const TeamBase::customOpts& popts(team->GetAllValues());
1156 	for (TeamBase::customOpts::const_iterator it = popts.begin(); it != popts.end(); ++it) {
1157 		lua_pushsstring(L, it->first);
1158 		lua_pushsstring(L, it->second);
1159 		lua_rawset(L, -3);
1160 	}
1161 	lua_pushnumber(L, team->GetIncomeMultiplier());
1162 	return 8;
1163 }
1164 
1165 
GetTeamResources(lua_State * L)1166 int LuaSyncedRead::GetTeamResources(lua_State* L)
1167 {
1168 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1169 	if (team == NULL) {
1170 		return 0;
1171 	}
1172 	const int teamID = team->teamNum;
1173 
1174 	if (!IsAlliedTeam(L, teamID)) {
1175 		return 0;
1176 	}
1177 
1178 	const string type = luaL_checkstring(L, 2);
1179 	if (type == "metal") {
1180 		lua_pushnumber(L, team->metal);
1181 		lua_pushnumber(L, team->metalStorage);
1182 		lua_pushnumber(L, team->prevMetalPull);
1183 		lua_pushnumber(L, team->prevMetalIncome);
1184 		lua_pushnumber(L, team->prevMetalExpense);
1185 		lua_pushnumber(L, team->metalShare);
1186 		lua_pushnumber(L, team->prevMetalSent);
1187 		lua_pushnumber(L, team->prevMetalReceived);
1188 		lua_pushnumber(L, team->prevMetalExcess);
1189 		return 9;
1190 	}
1191 	else if (type == "energy") {
1192 		lua_pushnumber(L, team->energy);
1193 		lua_pushnumber(L, team->energyStorage);
1194 		lua_pushnumber(L, team->prevEnergyPull);
1195 		lua_pushnumber(L, team->prevEnergyIncome);
1196 		lua_pushnumber(L, team->prevEnergyExpense);
1197 		lua_pushnumber(L, team->energyShare);
1198 		lua_pushnumber(L, team->prevEnergySent);
1199 		lua_pushnumber(L, team->prevEnergyReceived);
1200 		lua_pushnumber(L, team->prevEnergyExcess);
1201 		return 9;
1202 	}
1203 
1204 	return 0;
1205 }
1206 
1207 
GetTeamUnitStats(lua_State * L)1208 int LuaSyncedRead::GetTeamUnitStats(lua_State* L)
1209 {
1210 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1211 	if (team == NULL) {
1212 		return 0;
1213 	}
1214 	const int teamID = team->teamNum;
1215 
1216 	if (!IsAlliedTeam(L, teamID) && !game->IsGameOver()) {
1217 		return 0;
1218 	}
1219 
1220 	const CTeam::Statistics& stats = *team->currentStats;
1221 	lua_pushnumber(L, stats.unitsKilled);
1222 	lua_pushnumber(L, stats.unitsDied);
1223 	lua_pushnumber(L, stats.unitsCaptured);
1224 	lua_pushnumber(L, stats.unitsOutCaptured);
1225 	lua_pushnumber(L, stats.unitsReceived);
1226 	lua_pushnumber(L, stats.unitsSent);
1227 
1228 	return 6;
1229 }
1230 
1231 
GetTeamResourceStats(lua_State * L)1232 int LuaSyncedRead::GetTeamResourceStats(lua_State* L)
1233 {
1234 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1235 	if (team == NULL) {
1236 		return 0;
1237 	}
1238 	const int teamID = team->teamNum;
1239 
1240 	if (!IsAlliedTeam(L, teamID) && !game->IsGameOver()) {
1241 		return 0;
1242 	}
1243 
1244 	const CTeam::Statistics& stats = *team->currentStats;
1245 
1246 	const string type = luaL_checkstring(L, 2);
1247 	if (type == "metal") {
1248 		lua_pushnumber(L, stats.metalUsed);
1249 		lua_pushnumber(L, stats.metalProduced);
1250 		lua_pushnumber(L, stats.metalExcess);
1251 		lua_pushnumber(L, stats.metalReceived);
1252 		lua_pushnumber(L, stats.metalSent);
1253 		return 5;
1254 	}
1255 	else if (type == "energy") {
1256 		lua_pushnumber(L, stats.energyUsed);
1257 		lua_pushnumber(L, stats.energyProduced);
1258 		lua_pushnumber(L, stats.energyExcess);
1259 		lua_pushnumber(L, stats.energyReceived);
1260 		lua_pushnumber(L, stats.energySent);
1261 		return 5;
1262 	}
1263 
1264 	return 0;
1265 }
1266 
1267 
GetTeamRulesParams(lua_State * L)1268 int LuaSyncedRead::GetTeamRulesParams(lua_State* L)
1269 {
1270 	CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1271 	if (team == NULL) {
1272 		return 0;
1273 	}
1274 
1275 	int losMask = LuaRulesParams::RULESPARAMLOS_PUBLIC;
1276 
1277 	if (IsAlliedTeam(L, team->teamNum) || game->IsGameOver()) {
1278 		losMask |= LuaRulesParams::RULESPARAMLOS_PRIVATE_MASK;
1279 	}
1280 	else if (teamHandler->AlliedTeams(team->teamNum, CLuaHandle::GetHandleReadTeam(L)) || ((CLuaHandle::GetHandleReadAllyTeam(L) < 0) && CLuaHandle::GetHandleFullRead(L))) {
1281 		losMask |= LuaRulesParams::RULESPARAMLOS_ALLIED_MASK;
1282 	}
1283 
1284 	const LuaRulesParams::Params&  params    = team->modParams;
1285 	const LuaRulesParams::HashMap& paramsMap = team->modParamsMap;
1286 
1287 	return PushRulesParams(L, __FUNCTION__, params, paramsMap, losMask);
1288 }
1289 
1290 
GetTeamRulesParam(lua_State * L)1291 int LuaSyncedRead::GetTeamRulesParam(lua_State* L)
1292 {
1293 	CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1294 	if (team == NULL) {
1295 		return 0;
1296 	}
1297 
1298 	int losMask = LuaRulesParams::RULESPARAMLOS_PUBLIC;
1299 
1300 	if (IsAlliedTeam(L, team->teamNum) || game->IsGameOver()) {
1301 		losMask |= LuaRulesParams::RULESPARAMLOS_PRIVATE_MASK;
1302 	}
1303 	else if (teamHandler->AlliedTeams(team->teamNum, CLuaHandle::GetHandleReadTeam(L)) || ((CLuaHandle::GetHandleReadAllyTeam(L) < 0) && CLuaHandle::GetHandleFullRead(L))) {
1304 		losMask |= LuaRulesParams::RULESPARAMLOS_ALLIED_MASK;
1305 	}
1306 
1307 	const LuaRulesParams::Params&  params    = team->modParams;
1308 	const LuaRulesParams::HashMap& paramsMap = team->modParamsMap;
1309 
1310 	return GetRulesParam(L, __FUNCTION__, 2, params, paramsMap, losMask);
1311 }
1312 
1313 
GetTeamStatsHistory(lua_State * L)1314 int LuaSyncedRead::GetTeamStatsHistory(lua_State* L)
1315 {
1316 	CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1317 	if (team == NULL) {
1318 		return 0;
1319 	}
1320 	const int teamID = team->teamNum;
1321 
1322 	if (!IsAlliedTeam(L, teamID) && !game->IsGameOver()) {
1323 		return 0;
1324 	}
1325 
1326 	const int args = lua_gettop(L);
1327 	if (args == 1) {
1328 		lua_pushnumber(L, team->statHistory.size());
1329 		return 1;
1330 	}
1331 
1332 	const std::list<CTeam::Statistics>& teamStats = team->statHistory;
1333 	std::list<CTeam::Statistics>::const_iterator it = teamStats.begin();
1334 	const int statCount = teamStats.size();
1335 
1336 	int start = 0;
1337 	if ((args >= 2) && lua_isnumber(L, 2)) {
1338 		start = lua_toint(L, 2) - 1;
1339 		start = max(0, min(statCount - 1, start));
1340 	}
1341 
1342 	int end = start;
1343 	if ((args >= 3) && lua_isnumber(L, 3)) {
1344 		end = lua_toint(L, 3) - 1;
1345 		end = max(0, min(statCount - 1, end));
1346 	}
1347 
1348 	std::advance(it, start);
1349 
1350 	lua_newtable(L);
1351 	if (statCount > 0) {
1352 		int count = 1;
1353 		for (int i = start; i <= end; ++i, ++it) {
1354 			const CTeam::Statistics& stats = *it;
1355 			lua_newtable(L); {
1356 				if (i+1 == teamStats.size()) {
1357 					//! the `stats.frame` var indicates the frame when a new entry needs to get added,
1358 					//! for the most recent stats entry this lies obviously in the future,
1359 					//! so we just output the current frame here
1360 					HSTR_PUSH_NUMBER(L, "time",             gs->frameNum / GAME_SPEED);
1361 					HSTR_PUSH_NUMBER(L, "frame",            gs->frameNum);
1362 				} else {
1363 					HSTR_PUSH_NUMBER(L, "time",             stats.frame / GAME_SPEED);
1364 					HSTR_PUSH_NUMBER(L, "frame",            stats.frame);
1365 				}
1366 				HSTR_PUSH_NUMBER(L, "metalUsed",        stats.metalUsed);
1367 				HSTR_PUSH_NUMBER(L, "metalProduced",    stats.metalProduced);
1368 				HSTR_PUSH_NUMBER(L, "metalExcess",      stats.metalExcess);
1369 				HSTR_PUSH_NUMBER(L, "metalReceived",    stats.metalReceived);
1370 				HSTR_PUSH_NUMBER(L, "metalSent",        stats.metalSent);
1371 
1372 				HSTR_PUSH_NUMBER(L, "energyUsed",       stats.energyUsed);
1373 				HSTR_PUSH_NUMBER(L, "energyProduced",   stats.energyProduced);
1374 				HSTR_PUSH_NUMBER(L, "energyExcess",     stats.energyExcess);
1375 				HSTR_PUSH_NUMBER(L, "energyReceived",   stats.energyReceived);
1376 				HSTR_PUSH_NUMBER(L, "energySent",       stats.energySent);
1377 
1378 				HSTR_PUSH_NUMBER(L, "damageDealt",      stats.damageDealt);
1379 				HSTR_PUSH_NUMBER(L, "damageReceived",   stats.damageReceived);
1380 
1381 				HSTR_PUSH_NUMBER(L, "unitsProduced",    stats.unitsProduced);
1382 				HSTR_PUSH_NUMBER(L, "unitsDied",        stats.unitsDied);
1383 				HSTR_PUSH_NUMBER(L, "unitsReceived",    stats.unitsReceived);
1384 				HSTR_PUSH_NUMBER(L, "unitsSent",        stats.unitsSent);
1385 				HSTR_PUSH_NUMBER(L, "unitsCaptured",    stats.unitsCaptured);
1386 				HSTR_PUSH_NUMBER(L, "unitsOutCaptured", stats.unitsOutCaptured);
1387 				HSTR_PUSH_NUMBER(L, "unitsKilled",      stats.unitsKilled);
1388 			}
1389 			lua_rawseti(L, -2, count++);
1390 		}
1391 	}
1392 
1393 	return 1;
1394 }
1395 
1396 
GetTeamLuaAI(lua_State * L)1397 int LuaSyncedRead::GetTeamLuaAI(lua_State* L)
1398 {
1399 	CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1400 	if (team == NULL) {
1401 		return 0;
1402 	}
1403 
1404 	std::string luaAIName = "";
1405 	CSkirmishAIHandler::ids_t saids = skirmishAIHandler.GetSkirmishAIsInTeam(team->teamNum);
1406 	for (CSkirmishAIHandler::ids_t::const_iterator ai = saids.begin(); ai != saids.end(); ++ai) {
1407 		const SkirmishAIData* aiData = skirmishAIHandler.GetSkirmishAI(*ai);
1408 		if (aiData->isLuaAI) {
1409 			luaAIName = aiData->shortName;
1410 		}
1411 	}
1412 	lua_pushsstring(L, luaAIName);
1413 	return 1;
1414 }
1415 
1416 
1417 /******************************************************************************/
1418 
GetPlayerInfo(lua_State * L)1419 int LuaSyncedRead::GetPlayerInfo(lua_State* L)
1420 {
1421 	const int playerID = luaL_checkint(L, 1);
1422 	if (!playerHandler->IsValidPlayer(playerID)) {
1423 		return 0;
1424 	}
1425 
1426 	const CPlayer* player = playerHandler->Player(playerID);
1427 	if (player == NULL) {
1428 		return 0;
1429 	}
1430 
1431 	lua_pushsstring(L, player->name);
1432 	lua_pushboolean(L, player->active);
1433 	lua_pushboolean(L, player->spectator);
1434 	lua_pushnumber(L, player->team);
1435 	lua_pushnumber(L, teamHandler->AllyTeam(player->team));
1436 	const float pingScale = (GAME_SPEED * gs->speedFactor);
1437 	const float pingSecs = float(player->ping) / pingScale;
1438 	lua_pushnumber(L, pingSecs);
1439 	lua_pushnumber(L, player->cpuUsage);
1440 	lua_pushsstring(L, player->countryCode);
1441 	lua_pushnumber(L, player->rank);
1442 
1443 	lua_newtable(L);
1444 	const PlayerBase::customOpts& popts(player->GetAllValues());
1445 	for (PlayerBase::customOpts::const_iterator it = popts.begin(); it != popts.end(); ++it) {
1446 		lua_pushsstring(L, it->first);
1447 		lua_pushsstring(L, it->second);
1448 		lua_rawset(L, -3);
1449 	}
1450 	return 10;
1451 }
1452 
1453 
GetPlayerControlledUnit(lua_State * L)1454 int LuaSyncedRead::GetPlayerControlledUnit(lua_State* L)
1455 {
1456 	const int playerID = luaL_checkint(L, 1);
1457 	if (!playerHandler->IsValidPlayer(playerID)) {
1458 		return 0;
1459 	}
1460 
1461 	const CPlayer* player = playerHandler->Player(playerID);
1462 	if (player == NULL) {
1463 		return 0;
1464 	}
1465 
1466 	const FPSUnitController& con = player->fpsController;
1467 	const CUnit* unit = con.GetControllee();
1468 
1469 	if (unit == NULL) {
1470 		return 0;
1471 	}
1472 
1473 	if ((CLuaHandle::GetHandleReadAllyTeam(L) == CEventClient::NoAccessTeam) ||
1474 	    ((CLuaHandle::GetHandleReadAllyTeam(L) >= 0) && !teamHandler->Ally(unit->allyteam, CLuaHandle::GetHandleReadAllyTeam(L)))) {
1475 		return 0;
1476 	}
1477 
1478 	lua_pushnumber(L, unit->id);
1479 	return 1;
1480 }
1481 
GetAIInfo(lua_State * L)1482 int LuaSyncedRead::GetAIInfo(lua_State* L)
1483 {
1484 	int numVals = 0;
1485 
1486 	const int teamId = luaL_checkint(L, 1);
1487 	if (!teamHandler->IsValidTeam(teamId)) {
1488 		return numVals;
1489 	}
1490 
1491 	CSkirmishAIHandler::ids_t saids = skirmishAIHandler.GetSkirmishAIsInTeam(teamId);
1492 	if (saids.empty()) {
1493 		return numVals;
1494 	}
1495 	const size_t skirmishAIId    = *(saids.begin());
1496 	const SkirmishAIData* aiData = skirmishAIHandler.GetSkirmishAI(skirmishAIId);
1497 	const bool isLocal           = skirmishAIHandler.IsLocalSkirmishAI(skirmishAIId);
1498 
1499 	// this is synced AI info
1500 	lua_pushnumber(L, skirmishAIId);
1501 	lua_pushsstring(L, aiData->name);
1502 	lua_pushnumber(L, aiData->hostPlayer);
1503 	numVals += 3;
1504 
1505 	// no unsynced Skirmish AI info for synchronized scripts
1506 	if (CLuaHandle::GetHandleSynced(L)) {
1507 		HSTR_PUSH(L, "SYNCED_NOSHORTNAME");
1508 		HSTR_PUSH(L, "SYNCED_NOVERSION");
1509 		lua_newtable(L);
1510 	} else if (isLocal) {
1511 		lua_pushsstring(L, aiData->shortName);
1512 		lua_pushsstring(L, aiData->version);
1513 
1514 		lua_newtable(L);
1515 		std::map<std::string, std::string>::const_iterator o;
1516 		for (o = aiData->options.begin(); o != aiData->options.end(); ++o) {
1517 			lua_pushsstring(L, o->first);
1518 			lua_pushsstring(L, o->second);
1519 			lua_rawset(L, -3);
1520 		}
1521 	} else {
1522 		HSTR_PUSH(L, "UKNOWN");
1523 		HSTR_PUSH(L, "UKNOWN");
1524 		lua_newtable(L);
1525 	}
1526 	numVals += 3;
1527 
1528 	return numVals;
1529 }
1530 
GetAllyTeamInfo(lua_State * L)1531 int LuaSyncedRead::GetAllyTeamInfo(lua_State* L)
1532 {
1533 	const size_t allyteam = (size_t)luaL_checkint(L, -1);
1534 	if (!teamHandler->ValidAllyTeam(allyteam))
1535 		return 0;
1536 
1537 	const AllyTeam& ally = teamHandler->GetAllyTeam(allyteam);
1538 	lua_newtable(L);
1539 	const AllyTeam::customOpts& popts(ally.GetAllValues());
1540 	for (AllyTeam::customOpts::const_iterator it = popts.begin(); it != popts.end(); ++it) {
1541 		lua_pushsstring(L, it->first);
1542 		lua_pushsstring(L, it->second);
1543 		lua_rawset(L, -3);
1544 	}
1545 	return 1;
1546 }
1547 
AreTeamsAllied(lua_State * L)1548 int LuaSyncedRead::AreTeamsAllied(lua_State* L)
1549 {
1550 	const int teamId1 = (int)luaL_checkint(L, -1);
1551 	const int teamId2 = (int)luaL_checkint(L, -2);
1552 	if (!teamHandler->IsValidTeam(teamId1) ||
1553 	    !teamHandler->IsValidTeam(teamId2)) {
1554 		return 0;
1555 	}
1556 	lua_pushboolean(L, teamHandler->AlliedTeams(teamId1, teamId2));
1557 	return 1;
1558 }
1559 
1560 
ArePlayersAllied(lua_State * L)1561 int LuaSyncedRead::ArePlayersAllied(lua_State* L)
1562 {
1563 	const int player1 = luaL_checkint(L, -1);
1564 	const int player2 = luaL_checkint(L, -2);
1565 	if (!playerHandler->IsValidPlayer(player1) ||
1566 	    !playerHandler->IsValidPlayer(player2)) {
1567 		return 0;
1568 	}
1569 	const CPlayer* p1 = playerHandler->Player(player1);
1570 	const CPlayer* p2 = playerHandler->Player(player2);
1571 	if ((p1 == NULL) || (p2 == NULL)) {
1572 		return 0;
1573 	}
1574 	lua_pushboolean(L, teamHandler->AlliedTeams(p1->team, p2->team));
1575 	return 1;
1576 }
1577 
1578 
1579 /******************************************************************************/
1580 /******************************************************************************/
1581 //
1582 //  Grouped Unit Queries
1583 //
1584 
GetAllUnits(lua_State * L)1585 int LuaSyncedRead::GetAllUnits(lua_State* L)
1586 {
1587 	int count = 1;
1588 	std::list<CUnit*>::const_iterator uit;
1589 	if (CLuaHandle::GetHandleFullRead(L)) {
1590 		lua_createtable(L, unitHandler->activeUnits.size(), 0);
1591 		for (uit = unitHandler->activeUnits.begin(); uit != unitHandler->activeUnits.end(); ++uit) {
1592 			lua_pushnumber(L, (*uit)->id);
1593 			lua_rawseti(L, -2, count++);
1594 		}
1595 	} else {
1596 		lua_newtable(L);
1597 		for (uit = unitHandler->activeUnits.begin(); uit != unitHandler->activeUnits.end(); ++uit) {
1598 			if (IsUnitVisible(L, *uit)) {
1599 				lua_pushnumber(L, (*uit)->id);
1600 				lua_rawseti(L, -2, count++);
1601 			}
1602 		}
1603 	}
1604 
1605 	return 1;
1606 }
1607 
1608 
GetTeamUnits(lua_State * L)1609 int LuaSyncedRead::GetTeamUnits(lua_State* L)
1610 {
1611 	if (CLuaHandle::GetHandleReadAllyTeam(L) == CEventClient::NoAccessTeam) {
1612 		return 0;
1613 	}
1614 
1615 	// parse the team
1616 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1617 	if (team == NULL) {
1618 		return 0;
1619 	}
1620 	const int teamID = team->teamNum;
1621 
1622 	const CUnitSet& units = team->units;
1623 	CUnitSet::const_iterator uit;
1624 
1625 	// raw push for allies
1626 	if (IsAlliedTeam(L, teamID)) {
1627 		lua_newtable(L);
1628 		int count = 1;
1629 		for (uit = units.begin(); uit != units.end(); ++uit) {
1630 			lua_pushnumber(L, (*uit)->id);
1631 			lua_rawseti(L, -2, count++);
1632 		}
1633 
1634 		return 1;
1635 	}
1636 
1637 	// check visibility for enemies
1638 	lua_newtable(L);
1639 	int count = 1;
1640 	for (uit = units.begin(); uit != units.end(); ++uit) {
1641 		const CUnit* unit = *uit;
1642 		if (IsUnitVisible(L, unit)) {
1643 			lua_pushnumber(L, unit->id);
1644 			lua_rawseti(L, -2, count++);
1645 		}
1646 	}
1647 
1648 	return 1;
1649 }
1650 
1651 
GetTeamUnitsSorted(lua_State * L)1652 int LuaSyncedRead::GetTeamUnitsSorted(lua_State* L)
1653 {
1654 	if (CLuaHandle::GetHandleReadAllyTeam(L) == CEventClient::NoAccessTeam) {
1655 		return 0;
1656 	}
1657 
1658 	// parse the team
1659 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1660 	if (team == NULL) {
1661 		return 0;
1662 	}
1663 	const int teamID = team->teamNum;
1664 
1665 	map<int, vector<CUnit*> > unitDefMap;
1666 	const CUnitSet& units = team->units;
1667 	CUnitSet::const_iterator uit;
1668 
1669 	// tally for allies
1670 	if (IsAlliedTeam(L, teamID)) {
1671 		for (uit = units.begin(); uit != units.end(); ++uit) {
1672 			CUnit* unit = *uit;
1673 			unitDefMap[unit->unitDef->id].push_back(unit);
1674 		}
1675 	}
1676 	// tally for enemies
1677 	else {
1678 		// NOTE: (using unitsByDefs[] might be faster late-game)
1679 		for (uit = units.begin(); uit != units.end(); ++uit) {
1680 			CUnit* unit = *uit;
1681 			if (IsUnitVisible(L, unit)) {
1682 				const UnitDef* ud = EffectiveUnitDef(L, unit);
1683 				if (IsUnitTyped(L, unit)) {
1684 					unitDefMap[ud->id].push_back(unit);
1685 				} else {
1686 					unitDefMap[-1].push_back(unit); // unknown
1687 				}
1688 			}
1689 			unitDefMap[unit->unitDef->id].push_back(unit);
1690 		}
1691 	}
1692 
1693 	// push the table
1694 	lua_newtable(L);
1695 	map<int, vector<CUnit*> >::const_iterator mit;
1696 	for (mit = unitDefMap.begin(); mit != unitDefMap.end(); ++mit) {
1697 		const int unitDefID = mit->first;
1698 		if (unitDefID < 0) {
1699 			HSTR_PUSH(L, "unknown");
1700 		} else {
1701 			lua_pushnumber(L, unitDefID); // push the UnitDef index
1702 		}
1703 		lua_newtable(L); {
1704 			const vector<CUnit*>& v = mit->second;
1705 			for (int i = 0; i < (int)v.size(); i++) {
1706 				CUnit* unit = v[i];
1707 				lua_pushnumber(L, unit->id);
1708 				lua_rawseti(L, -2, i + 1);
1709 			}
1710 		}
1711 		lua_rawset(L, -3);
1712 	}
1713 
1714 	// UnitDef ID keys are not consecutive, so add the "n"
1715 	hs_n.PushNumber(L, unitDefMap.size());
1716 	return 1;
1717 }
1718 
1719 
GetTeamUnitsCounts(lua_State * L)1720 int LuaSyncedRead::GetTeamUnitsCounts(lua_State* L)
1721 {
1722 	if (CLuaHandle::GetHandleReadAllyTeam(L) == CEventClient::NoAccessTeam) {
1723 		return 0;
1724 	}
1725 
1726 	// parse the team
1727 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1728 	if (team == NULL) {
1729 		return 0;
1730 	}
1731 	const int teamID = team->teamNum;
1732 
1733 	// send the raw unitsByDefs counts for allies
1734 	if (IsAlliedTeam(L, teamID)) {
1735 		lua_newtable(L);
1736 		int defCount = 0;
1737 
1738 		for (int udID = 0; udID < unitDefHandler->unitDefs.size(); udID++) {
1739 			const int unitCount = unitHandler->unitsByDefs[teamID][udID].size();
1740 			if (unitCount > 0) {
1741 				lua_pushnumber(L, unitCount);
1742 				lua_rawseti(L, -2, udID);
1743 				defCount++;
1744 			}
1745 		}
1746 		// keys are not necessarily consecutive here
1747 		// due to the unitCount check, so add the "n"
1748 		hs_n.PushNumber(L, defCount);
1749 		return 1;
1750 	}
1751 
1752 	// tally the counts for enemies
1753 	map<int, int> unitDefCounts;
1754 	map<int, int>::const_iterator mit;
1755 
1756 	const CUnitSet& unitSet = team->units;
1757 	CUnitSet::const_iterator uit;
1758 
1759 	int unknownCount = 0;
1760 	for (uit = unitSet.begin(); uit != unitSet.end(); ++uit) {
1761 		const CUnit* unit = *uit;
1762 
1763 		if (!IsUnitVisible(L, unit)) {
1764 			continue;
1765 		}
1766 		if (!IsUnitTyped(L, unit)) {
1767 			unknownCount++;
1768 		} else {
1769 			const UnitDef* ud = EffectiveUnitDef(L, unit);
1770 			map<int, int>::iterator mit = unitDefCounts.find(ud->id);
1771 			if (mit == unitDefCounts.end()) {
1772 				unitDefCounts[ud->id] = 1;
1773 			} else {
1774 				unitDefCounts[ud->id] = mit->second + 1;
1775 			}
1776 		}
1777 	}
1778 
1779 	// push the counts
1780 	lua_newtable(L);
1781 	int defCount = 0;
1782 
1783 	for (mit = unitDefCounts.begin(); mit != unitDefCounts.end(); ++mit) {
1784 		lua_pushnumber(L, mit->second);
1785 		lua_rawseti(L, -2, mit->first);
1786 		defCount++;
1787 	}
1788 	if (unknownCount > 0) {
1789 		HSTR_PUSH_NUMBER(L, "unknown", unknownCount);
1790 		defCount++;
1791 	}
1792 
1793 	// unitDef->id is used for ordering, so not consecutive
1794 	hs_n.PushNumber(L, defCount);
1795 	return 1;
1796 }
1797 
1798 
InsertSearchUnitDefs(const UnitDef * ud,bool allied,set<int> & defs)1799 static inline void InsertSearchUnitDefs(const UnitDef* ud, bool allied,
1800                                         set<int>& defs)
1801 {
1802 	if (ud != NULL) {
1803 		if (allied) {
1804 			defs.insert(ud->id);
1805 		}
1806 		else if (ud->decoyDef == NULL) {
1807 			defs.insert(ud->id);
1808 			map<int, set<int> >::const_iterator dmit;
1809 			dmit = unitDefHandler->decoyMap.find(ud->id);
1810 			if (dmit != unitDefHandler->decoyMap.end()) {
1811 				const set<int>& decoys = dmit->second;
1812 				set<int>::const_iterator dit;
1813 				for (dit = decoys.begin(); dit != decoys.end(); ++dit) {
1814 					defs.insert(*dit);
1815 				}
1816 			}
1817 		}
1818 	}
1819 }
1820 
1821 
GetTeamUnitsByDefs(lua_State * L)1822 int LuaSyncedRead::GetTeamUnitsByDefs(lua_State* L)
1823 {
1824 	if (CLuaHandle::GetHandleReadAllyTeam(L) == CEventClient::NoAccessTeam) {
1825 		return 0;
1826 	}
1827 
1828 	// parse the team
1829 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1830 	if (team == NULL) {
1831 		return 0;
1832 	}
1833 	const int teamID = team->teamNum;
1834 
1835 	const bool allied = IsAlliedTeam(L, teamID);
1836 
1837 	// parse the unitDefs
1838 	set<int> defs;
1839 	if (lua_isnumber(L, 2)) {
1840 		const int unitDefID = lua_toint(L, 2);
1841 		const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
1842 		InsertSearchUnitDefs(ud, allied, defs);
1843 	}
1844 	else if (lua_istable(L, 2)) {
1845 		const int table = 2;
1846 		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
1847 			if (lua_isnumber(L, -1)) {
1848 				const int unitDefID = lua_toint(L, -1);
1849 				const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
1850 				InsertSearchUnitDefs(ud, allied, defs);
1851 			}
1852 		}
1853 	}
1854 	else {
1855 		luaL_error(L, "Incorrect arguments to GetTeamUnitsByDefs()");
1856 	}
1857 
1858 	lua_newtable(L);
1859 	int count = 1;
1860 
1861 	set<int>::const_iterator udit;
1862 	for (udit = defs.begin(); udit != defs.end(); ++udit) {
1863 		const CUnitSet& units = unitHandler->unitsByDefs[teamID][*udit];
1864 		CUnitSet::const_iterator uit;
1865 		for (uit = units.begin(); uit != units.end(); ++uit) {
1866 			const CUnit* unit = *uit;
1867 			if (allied || IsUnitTyped(L, unit)) {
1868 				lua_pushnumber(L, unit->id);
1869 				lua_rawseti(L, -2, count++);
1870 			}
1871 		}
1872 	}
1873 
1874 	return 1;
1875 }
1876 
1877 
GetTeamUnitDefCount(lua_State * L)1878 int LuaSyncedRead::GetTeamUnitDefCount(lua_State* L)
1879 {
1880 	if (CLuaHandle::GetHandleReadAllyTeam(L) == CEventClient::NoAccessTeam) {
1881 		return 0;
1882 	}
1883 
1884 	// parse the team
1885 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1886 	if (team == NULL) {
1887 		return 0;
1888 	}
1889 	const int teamID = team->teamNum;
1890 
1891 	// parse the unitDef
1892 	const int unitDefID = luaL_checkint(L, 2);
1893 	const UnitDef* unitDef = unitDefHandler->GetUnitDefByID(unitDefID);
1894 	if (unitDef == NULL) {
1895 		luaL_error(L, "Bad unitDefID in GetTeamUnitDefCount()");
1896 	}
1897 
1898 	// use the unitsByDefs count for allies
1899 	if (IsAlliedTeam(L, teamID)) {
1900 		lua_pushnumber(L, unitHandler->unitsByDefs[teamID][unitDefID].size());
1901 		return 1;
1902 	}
1903 
1904 	// you can never count enemy decoys
1905 	if (unitDef->decoyDef != NULL) {
1906 		lua_pushnumber(L, 0);
1907 		return 1;
1908 	}
1909 
1910 	int count = 0;
1911 
1912 	// tally the given unitDef units
1913 	const CUnitSet& units = unitHandler->unitsByDefs[teamID][unitDef->id];
1914 	CUnitSet::const_iterator uit;
1915 	for (uit = units.begin(); uit != units.end(); ++uit) {
1916 		const CUnit* unit = *uit;
1917 		if (IsUnitTyped(L, unit)) {
1918 			count++;
1919 		}
1920 	}
1921 
1922 	// tally the decoy units for the given unitDef
1923 	map<int, set<int> >::const_iterator dmit
1924 		= unitDefHandler->decoyMap.find(unitDef->id);
1925 
1926 	if (dmit != unitDefHandler->decoyMap.end()) {
1927 		const set<int>& decoyDefIDs = dmit->second;
1928 		set<int>::const_iterator dit;
1929 		for (dit = decoyDefIDs.begin(); dit != decoyDefIDs.end(); ++dit) {
1930 			const CUnitSet& units = unitHandler->unitsByDefs[teamID][*dit];
1931 			CUnitSet::const_iterator uit;
1932 			for (uit = units.begin(); uit != units.end(); ++uit) {
1933 				const CUnit* unit = *uit;
1934 				if (IsUnitTyped(L, unit)) {
1935 					count++;
1936 				}
1937 			}
1938 		}
1939 	}
1940 
1941 	lua_pushnumber(L, count);
1942 	return 1;
1943 }
1944 
1945 
GetTeamUnitCount(lua_State * L)1946 int LuaSyncedRead::GetTeamUnitCount(lua_State* L)
1947 {
1948 	if (CLuaHandle::GetHandleReadAllyTeam(L) == CEventClient::NoAccessTeam)
1949 		return 0;
1950 
1951 	// parse the team
1952 	const CTeam* team = ParseTeam(L, __FUNCTION__, 1);
1953 
1954 	if (team == NULL)
1955 		return 0;
1956 
1957 	// use the raw team count for allies
1958 	if (IsAlliedTeam(L, team->teamNum)) {
1959 		lua_pushnumber(L, team->units.size());
1960 		return 1;
1961 	}
1962 
1963 	// loop through the units for enemies
1964 	int count = 0;
1965 	const CUnitSet& units = team->units;
1966 
1967 	for (auto uit = units.begin(); uit != units.end(); ++uit) {
1968 		count += int(IsUnitVisible(L, *uit));
1969 	}
1970 
1971 	lua_pushnumber(L, count);
1972 	return 1;
1973 }
1974 
1975 
1976 /******************************************************************************/
1977 /******************************************************************************/
1978 //
1979 //  Spatial Unit Queries
1980 //
1981 
1982 // Macro Requirements:
1983 //   L, units
1984 
1985 #define LOOP_UNIT_CONTAINER(ALLEGIANCE_TEST, CUSTOM_TEST, NEWTABLE) \
1986 	{                                                               \
1987 		unsigned int count = 0;                                     \
1988                                                                     \
1989 		if (NEWTABLE) {                                             \
1990 			lua_createtable(L, units.size(), 0);                    \
1991 		}                                                           \
1992                                                                     \
1993 		for (auto it = units.begin(); it != units.end(); ++it) {    \
1994 			const CUnit* unit = *it;                                \
1995                                                                     \
1996 			ALLEGIANCE_TEST;                                        \
1997 			CUSTOM_TEST;                                            \
1998                                                                     \
1999 			lua_pushnumber(L, unit->id);                            \
2000 			lua_rawseti(L, -2, ++count);                            \
2001 		}                                                           \
2002 	}
2003 
2004 // Macro Requirements:
2005 //   unit
2006 //   readTeam   for MY_UNIT_TEST
2007 //   allegiance for SIMPLE_TEAM_TEST and VISIBLE_TEAM_TEST
2008 
2009 #define NULL_TEST  ;  // always passes
2010 
2011 #define VISIBLE_TEST \
2012 	if (!IsUnitVisible(L, unit)) { continue; }
2013 
2014 #define SIMPLE_TEAM_TEST \
2015 	if (unit->team != allegiance) { continue; }
2016 
2017 #define VISIBLE_TEAM_TEST \
2018 	if (unit->team != allegiance) { continue; } \
2019 	if (!IsUnitVisible(L, unit)) { continue; }
2020 
2021 #define MY_UNIT_TEST \
2022 	if (unit->team != readTeam) { continue; }
2023 
2024 #define ALLY_UNIT_TEST \
2025 	if (unit->allyteam != CLuaHandle::GetHandleReadAllyTeam(L)) { continue; }
2026 
2027 #define ENEMY_UNIT_TEST \
2028 	if (unit->allyteam == CLuaHandle::GetHandleReadAllyTeam(L)) { continue; } \
2029 	if (!IsUnitVisible(L, unit)) { continue; }
2030 
2031 
ParseAllegiance(lua_State * L,const char * caller,int index)2032 static int ParseAllegiance(lua_State* L, const char* caller, int index)
2033 {
2034 	if (!lua_isnumber(L, index)) {
2035 		return AllUnits;
2036 	}
2037 	const int teamID = lua_toint(L, index);
2038 	if (CLuaHandle::GetHandleFullRead(L) && (teamID < 0)) {
2039 		// MyUnits, AllyUnits, and EnemyUnits do not apply to fullRead
2040 		return AllUnits;
2041 	}
2042 	if (teamID < EnemyUnits) {
2043 		luaL_error(L, "Bad teamID in %s (%d)", caller, teamID);
2044 	} else if (teamID >= teamHandler->ActiveTeams()) {
2045 		luaL_error(L, "Bad teamID in %s (%d)", caller, teamID);
2046 	}
2047 	return teamID;
2048 }
2049 
2050 
GetUnitsInRectangle(lua_State * L)2051 int LuaSyncedRead::GetUnitsInRectangle(lua_State* L)
2052 {
2053 	const float xmin = luaL_checkfloat(L, 1);
2054 	const float zmin = luaL_checkfloat(L, 2);
2055 	const float xmax = luaL_checkfloat(L, 3);
2056 	const float zmax = luaL_checkfloat(L, 4);
2057 
2058 	const float3 mins(xmin, 0.0f, zmin);
2059 	const float3 maxs(xmax, 0.0f, zmax);
2060 
2061 	const int allegiance = ParseAllegiance(L, __FUNCTION__, 5);
2062 
2063 #define RECTANGLE_TEST ; // no test, GetUnitsExact is sufficient
2064 
2065 	const vector<CUnit*>& units = quadField->GetUnitsExact(mins, maxs);
2066 
2067 	if (allegiance >= 0) {
2068 		if (IsAlliedTeam(L, allegiance)) {
2069 			LOOP_UNIT_CONTAINER(SIMPLE_TEAM_TEST, RECTANGLE_TEST, true);
2070 		} else {
2071 			LOOP_UNIT_CONTAINER(VISIBLE_TEAM_TEST, RECTANGLE_TEST, true);
2072 		}
2073 	}
2074 	else if (allegiance == MyUnits) {
2075 		const int readTeam = CLuaHandle::GetHandleReadTeam(L);
2076 		LOOP_UNIT_CONTAINER(MY_UNIT_TEST, RECTANGLE_TEST, true);
2077 	}
2078 	else if (allegiance == AllyUnits) {
2079 		LOOP_UNIT_CONTAINER(ALLY_UNIT_TEST, RECTANGLE_TEST, true);
2080 	}
2081 	else if (allegiance == EnemyUnits) {
2082 		LOOP_UNIT_CONTAINER(ENEMY_UNIT_TEST, RECTANGLE_TEST, true);
2083 	}
2084 	else { // AllUnits
2085 		LOOP_UNIT_CONTAINER(VISIBLE_TEST, RECTANGLE_TEST, true);
2086 	}
2087 
2088 	return 1;
2089 }
2090 
2091 
GetUnitsInBox(lua_State * L)2092 int LuaSyncedRead::GetUnitsInBox(lua_State* L)
2093 {
2094 	const float xmin = luaL_checkfloat(L, 1);
2095 	const float ymin = luaL_checkfloat(L, 2);
2096 	const float zmin = luaL_checkfloat(L, 3);
2097 	const float xmax = luaL_checkfloat(L, 4);
2098 	const float ymax = luaL_checkfloat(L, 5);
2099 	const float zmax = luaL_checkfloat(L, 6);
2100 
2101 	const float3 mins(xmin, 0.0f, zmin);
2102 	const float3 maxs(xmax, 0.0f, zmax);
2103 
2104 	const int allegiance = ParseAllegiance(L, __FUNCTION__, 7);
2105 
2106 #define BOX_TEST                  \
2107 	const float y = unit->midPos.y; \
2108 	if ((y < ymin) || (y > ymax)) { \
2109 		continue;                     \
2110 	}
2111 
2112 	const vector<CUnit*>& units = quadField->GetUnitsExact(mins, maxs);
2113 
2114 	if (allegiance >= 0) {
2115 		if (IsAlliedTeam(L, allegiance)) {
2116 			LOOP_UNIT_CONTAINER(SIMPLE_TEAM_TEST, BOX_TEST, true);
2117 		} else {
2118 			LOOP_UNIT_CONTAINER(VISIBLE_TEAM_TEST, BOX_TEST, true);
2119 		}
2120 	}
2121 	else if (allegiance == MyUnits) {
2122 		const int readTeam = CLuaHandle::GetHandleReadTeam(L);
2123 		LOOP_UNIT_CONTAINER(MY_UNIT_TEST, BOX_TEST, true);
2124 	}
2125 	else if (allegiance == AllyUnits) {
2126 		LOOP_UNIT_CONTAINER(ALLY_UNIT_TEST, BOX_TEST, true);
2127 	}
2128 	else if (allegiance == EnemyUnits) {
2129 		LOOP_UNIT_CONTAINER(ENEMY_UNIT_TEST, BOX_TEST, true);
2130 	}
2131 	else { // AllUnits
2132 		LOOP_UNIT_CONTAINER(VISIBLE_TEST, BOX_TEST, true);
2133 	}
2134 
2135 	return 1;
2136 }
2137 
2138 
GetUnitsInCylinder(lua_State * L)2139 int LuaSyncedRead::GetUnitsInCylinder(lua_State* L)
2140 {
2141 	const float x      = luaL_checkfloat(L, 1);
2142 	const float z      = luaL_checkfloat(L, 2);
2143 	const float radius = luaL_checkfloat(L, 3);
2144 	const float radSqr = (radius * radius);
2145 
2146 	const float3 mins(x - radius, 0.0f, z - radius);
2147 	const float3 maxs(x + radius, 0.0f, z + radius);
2148 
2149 	const int allegiance = ParseAllegiance(L, __FUNCTION__, 4);
2150 
2151 #define CYLINDER_TEST                         \
2152 	const float3& p = unit->midPos;             \
2153 	const float dx = (p.x - x);                 \
2154 	const float dz = (p.z - z);                 \
2155 	const float dist = ((dx * dx) + (dz * dz)); \
2156 	if (dist > radSqr) {                        \
2157 		continue;                                 \
2158 	}                                           \
2159 
2160 	const vector<CUnit*>& units = quadField->GetUnitsExact(mins, maxs);
2161 
2162 	if (allegiance >= 0) {
2163 		if (IsAlliedTeam(L, allegiance)) {
2164 			LOOP_UNIT_CONTAINER(SIMPLE_TEAM_TEST, CYLINDER_TEST, true);
2165 		} else {
2166 			LOOP_UNIT_CONTAINER(VISIBLE_TEAM_TEST, CYLINDER_TEST, true);
2167 		}
2168 	}
2169 	else if (allegiance == MyUnits) {
2170 		const int readTeam = CLuaHandle::GetHandleReadTeam(L);
2171 		LOOP_UNIT_CONTAINER(MY_UNIT_TEST, CYLINDER_TEST, true);
2172 	}
2173 	else if (allegiance == AllyUnits) {
2174 		LOOP_UNIT_CONTAINER(ALLY_UNIT_TEST, CYLINDER_TEST, true);
2175 	}
2176 	else if (allegiance == EnemyUnits) {
2177 		LOOP_UNIT_CONTAINER(ENEMY_UNIT_TEST, CYLINDER_TEST, true);
2178 	}
2179 	else { // AllUnits
2180 		LOOP_UNIT_CONTAINER(VISIBLE_TEST, CYLINDER_TEST, true);
2181 	}
2182 
2183 	return 1;
2184 }
2185 
2186 
GetUnitsInSphere(lua_State * L)2187 int LuaSyncedRead::GetUnitsInSphere(lua_State* L)
2188 {
2189 	const float x      = luaL_checkfloat(L, 1);
2190 	const float y      = luaL_checkfloat(L, 2);
2191 	const float z      = luaL_checkfloat(L, 3);
2192 	const float radius = luaL_checkfloat(L, 4);
2193 	const float radSqr = (radius * radius);
2194 
2195 	const float3 pos(x, y, z);
2196 	const float3 mins(x - radius, 0.0f, z - radius);
2197 	const float3 maxs(x + radius, 0.0f, z + radius);
2198 
2199 	const int allegiance = ParseAllegiance(L, __FUNCTION__, 5);
2200 
2201 #define SPHERE_TEST                           \
2202 	const float3& p = unit->midPos;             \
2203 	const float dx = (p.x - x);                 \
2204 	const float dy = (p.y - y);                 \
2205 	const float dz = (p.z - z);                 \
2206 	const float dist =                          \
2207 		((dx * dx) + (dy * dy) + (dz * dz));      \
2208 	if (dist > radSqr) {                        \
2209 		continue;                                 \
2210 	}                                           \
2211 
2212 	const vector<CUnit*>& units = quadField->GetUnitsExact(mins, maxs);
2213 
2214 	if (allegiance >= 0) {
2215 		if (IsAlliedTeam(L, allegiance)) {
2216 			LOOP_UNIT_CONTAINER(SIMPLE_TEAM_TEST, SPHERE_TEST, true);
2217 		} else {
2218 			LOOP_UNIT_CONTAINER(VISIBLE_TEAM_TEST, SPHERE_TEST, true);
2219 		}
2220 	}
2221 	else if (allegiance == MyUnits) {
2222 		const int readTeam = CLuaHandle::GetHandleReadTeam(L);
2223 		LOOP_UNIT_CONTAINER(MY_UNIT_TEST, SPHERE_TEST, true);
2224 	}
2225 	else if (allegiance == AllyUnits) {
2226 		LOOP_UNIT_CONTAINER(ALLY_UNIT_TEST, SPHERE_TEST, true);
2227 	}
2228 	else if (allegiance == EnemyUnits) {
2229 		LOOP_UNIT_CONTAINER(ENEMY_UNIT_TEST, SPHERE_TEST, true);
2230 	}
2231 	else { // AllUnits
2232 		LOOP_UNIT_CONTAINER(VISIBLE_TEST, SPHERE_TEST, true);
2233 	}
2234 
2235 	return 1;
2236 }
2237 
2238 
2239 struct Plane {
2240 	float x, y, z, d;  // ax + by + cz + d = 0
2241 };
2242 
2243 
UnitInPlanes(const CUnit * unit,const vector<Plane> & planes)2244 static inline bool UnitInPlanes(const CUnit* unit, const vector<Plane>& planes)
2245 {
2246 	const float3& pos = unit->midPos;
2247 	for (int i = 0; i < (int)planes.size(); i++) {
2248 		const Plane& p = planes[i];
2249 		const float dist = (pos.x * p.x) + (pos.y * p.y) + (pos.z * p.z) + p.d;
2250 		if ((dist - unit->radius) > 0.0f) {
2251 			return false; // outside
2252 		}
2253 	}
2254 	return true;
2255 }
2256 
2257 
GetUnitsInPlanes(lua_State * L)2258 int LuaSyncedRead::GetUnitsInPlanes(lua_State* L)
2259 {
2260 	if (!lua_istable(L, 1)) {
2261 		luaL_error(L, "Incorrect arguments to GetUnitsInPlanes()");
2262 	}
2263 
2264 	// parse the planes
2265 	vector<Plane> planes;
2266 	const int table = lua_gettop(L);
2267 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
2268 		if (lua_istable(L, -1)) {
2269 			float values[4];
2270 			const int v = ParseFloatArray(L, values, 4);
2271 			if (v == 4) {
2272 				Plane plane = { values[0], values[1], values[2], values[3] };
2273 				planes.push_back(plane);
2274 			}
2275 		}
2276 	}
2277 
2278 	int startTeam, endTeam;
2279 
2280 	const int allegiance = ParseAllegiance(L, __FUNCTION__, 2);
2281 	if (allegiance >= 0) {
2282 		startTeam = allegiance;
2283 		endTeam = allegiance;
2284 	}
2285 	else if (allegiance == MyUnits) {
2286 		const int readTeam = CLuaHandle::GetHandleReadTeam(L);
2287 		startTeam = readTeam;
2288 		endTeam = readTeam;
2289 	}
2290 	else {
2291 		startTeam = 0;
2292 		endTeam = teamHandler->ActiveTeams() - 1;
2293 	}
2294 
2295 #define PLANES_TEST                    \
2296 	if (!UnitInPlanes(unit, planes)) { \
2297 		continue;                      \
2298 	}
2299 
2300 	const int readTeam = CLuaHandle::GetHandleReadTeam(L);
2301 
2302 	lua_newtable(L);
2303 
2304 	for (int team = startTeam; team <= endTeam; team++) {
2305 		const CUnitSet& units = teamHandler->Team(team)->units;
2306 
2307 		if (allegiance >= 0) {
2308 			if (allegiance == team) {
2309 				if (IsAlliedTeam(L, allegiance)) {
2310 					LOOP_UNIT_CONTAINER(NULL_TEST, PLANES_TEST, false);
2311 				} else {
2312 					LOOP_UNIT_CONTAINER(VISIBLE_TEST, PLANES_TEST, false);
2313 				}
2314 			}
2315 		}
2316 		else if (allegiance == MyUnits) {
2317 			if (readTeam == team) {
2318 				LOOP_UNIT_CONTAINER(NULL_TEST, PLANES_TEST, false);
2319 			}
2320 		}
2321 		else if (allegiance == AllyUnits) {
2322 			if (CLuaHandle::GetHandleReadAllyTeam(L) == teamHandler->AllyTeam(team)) {
2323 				LOOP_UNIT_CONTAINER(NULL_TEST, PLANES_TEST, false);
2324 			}
2325 		}
2326 		else if (allegiance == EnemyUnits) {
2327 			if (CLuaHandle::GetHandleReadAllyTeam(L) != teamHandler->AllyTeam(team)) {
2328 				LOOP_UNIT_CONTAINER(VISIBLE_TEST, PLANES_TEST, false);
2329 			}
2330 		}
2331 		else { // AllUnits
2332 			if (IsAlliedTeam(L, team)) {
2333 				LOOP_UNIT_CONTAINER(NULL_TEST, PLANES_TEST, false);
2334 			} else {
2335 				LOOP_UNIT_CONTAINER(VISIBLE_TEST, PLANES_TEST, false);
2336 			}
2337 		}
2338 	}
2339 
2340 	return 1;
2341 }
2342 
2343 
2344 /******************************************************************************/
2345 
GetUnitNearestAlly(lua_State * L)2346 int LuaSyncedRead::GetUnitNearestAlly(lua_State* L)
2347 {
2348 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2349 	if (unit == NULL) {
2350 		return 0;
2351 	}
2352 	const float range = luaL_optnumber(L, 2, 1.0e9f);
2353 	const CUnit* target =
2354 		CGameHelper::GetClosestFriendlyUnit(unit, unit->pos, range, unit->allyteam);
2355 
2356 	if (target != NULL) {
2357 		lua_pushnumber(L, target->id);
2358 		return 1;
2359 	}
2360 	return 0;
2361 }
2362 
2363 
GetUnitNearestEnemy(lua_State * L)2364 int LuaSyncedRead::GetUnitNearestEnemy(lua_State* L)
2365 {
2366 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2367 	if (unit == NULL) {
2368 		return 0;
2369 	}
2370 	const float range = luaL_optnumber(L, 2, 1.0e9f);
2371 	const bool useLos =
2372 		!CLuaHandle::GetHandleFullRead(L) || !lua_isboolean(L, 3) || lua_toboolean(L, 3);
2373 	const CUnit* target = NULL;
2374 
2375 	if (useLos) {
2376 		target = CGameHelper::GetClosestEnemyUnit(unit, unit->pos, range, unit->allyteam);
2377 	} else {
2378 		target = CGameHelper::GetClosestEnemyUnitNoLosTest(unit, unit->pos, range, unit->allyteam, false, true);
2379 	}
2380 
2381 	if (target != NULL) {
2382 		lua_pushnumber(L, target->id);
2383 		return 1;
2384 	}
2385 	return 0;
2386 }
2387 
2388 
2389 /******************************************************************************/
2390 
ProcessFeatures(lua_State * L,const vector<CFeature * > & features)2391 inline void ProcessFeatures(lua_State* L, const vector<CFeature*>& features) {
2392 	const unsigned int featureCount = features.size();
2393 	unsigned int arrayIndex = 1;
2394 
2395 	lua_createtable(L, featureCount, 0);
2396 
2397 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
2398 		if (CLuaHandle::GetHandleFullRead(L)) {
2399 			for (unsigned int i = 0; i < featureCount; i++) {
2400 				const CFeature* feature = features[i];
2401 
2402 				lua_pushnumber(L, feature->id);
2403 				lua_rawseti(L, -2, arrayIndex++);
2404 			}
2405 		}
2406 	} else {
2407 		for (unsigned int i = 0; i < featureCount; i++) {
2408 			const CFeature* feature = features[i];
2409 
2410 			if (!IsFeatureVisible(L, feature)) {
2411 				continue;
2412 			}
2413 
2414 			lua_pushnumber(L, feature->id);
2415 			lua_rawseti(L, -2, arrayIndex++);
2416 		}
2417 	}
2418 }
2419 
GetFeaturesInRectangle(lua_State * L)2420 int LuaSyncedRead::GetFeaturesInRectangle(lua_State* L)
2421 {
2422 	const float xmin = luaL_checkfloat(L, 1);
2423 	const float zmin = luaL_checkfloat(L, 2);
2424 	const float xmax = luaL_checkfloat(L, 3);
2425 	const float zmax = luaL_checkfloat(L, 4);
2426 
2427 	const float3 mins(xmin, 0.0f, zmin);
2428 	const float3 maxs(xmax, 0.0f, zmax);
2429 
2430 	const vector<CFeature*>& rectFeatures = quadField->GetFeaturesExact(mins, maxs);
2431 	ProcessFeatures(L, rectFeatures);
2432 	return 1;
2433 }
2434 
GetFeaturesInSphere(lua_State * L)2435 int LuaSyncedRead::GetFeaturesInSphere(lua_State* L)
2436 {
2437 	const float x = luaL_checkfloat(L, 1);
2438 	const float y = luaL_checkfloat(L, 2);
2439 	const float z = luaL_checkfloat(L, 3);
2440 	const float rad = luaL_checkfloat(L, 4);
2441 
2442 	const float3 pos(x, y, z);
2443 
2444 	const vector<CFeature*>& sphFeatures = quadField->GetFeaturesExact(pos, rad, true);
2445 	ProcessFeatures(L, sphFeatures);
2446 	return 1;
2447 }
2448 
GetFeaturesInCylinder(lua_State * L)2449 int LuaSyncedRead::GetFeaturesInCylinder(lua_State* L)
2450 {
2451 	const float x = luaL_checkfloat(L, 1);
2452 	const float z = luaL_checkfloat(L, 2);
2453 	const float rad = luaL_checkfloat(L, 3);
2454 
2455 	const float3 pos(x, 0, z);
2456 
2457 	const vector<CFeature*>& cylFeatures = quadField->GetFeaturesExact(pos, rad, false);
2458 	ProcessFeatures(L, cylFeatures);
2459 	return 1;
2460 }
2461 
GetProjectilesInRectangle(lua_State * L)2462 int LuaSyncedRead::GetProjectilesInRectangle(lua_State* L)
2463 {
2464 	const float xmin = luaL_checkfloat(L, 1);
2465 	const float zmin = luaL_checkfloat(L, 2);
2466 	const float xmax = luaL_checkfloat(L, 3);
2467 	const float zmax = luaL_checkfloat(L, 4);
2468 
2469 	const bool excludeWeaponProjectiles = luaL_optboolean(L, 5, false);
2470 	const bool excludePieceProjectiles = luaL_optboolean(L, 6, false);
2471 
2472 	const float3 mins(xmin, 0.0f, zmin);
2473 	const float3 maxs(xmax, 0.0f, zmax);
2474 
2475 	const bool renderAccess = !Threading::IsSimThread();
2476 
2477 	const vector<CProjectile*>& rectProjectiles = quadField->GetProjectilesExact(mins, maxs);
2478 	const unsigned int rectProjectileCount = rectProjectiles.size();
2479 	unsigned int arrayIndex = 1;
2480 
2481 	lua_createtable(L, rectProjectileCount, 0);
2482 
2483 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
2484 		if (CLuaHandle::GetHandleFullRead(L)) {
2485 			for (unsigned int i = 0; i < rectProjectileCount; i++) {
2486 				const CProjectile* pro = rectProjectiles[i];
2487 
2488 				if (renderAccess && !projectileHandler->RenderAccess(pro))
2489 					continue;
2490 
2491 				// filter out unsynced projectiles, the SyncedRead
2492 				// projecile Get* functions accept only synced ID's
2493 				// (specifically they interpret all ID's as synced)
2494 				if (!pro->synced)
2495 					continue;
2496 
2497 				if (pro->weapon && excludeWeaponProjectiles)
2498 					continue;
2499 				if (pro->piece && excludePieceProjectiles)
2500 					continue;
2501 
2502 				lua_pushnumber(L, pro->id);
2503 				lua_rawseti(L, -2, arrayIndex++);
2504 			}
2505 		}
2506 	} else {
2507 		for (unsigned int i = 0; i < rectProjectileCount; i++) {
2508 			const CProjectile* pro = rectProjectiles[i];
2509 
2510 			if (renderAccess && !projectileHandler->RenderAccess(pro))
2511 				continue;
2512 
2513 			const CUnit* unit = pro->owner();
2514 			const ProjectileMapValPair proPair(const_cast<CProjectile*>(pro), ((unit != NULL)? unit->allyteam: CLuaHandle::GetHandleReadAllyTeam(L)));
2515 
2516 			// see above
2517 			if (!pro->synced)
2518 				continue;
2519 
2520 			if (pro->weapon && excludeWeaponProjectiles)
2521 				continue;
2522 			if (pro->piece && excludePieceProjectiles)
2523 				continue;
2524 
2525 			if (!IsProjectileVisible(L, proPair))
2526 				continue;
2527 
2528 			lua_pushnumber(L, pro->id);
2529 			lua_rawseti(L, -2, arrayIndex++);
2530 		}
2531 	}
2532 
2533 	return 1;
2534 }
2535 
2536 
2537 /******************************************************************************/
2538 /******************************************************************************/
2539 
ValidUnitID(lua_State * L)2540 int LuaSyncedRead::ValidUnitID(lua_State* L)
2541 {
2542 	// note the NULL 'caller' arg (so ParseUnit won't call luaL_error)
2543 	lua_pushboolean(L, ParseUnit(L, NULL, 1) != NULL);
2544 	return 1;
2545 }
2546 
2547 
GetUnitStates(lua_State * L)2548 int LuaSyncedRead::GetUnitStates(lua_State* L)
2549 {
2550 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2551 	if (unit == NULL) {
2552 		return 0;
2553 	}
2554 
2555 	lua_createtable(L, 0, 9);
2556 	HSTR_PUSH_NUMBER(L, "firestate",  unit->fireState);
2557 	HSTR_PUSH_NUMBER(L, "movestate",  unit->moveState);
2558 	HSTR_PUSH_BOOL  (L, "repeat",     unit->commandAI->repeatOrders);
2559 	HSTR_PUSH_BOOL  (L, "cloak",      unit->wantCloak);
2560 	HSTR_PUSH_BOOL  (L, "active",     unit->activated);
2561 	HSTR_PUSH_BOOL  (L, "trajectory", unit->useHighTrajectory);
2562 
2563 	const AMoveType* mt = unit->moveType;
2564 	if (mt) {
2565 		const CHoverAirMoveType* hAMT = dynamic_cast<const CHoverAirMoveType*>(mt);
2566 		if (hAMT) {
2567 			HSTR_PUSH_BOOL  (L, "autoland",        hAMT->autoLand);
2568 			HSTR_PUSH_NUMBER(L, "autorepairlevel", hAMT->GetRepairBelowHealth());
2569 		} else {
2570 			const CStrafeAirMoveType* sAMT = dynamic_cast<const CStrafeAirMoveType*>(mt);
2571 			if (sAMT) {
2572 				HSTR_PUSH_BOOL  (L, "autoland",        sAMT->autoLand);
2573 				HSTR_PUSH_BOOL  (L, "loopbackattack",  sAMT->loopbackAttack);
2574 				HSTR_PUSH_NUMBER(L, "autorepairlevel", sAMT->GetRepairBelowHealth());
2575 			}
2576 		}
2577 	}
2578 
2579 	return 1;
2580 }
2581 
2582 
GetUnitArmored(lua_State * L)2583 int LuaSyncedRead::GetUnitArmored(lua_State* L)
2584 {
2585 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2586 	if (unit == NULL) {
2587 		return 0;
2588 	}
2589 	lua_pushboolean(L, unit->armoredState);
2590 	lua_pushnumber(L, unit->armoredMultiple);
2591 	return 2;
2592 }
2593 
2594 
GetUnitIsActive(lua_State * L)2595 int LuaSyncedRead::GetUnitIsActive(lua_State* L)
2596 {
2597 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2598 	if (unit == NULL) {
2599 		return 0;
2600 	}
2601 	lua_pushboolean(L, unit->activated);
2602 	return 1;
2603 }
2604 
2605 
GetUnitIsCloaked(lua_State * L)2606 int LuaSyncedRead::GetUnitIsCloaked(lua_State* L)
2607 {
2608 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2609 	if (unit == NULL) {
2610 		return 0;
2611 	}
2612 	lua_pushboolean(L, unit->isCloaked);
2613 	return 1;
2614 }
2615 
2616 
GetUnitSelfDTime(lua_State * L)2617 int LuaSyncedRead::GetUnitSelfDTime(lua_State* L)
2618 {
2619 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2620 	if (unit == NULL) {
2621 		return 0;
2622 	}
2623 	lua_pushnumber(L, unit->selfDCountdown);
2624 	return 1;
2625 }
2626 
2627 
GetUnitStockpile(lua_State * L)2628 int LuaSyncedRead::GetUnitStockpile(lua_State* L)
2629 {
2630 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2631 	if (unit == NULL) {
2632 		return 0;
2633 	}
2634 	if (unit->stockpileWeapon == NULL) {
2635 		return 0;
2636 	}
2637 	lua_pushnumber(L, unit->stockpileWeapon->numStockpiled);
2638 	lua_pushnumber(L, unit->stockpileWeapon->numStockpileQued);
2639 	lua_pushnumber(L, unit->stockpileWeapon->buildPercent);
2640 	return 3;
2641 }
2642 
2643 
GetUnitSensorRadius(lua_State * L)2644 int LuaSyncedRead::GetUnitSensorRadius(lua_State* L)
2645 {
2646 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2647 	if (unit == NULL) {
2648 		return 0;
2649 	}
2650 	const string key = luaL_checkstring(L, 2);
2651 
2652 	const int radarDiv = radarHandler->radarDiv;
2653 
2654 	if (key == "los") {
2655 		lua_pushnumber(L, unit->losRadius * losHandler->losDiv);
2656 	} else if (key == "airLos") {
2657 		lua_pushnumber(L, unit->airLosRadius * losHandler->airDiv);
2658 	} else if (key == "radar") {
2659 		lua_pushnumber(L, unit->radarRadius * radarDiv);
2660 	} else if (key == "sonar") {
2661 		lua_pushnumber(L, unit->sonarRadius * radarDiv);
2662 	} else if (key == "seismic") {
2663 		lua_pushnumber(L, unit->seismicRadius * radarDiv);
2664 	} else if (key == "radarJammer") {
2665 		lua_pushnumber(L, unit->jammerRadius * radarDiv);
2666 	} else if (key == "sonarJammer") {
2667 		lua_pushnumber(L, unit->sonarJamRadius * radarDiv);
2668 	} else {
2669 		return 0; // unknown sensor type
2670 	}
2671 
2672 	return 1;
2673 }
2674 
GetUnitPosErrorParams(lua_State * L)2675 int LuaSyncedRead::GetUnitPosErrorParams(lua_State* L)
2676 {
2677 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2678 
2679 	if (unit == NULL)
2680 		return 0;
2681 
2682 	lua_pushnumber(L, unit->posErrorVector.x);
2683 	lua_pushnumber(L, unit->posErrorVector.y);
2684 	lua_pushnumber(L, unit->posErrorVector.z);
2685 	lua_pushnumber(L, unit->posErrorDelta.x);
2686 	lua_pushnumber(L, unit->posErrorDelta.y);
2687 	lua_pushnumber(L, unit->posErrorDelta.z);
2688 	lua_pushnumber(L, unit->nextPosErrorUpdate);
2689 	return (3 + 3 + 1);
2690 }
2691 
2692 
GetUnitTooltip(lua_State * L)2693 int LuaSyncedRead::GetUnitTooltip(lua_State* L)
2694 {
2695 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
2696 	if (unit == NULL) {
2697 		return 0;
2698 	}
2699 
2700 	string tooltip;
2701 
2702 	const CTeam* unitTeam = NULL;
2703 	const UnitDef* unitDef = unit->unitDef;
2704 	const UnitDef* decoyDef = IsAllyUnit(L, unit) ? NULL : unitDef->decoyDef;
2705 	const UnitDef* effectiveDef = EffectiveUnitDef(L, unit);
2706 
2707 	if (effectiveDef->showPlayerName) {
2708 		if (teamHandler->IsValidTeam(unit->team)) {
2709 			unitTeam = teamHandler->Team(unit->team);
2710 		}
2711 
2712 		if (unitTeam != NULL && unitTeam->HasLeader()) {
2713 			tooltip = playerHandler->Player(unitTeam->GetLeader())->name;
2714 			CSkirmishAIHandler::ids_t teamAIs = skirmishAIHandler.GetSkirmishAIsInTeam(unit->team);
2715 
2716 			if (!teamAIs.empty()) {
2717 				tooltip = std::string("AI@") + tooltip;
2718 			}
2719 		}
2720 	} else {
2721 		if (!decoyDef) {
2722 			tooltip = unit->tooltip;
2723 		} else {
2724 			tooltip = decoyDef->humanName + " - " + decoyDef->tooltip;
2725 		}
2726 	}
2727 	lua_pushsstring(L, tooltip);
2728 	return 1;
2729 }
2730 
2731 
GetUnitDefID(lua_State * L)2732 int LuaSyncedRead::GetUnitDefID(lua_State* L)
2733 {
2734 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2735 	if (unit == NULL) {
2736 		return 0;
2737 	}
2738 	if (IsAllyUnit(L, unit)) {
2739 		lua_pushnumber(L, unit->unitDef->id);
2740 	}
2741 	else {
2742 		if (!IsUnitTyped(L, unit)) {
2743 			return 0;
2744 		}
2745 		lua_pushnumber(L, EffectiveUnitDef(L, unit)->id);
2746 	}
2747 	return 1;
2748 }
2749 
2750 
GetUnitTeam(lua_State * L)2751 int LuaSyncedRead::GetUnitTeam(lua_State* L)
2752 {
2753 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2754 	if (unit == NULL) {
2755 		return 0;
2756 	}
2757 	lua_pushnumber(L, unit->team);
2758 	return 1;
2759 }
2760 
2761 
GetUnitAllyTeam(lua_State * L)2762 int LuaSyncedRead::GetUnitAllyTeam(lua_State* L)
2763 {
2764 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2765 	if (unit == NULL) {
2766 		return 0;
2767 	}
2768 	lua_pushnumber(L, unit->allyteam);
2769 	return 1;
2770 }
2771 
2772 
GetUnitNeutral(lua_State * L)2773 int LuaSyncedRead::GetUnitNeutral(lua_State* L)
2774 {
2775 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2776 	if (unit == NULL) {
2777 		return 0;
2778 	}
2779 	lua_pushboolean(L, unit->IsNeutral());
2780 	return 1;
2781 }
2782 
2783 
GetUnitHealth(lua_State * L)2784 int LuaSyncedRead::GetUnitHealth(lua_State* L)
2785 {
2786 	CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
2787 	if (unit == NULL) {
2788 		return 0;
2789 	}
2790 	const UnitDef* ud = unit->unitDef;
2791 	const bool enemyUnit = IsEnemyUnit(L, unit);
2792 
2793 	if (ud->hideDamage && enemyUnit) {
2794 		lua_pushnil(L);
2795 		lua_pushnil(L);
2796 		lua_pushnil(L);
2797 	} else if (!enemyUnit || (ud->decoyDef == NULL)) {
2798 		lua_pushnumber(L, unit->health);
2799 		lua_pushnumber(L, unit->maxHealth);
2800 		lua_pushnumber(L, unit->paralyzeDamage);
2801 	} else {
2802 		const float scale = (ud->decoyDef->health / ud->health);
2803 		lua_pushnumber(L, scale * unit->health);
2804 		lua_pushnumber(L, scale * unit->maxHealth);
2805 		lua_pushnumber(L, scale * unit->paralyzeDamage);
2806 	}
2807 	lua_pushnumber(L, unit->captureProgress);
2808 	lua_pushnumber(L, unit->buildProgress);
2809 	return 5;
2810 }
2811 
2812 
GetUnitIsDead(lua_State * L)2813 int LuaSyncedRead::GetUnitIsDead(lua_State* L)
2814 {
2815 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
2816 	if (unit == NULL) {
2817 		return 0;
2818 	}
2819 	lua_pushboolean(L, unit->isDead);
2820 	return 1;
2821 }
2822 
2823 
GetUnitIsStunned(lua_State * L)2824 int LuaSyncedRead::GetUnitIsStunned(lua_State* L)
2825 {
2826 	CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
2827 	if (unit == NULL) {
2828 		return 0;
2829 	}
2830 	lua_pushboolean(L, unit->IsStunned() || unit->beingBuilt);
2831 	lua_pushboolean(L, unit->IsStunned());
2832 	lua_pushboolean(L, unit->beingBuilt);
2833 	return 3;
2834 }
2835 
2836 
GetUnitResources(lua_State * L)2837 int LuaSyncedRead::GetUnitResources(lua_State* L)
2838 {
2839 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2840 	if (unit == NULL) {
2841 		return 0;
2842 	}
2843 	lua_pushnumber(L, unit->metalMake);
2844 	lua_pushnumber(L, unit->metalUse);
2845 	lua_pushnumber(L, unit->energyMake);
2846 	lua_pushnumber(L, unit->energyUse);
2847 	return 4;
2848 }
2849 
2850 
GetUnitMetalExtraction(lua_State * L)2851 int LuaSyncedRead::GetUnitMetalExtraction(lua_State* L)
2852 {
2853 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2854 	if (unit == NULL) {
2855 		return 0;
2856 	}
2857 	if (!unit->unitDef->extractsMetal) {
2858 		return 0;
2859 	}
2860 	lua_pushnumber(L, unit->metalExtract);
2861 	return 1;
2862 }
2863 
2864 
GetUnitExperience(lua_State * L)2865 int LuaSyncedRead::GetUnitExperience(lua_State* L)
2866 {
2867 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
2868 	if (unit == NULL) {
2869 		return 0;
2870 	}
2871 	lua_pushnumber(L, unit->experience);
2872 	lua_pushnumber(L, unit->limExperience);
2873 	return 2;
2874 }
2875 
2876 
GetUnitHeight(lua_State * L)2877 int LuaSyncedRead::GetUnitHeight(lua_State* L)
2878 {
2879 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
2880 	if (unit == NULL) {
2881 		return 0;
2882 	}
2883 	lua_pushnumber(L, unit->height);
2884 	return 1;
2885 }
2886 
2887 
GetUnitRadius(lua_State * L)2888 int LuaSyncedRead::GetUnitRadius(lua_State* L)
2889 {
2890 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
2891 	if (unit == NULL) {
2892 		return 0;
2893 	}
2894 	lua_pushnumber(L, unit->radius);
2895 	return 1;
2896 }
2897 
2898 
GetUnitPosition(lua_State * L)2899 int LuaSyncedRead::GetUnitPosition(lua_State* L)
2900 {
2901 	return (GetSolidObjectPosition(L, ParseUnit(L, __FUNCTION__, 1), false));
2902 }
2903 
2904 
GetUnitBasePosition(lua_State * L)2905 int LuaSyncedRead::GetUnitBasePosition(lua_State* L)
2906 {
2907 	return (GetUnitPosition(L));
2908 }
2909 
GetUnitVectors(lua_State * L)2910 int LuaSyncedRead::GetUnitVectors(lua_State* L)
2911 {
2912 	CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
2913 	if (unit == NULL) {
2914 		return 0;
2915 	}
2916 
2917 #define PACK_VECTOR(n) \
2918 	lua_createtable(L, 3, 0);            \
2919 	lua_pushnumber(L, unit-> n .x); lua_rawseti(L, -2, 1); \
2920 	lua_pushnumber(L, unit-> n .y); lua_rawseti(L, -2, 2); \
2921 	lua_pushnumber(L, unit-> n .z); lua_rawseti(L, -2, 3)
2922 
2923 	PACK_VECTOR(frontdir);
2924 	PACK_VECTOR(updir);
2925 	PACK_VECTOR(rightdir);
2926 
2927 	return 3;
2928 }
2929 
2930 
GetUnitRotation(lua_State * L)2931 int LuaSyncedRead::GetUnitRotation(lua_State* L)
2932 {
2933 	const CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
2934 
2935 	if (unit == NULL)
2936 		return 0;
2937 
2938 	const CMatrix44f& matrix = unit->GetTransformMatrix(CLuaHandle::GetHandleSynced(L));
2939 	const float3 xdir = (matrix.GetX() * XYVector).SafeNormalize(); // worldspace, NOT unitspace
2940 	const float3 ydir = (matrix.GetY() * YZVector).SafeNormalize();
2941 	const float3 zdir = (matrix.GetZ() * XZVector).SafeNormalize();
2942 
2943 	const float pitchAngle = math::acosf(Clamp(math::fabs(ydir.dot( UpVector)), -1.0f, 1.0f)); // x
2944 	const float yawAngle   = math::acosf(Clamp(math::fabs(zdir.dot(FwdVector)), -1.0f, 1.0f)); // y
2945 	const float rollAngle  = math::acosf(Clamp(math::fabs(xdir.dot(RgtVector)), -1.0f, 1.0f)); // z
2946 
2947 	assert(matrix.IsOrthoNormal() == 0);
2948 
2949 	// FIXME: inaccurate with chained rotations (output != input)
2950 	lua_pushnumber(L, -pitchAngle * Sign(ydir.dot(FwdVector))); // x
2951 	lua_pushnumber(L,   -yawAngle * Sign(zdir.dot(RgtVector))); // y
2952 	lua_pushnumber(L,  -rollAngle * Sign(xdir.dot( UpVector))); // z
2953 	return 3;
2954 }
2955 
GetUnitDirection(lua_State * L)2956 int LuaSyncedRead::GetUnitDirection(lua_State* L)
2957 {
2958 	const CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
2959 
2960 	if (unit == NULL)
2961 		return 0;
2962 
2963 	lua_pushnumber(L, unit->frontdir.x);
2964 	lua_pushnumber(L, unit->frontdir.y);
2965 	lua_pushnumber(L, unit->frontdir.z);
2966 	return 3;
2967 }
2968 
2969 
GetUnitHeading(lua_State * L)2970 int LuaSyncedRead::GetUnitHeading(lua_State* L)
2971 {
2972 	CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
2973 	if (unit == NULL) {
2974 		return 0;
2975 	}
2976 	lua_pushnumber(L, unit->heading);
2977 	return 1;
2978 }
2979 
2980 
GetUnitVelocity(lua_State * L)2981 int LuaSyncedRead::GetUnitVelocity(lua_State* L)
2982 {
2983 	return (GetWorldObjectVelocity(L, ParseInLosUnit(L, __FUNCTION__, 1), false));
2984 }
2985 
2986 
GetUnitBuildFacing(lua_State * L)2987 int LuaSyncedRead::GetUnitBuildFacing(lua_State* L)
2988 {
2989 	CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
2990 	if (unit == NULL) {
2991 		return 0;
2992 	}
2993 	lua_pushnumber(L, unit->buildFacing);
2994 	return 1;
2995 }
2996 
2997 
GetUnitIsBuilding(lua_State * L)2998 int LuaSyncedRead::GetUnitIsBuilding(lua_State* L)
2999 {
3000 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3001 	if (unit == NULL) {
3002 		return 0;
3003 	}
3004 	const CBuilder* builder = dynamic_cast<CBuilder*>(unit);
3005 	if (builder && builder->curBuild) {
3006 		lua_pushnumber(L, builder->curBuild->id);
3007 		return 1;
3008 	}
3009 	const CFactory* factory = dynamic_cast<CFactory*>(unit);
3010 	if (factory && factory->curBuild) {
3011 		lua_pushnumber(L, factory->curBuild->id);
3012 		return 1;
3013 	}
3014 	return 0;
3015 }
3016 
3017 
GetUnitCurrentBuildPower(lua_State * L)3018 int LuaSyncedRead::GetUnitCurrentBuildPower(lua_State* L)
3019 {
3020 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3021 	if (unit == NULL) {
3022 		return 0;
3023 	}
3024 
3025 	const NanoPieceCache* pieceCache = NULL;
3026 
3027 	{
3028 		const CBuilder* builder = dynamic_cast<const CBuilder*>(unit);
3029 
3030 		if (builder != NULL) {
3031 			pieceCache = &builder->GetNanoPieceCache();
3032 		}
3033 
3034 		const CFactory* factory = dynamic_cast<const CFactory*>(unit);
3035 
3036 		if (factory != NULL) {
3037 			pieceCache = &factory->GetNanoPieceCache();
3038 		}
3039 	}
3040 
3041 	if (pieceCache == NULL)
3042 		return 0;
3043 
3044 	lua_pushnumber(L, pieceCache->GetBuildPower());
3045 	return 1;
3046 }
3047 
3048 
GetUnitHarvestStorage(lua_State * L)3049 int LuaSyncedRead::GetUnitHarvestStorage(lua_State* L)
3050 {
3051 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3052 	if (unit == NULL) {
3053 		return 0;
3054 	}
3055 
3056 	lua_pushnumber(L, unit->harvestStorage);
3057 	return 1;
3058 }
3059 
3060 
GetUnitNanoPieces(lua_State * L)3061 int LuaSyncedRead::GetUnitNanoPieces(lua_State* L)
3062 {
3063 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3064 	if (unit == NULL) {
3065 		return 0;
3066 	}
3067 
3068 	const NanoPieceCache* pieceCache = NULL;
3069 	const std::vector<int>* nanoPieces = NULL;
3070 
3071 	{
3072 		const CBuilder* builder = dynamic_cast<const CBuilder*>(unit);
3073 
3074 		if (builder != NULL) {
3075 			pieceCache = &builder->GetNanoPieceCache();
3076 			nanoPieces = &pieceCache->GetNanoPieces();
3077 		}
3078 
3079 		const CFactory* factory = dynamic_cast<const CFactory*>(unit);
3080 
3081 		if (factory != NULL) {
3082 			pieceCache = &factory->GetNanoPieceCache();
3083 			nanoPieces = &pieceCache->GetNanoPieces();
3084 		}
3085 	}
3086 
3087 	if (nanoPieces == NULL || nanoPieces->empty())
3088 		return 0;
3089 
3090 	lua_createtable(L, nanoPieces->size(), 0);
3091 
3092 	for (size_t p = 0; p < nanoPieces->size(); p++) {
3093 		const int modelPieceNum = (*nanoPieces)[p];
3094 
3095 		lua_pushnumber(L, modelPieceNum + 1); //lua 1-indexed, c++ 0-indexed
3096 		lua_rawseti(L, -2, p + 1);
3097 	}
3098 
3099 	return 1;
3100 }
3101 
3102 
GetUnitTransporter(lua_State * L)3103 int LuaSyncedRead::GetUnitTransporter(lua_State* L)
3104 {
3105 	CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
3106 	if (unit == NULL) {
3107 		return 0;
3108 	}
3109 	if (unit->transporter == 0) {
3110 		return 0;
3111 	}
3112 	lua_pushnumber(L, unit->transporter->id);
3113 	return 1;
3114 }
3115 
3116 
GetUnitIsTransporting(lua_State * L)3117 int LuaSyncedRead::GetUnitIsTransporting(lua_State* L)
3118 {
3119 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3120 	if (unit == NULL) {
3121 		return 0;
3122 	}
3123 	const CTransportUnit* tu = dynamic_cast<CTransportUnit*>(unit);
3124 	if (tu == NULL) {
3125 		return 0;
3126 	}
3127 	lua_newtable(L);
3128 	std::list<CTransportUnit::TransportedUnit>::const_iterator it;
3129 	int count = 1;
3130 	for (it = tu->GetTransportedUnits().begin(); it != tu->GetTransportedUnits().end(); ++it) {
3131 		const CUnit* carried = it->unit;
3132 		lua_pushnumber(L, carried->id);
3133 		lua_rawseti(L, -2, count++);
3134 	}
3135 
3136 	return 1;
3137 }
3138 
3139 
GetUnitShieldState(lua_State * L)3140 int LuaSyncedRead::GetUnitShieldState(lua_State* L)
3141 {
3142 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3143 	if (unit == NULL) {
3144 		return 0;
3145 	}
3146 
3147 	const CPlasmaRepulser* shield = NULL;
3148 	const size_t idx = luaL_optint(L, 2, -1) - LUA_WEAPON_BASE_INDEX;
3149 
3150 	if (idx >= unit->weapons.size()) {
3151 		shield = static_cast<const CPlasmaRepulser*>(unit->shieldWeapon);
3152 	} else {
3153 		shield = dynamic_cast<const CPlasmaRepulser*>(unit->weapons[idx]);
3154 	}
3155 
3156 	if (shield == NULL) {
3157 		return 0;
3158 	}
3159 
3160 	lua_pushnumber(L, shield->IsEnabled());
3161 	lua_pushnumber(L, shield->GetCurPower());
3162 	return 2;
3163 }
3164 
3165 
GetUnitFlanking(lua_State * L)3166 int LuaSyncedRead::GetUnitFlanking(lua_State* L)
3167 {
3168 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3169 	if (unit == NULL) {
3170 		return 0;
3171 	}
3172 	if (lua_israwstring(L, 2)) {
3173 		const string key = lua_tostring(L, 2);
3174 		if (key == "mode") {
3175 			lua_pushnumber(L, unit->flankingBonusMode);
3176 			return 1;
3177 		}
3178 		else if (key == "dir") {
3179 			lua_pushnumber(L, unit->flankingBonusDir.x);
3180 			lua_pushnumber(L, unit->flankingBonusDir.y);
3181 			lua_pushnumber(L, unit->flankingBonusDir.z);
3182 			return 3;
3183 		}
3184 		else if (key == "moveFactor") {
3185 			lua_pushnumber(L, unit->flankingBonusMobilityAdd);
3186 			return 1;
3187 		}
3188 		else if (key == "minDamage") {
3189 			lua_pushnumber(L, unit->flankingBonusAvgDamage -
3190 												unit->flankingBonusDifDamage);
3191 			return 1;
3192 		}
3193 		else if (key == "maxDamage") {
3194 			lua_pushnumber(L, unit->flankingBonusAvgDamage +
3195 												unit->flankingBonusDifDamage);
3196 			return 1;
3197 		}
3198 	}
3199 	else if (lua_isnoneornil(L, 2)) {
3200 		lua_pushnumber(L, unit->flankingBonusMode);
3201 		lua_pushnumber(L, unit->flankingBonusMobilityAdd);
3202 		lua_pushnumber(L, unit->flankingBonusAvgDamage - // min
3203 		                  unit->flankingBonusDifDamage);
3204 		lua_pushnumber(L, unit->flankingBonusAvgDamage + // max
3205 		                  unit->flankingBonusDifDamage);
3206 		lua_pushnumber(L, unit->flankingBonusDir.x);
3207 		lua_pushnumber(L, unit->flankingBonusDir.y);
3208 		lua_pushnumber(L, unit->flankingBonusDir.z);
3209 		return 7;
3210 	}
3211 
3212 	return 0;
3213 }
3214 
3215 
GetUnitWeaponState(lua_State * L)3216 int LuaSyncedRead::GetUnitWeaponState(lua_State* L)
3217 {
3218 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3219 	if (unit == NULL) {
3220 		return 0;
3221 	}
3222 
3223 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
3224 
3225 	if (weaponNum >= unit->weapons.size()) {
3226 		return 0;
3227 	}
3228 
3229 	const CWeapon* weapon = unit->weapons[weaponNum];
3230 	const string key = luaL_optstring(L, 3, "");
3231 
3232 	if (key.empty()) { // backwards compatible
3233 		lua_pushboolean(L, weapon->angleGood);
3234 		lua_pushboolean(L, weapon->reloadStatus <= gs->frameNum);
3235 		lua_pushnumber(L,  weapon->reloadStatus);
3236 		lua_pushnumber(L,  weapon->salvoLeft);
3237 		lua_pushnumber(L,  weapon->numStockpiled);
3238 		return 5;
3239 	}
3240 	else if (key == "reloadState" || key == "reloadFrame") {
3241 		lua_pushnumber(L, weapon->reloadStatus);
3242 	}
3243 	else if (key == "reloadTime") {
3244 		lua_pushnumber(L, (weapon->reloadTime / unit->reloadSpeed) / GAME_SPEED);
3245 	}
3246 	else if (key == "accuracy") {
3247 		lua_pushnumber(L, weapon->AccuracyExperience());
3248 	}
3249 	else if (key == "sprayAngle") {
3250 		lua_pushnumber(L, weapon->SprayAngleExperience());
3251 	}
3252 	else if (key == "range") {
3253 		lua_pushnumber(L, weapon->range);
3254 	}
3255 	else if (key == "projectileSpeed") {
3256 		lua_pushnumber(L, weapon->projectileSpeed);
3257 	}
3258 	else if (key == "burst") {
3259 		lua_pushnumber(L, weapon->salvoSize);
3260 	}
3261 	else if (key == "burstRate") {
3262 		lua_pushnumber(L, weapon->salvoDelay / GAME_SPEED);
3263 	}
3264 	else if (key == "projectiles") {
3265 		lua_pushnumber(L, weapon->projectilesPerShot);
3266 	}
3267 	else if (key == "salvoError") {
3268 		float3 salvoError =  weapon->SalvoErrorExperience();
3269 		lua_createtable(L, 3, 0);
3270 		lua_pushnumber(L, salvoError.x); lua_rawseti(L, -2, 1);
3271 		lua_pushnumber(L, salvoError.y); lua_rawseti(L, -2, 2);
3272 		lua_pushnumber(L, salvoError.z); lua_rawseti(L, -2, 3);
3273 	}
3274 	else if (key == "targetMoveError") {
3275 		lua_pushnumber(L, weapon->MoveErrorExperience());
3276 	}
3277 	else {
3278 		return 0;
3279 	}
3280 	return 1;
3281 }
3282 
3283 
GetUnitWeaponVectors(lua_State * L)3284 int LuaSyncedRead::GetUnitWeaponVectors(lua_State* L)
3285 {
3286 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3287 
3288 	if (unit == NULL)
3289 		return 0;
3290 
3291 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
3292 
3293 	if (weaponNum >= unit->weapons.size()) {
3294 		return 0;
3295 	}
3296 
3297 	const CWeapon* weapon = unit->weapons[weaponNum];
3298 	const float3& pos = weapon->weaponMuzzlePos;
3299 	const float3* dir = &weapon->wantedDir;
3300 
3301 	const string& type = weapon->weaponDef->type;
3302 	if ((type == "TorpedoLauncher") ||
3303 	    (type == "MissileLauncher") ||
3304 	    (type == "StarburstLauncher")) {
3305 		dir = &weapon->weaponDir;
3306 	}
3307 
3308 	lua_pushnumber(L, pos.x);
3309 	lua_pushnumber(L, pos.y);
3310 	lua_pushnumber(L, pos.z);
3311 
3312 	lua_pushnumber(L, dir->x);
3313 	lua_pushnumber(L, dir->y);
3314 	lua_pushnumber(L, dir->z);
3315 
3316 	return 6;
3317 }
3318 
3319 
GetUnitWeaponTryTarget(lua_State * L)3320 int LuaSyncedRead::GetUnitWeaponTryTarget(lua_State* L)
3321 {
3322 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3323 
3324 	if (unit == NULL)
3325 		return 0;
3326 
3327 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
3328 
3329 	if (weaponNum >= unit->weapons.size())
3330 		return 0;
3331 
3332 	const CWeapon* weapon = unit->weapons[weaponNum];
3333 	const CUnit* enemy = NULL;
3334 
3335 	float3 pos;
3336 
3337 	//we cannot test calling TryTarget/TestTarget/HaveFreeLineOfFire directly
3338 	// by passing a position thatis not approximately the wanted checked position, because
3339 	//the checks for target using passed position for checking both free line of fire and range
3340 	//which would result in wrong test unless target was by chance near coords <0,0,0>
3341 	//while position alone works because NULL target omits target class validity checks
3342 
3343 	if (lua_gettop(L) >= 5) {
3344 		pos.x = luaL_optnumber(L, 3, 0.0f);
3345 		pos.y = luaL_optnumber(L, 4, 0.0f);
3346 		pos.z = luaL_optnumber(L, 5, 0.0f);
3347 
3348 	} else {
3349 		if ((enemy = ParseUnit(L, __FUNCTION__, 3)) == NULL)
3350 			return 0;
3351 
3352 		pos = weapon->GetUnitPositionWithError(enemy);
3353 	}
3354 
3355 	lua_pushboolean(L, weapon->TryTarget(pos, true, enemy));
3356 	return 1;
3357 }
3358 
3359 
GetUnitWeaponTestTarget(lua_State * L)3360 int LuaSyncedRead::GetUnitWeaponTestTarget(lua_State* L)
3361 {
3362 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3363 
3364 	if (unit == NULL)
3365 		return 0;
3366 
3367 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
3368 
3369 	if (weaponNum >= unit->weapons.size())
3370 		return 0;
3371 
3372 	const CWeapon* weapon = unit->weapons[weaponNum];
3373 	const CUnit* enemy = NULL;
3374 
3375 	float3 pos;
3376 
3377 	if (lua_gettop(L) >= 5) {
3378 		pos.x = luaL_optnumber(L, 3, 0.0f);
3379 		pos.y = luaL_optnumber(L, 4, 0.0f);
3380 		pos.z = luaL_optnumber(L, 5, 0.0f);
3381 	} else {
3382 		if ((enemy = ParseUnit(L, __FUNCTION__, 3)) == NULL)
3383 			return 0;
3384 
3385 		pos = weapon->GetUnitPositionWithError(enemy);
3386 	}
3387 
3388 	lua_pushboolean(L, weapon->TestTarget(pos, true, enemy));
3389 	return 1;
3390 }
3391 
3392 
GetUnitWeaponTestRange(lua_State * L)3393 int LuaSyncedRead::GetUnitWeaponTestRange(lua_State* L)
3394 {
3395 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3396 
3397 	if (unit == NULL)
3398 		return 0;
3399 
3400 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
3401 
3402 	if (weaponNum >= unit->weapons.size())
3403 		return 0;
3404 
3405 	const CWeapon* weapon = unit->weapons[weaponNum];
3406 	const CUnit* enemy = NULL;
3407 
3408 	float3 pos;
3409 
3410 	if (lua_gettop(L) >= 5) {
3411 		pos.x = luaL_optnumber(L, 3, 0.0f);
3412 		pos.y = luaL_optnumber(L, 4, 0.0f);
3413 		pos.z = luaL_optnumber(L, 5, 0.0f);
3414 	} else {
3415 		if ((enemy = ParseUnit(L, __FUNCTION__, 3)) == NULL)
3416 			return 0;
3417 
3418 		pos = weapon->GetUnitPositionWithError(enemy);
3419 	}
3420 
3421 	lua_pushboolean(L, weapon->TestRange(pos, true, enemy));
3422 	return 1;
3423 }
3424 
3425 
GetUnitWeaponHaveFreeLineOfFire(lua_State * L)3426 int LuaSyncedRead::GetUnitWeaponHaveFreeLineOfFire(lua_State* L)
3427 {
3428 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3429 
3430 	if (unit == NULL)
3431 		return 0;
3432 
3433 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
3434 
3435 	if (weaponNum >= unit->weapons.size())
3436 		return 0;
3437 
3438 	const CWeapon* weapon = unit->weapons[weaponNum];
3439 	const CUnit* enemy = NULL;
3440 
3441 	float3 pos;
3442 
3443 	if (lua_gettop(L) >= 5) {
3444 		pos.x = luaL_optnumber(L, 3, 0.0f);
3445 		pos.y = luaL_optnumber(L, 4, 0.0f);
3446 		pos.z = luaL_optnumber(L, 5, 0.0f);
3447 	} else {
3448 		if ((enemy = ParseUnit(L, __FUNCTION__, 3)) == NULL)
3449 			return 0;
3450 
3451 		pos = weapon->GetUnitPositionWithError(enemy);
3452 	}
3453 
3454 	lua_pushboolean(L, weapon->HaveFreeLineOfFire(pos, true, enemy));
3455 	return 1;
3456 }
3457 
GetUnitWeaponCanFire(lua_State * L)3458 int LuaSyncedRead::GetUnitWeaponCanFire(lua_State* L)
3459 {
3460 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3461 
3462 	if (unit == NULL)
3463 		return 0;
3464 
3465 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
3466 
3467 	if (weaponNum >= unit->weapons.size())
3468 		return 0;
3469 
3470 	const bool ignoreAngleGood = luaL_optboolean(L, 3, false);
3471 	const bool ignoreTargetType = luaL_optboolean(L, 4, false);
3472 	const bool ignoreRequestedDir = luaL_optboolean(L, 5, false);
3473 
3474 	lua_pushboolean(L, unit->weapons[weaponNum]->CanFire(ignoreAngleGood, ignoreTargetType, ignoreRequestedDir));
3475 	return 1;
3476 }
3477 
GetUnitWeaponTarget(lua_State * L)3478 int LuaSyncedRead::GetUnitWeaponTarget(lua_State* L)
3479 {
3480 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3481 
3482 	if (unit == NULL)
3483 		return 0;
3484 
3485 	const size_t weaponNum = luaL_checkint(L, 2) - LUA_WEAPON_BASE_INDEX;
3486 
3487 	if (weaponNum >= unit->weapons.size())
3488 		return 0;
3489 
3490 	const CWeapon* weapon = unit->weapons[weaponNum];
3491 	const CWorldObject* target = NULL;
3492 
3493 	lua_pushnumber(L, weapon->targetType);
3494 
3495 	switch (weapon->targetType) {
3496 		case Target_None:
3497 			return 1;
3498 			break;
3499 		case Target_Unit: {
3500 			lua_pushboolean(L, weapon->haveUserTarget);
3501 			target = weapon->targetUnit;
3502 			assert(target != NULL);
3503 			lua_pushnumber(L, (target != NULL)? target->id: -1);
3504 			break;
3505 		}
3506 		case Target_Pos: {
3507 			lua_pushboolean(L, weapon->haveUserTarget);
3508 			lua_createtable(L, 3, 0);
3509 			lua_pushnumber(L, weapon->targetPos.x); lua_rawseti(L, -2, 1);
3510 			lua_pushnumber(L, weapon->targetPos.y); lua_rawseti(L, -2, 2);
3511 			lua_pushnumber(L, weapon->targetPos.z); lua_rawseti(L, -2, 3);
3512 			break;
3513 		}
3514 		case Target_Intercept: {
3515 			lua_pushboolean(L, weapon->haveUserTarget);
3516 			target = weapon->interceptTarget;
3517 			assert(target != NULL);
3518 			lua_pushnumber(L, (target != NULL)? target->id: -1);
3519 			break;
3520 		}
3521 	}
3522 
3523 	return 3;
3524 }
3525 
3526 
GetUnitTravel(lua_State * L)3527 int LuaSyncedRead::GetUnitTravel(lua_State* L)
3528 {
3529 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3530 	if (unit == NULL) {
3531 		return 0;
3532 	}
3533 	lua_pushnumber(L, unit->travel);
3534 	lua_pushnumber(L, unit->travelPeriod);
3535 	return 2;
3536 }
3537 
3538 
GetUnitFuel(lua_State * L)3539 int LuaSyncedRead::GetUnitFuel(lua_State* L)
3540 {
3541 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3542 	if (unit == NULL) {
3543 		return 0;
3544 	}
3545 	lua_pushnumber(L, unit->currentFuel);
3546 	return 1;
3547 }
3548 
3549 
GetUnitEstimatedPath(lua_State * L)3550 int LuaSyncedRead::GetUnitEstimatedPath(lua_State* L)
3551 {
3552 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3553 	if (unit == NULL) {
3554 		return 0;
3555 	}
3556 
3557 	const CGroundMoveType* gmt = dynamic_cast<const CGroundMoveType*>(unit->moveType);
3558 
3559 	if (gmt == NULL) {
3560 		return 0;
3561 	}
3562 
3563 	return (LuaPathFinder::PushPathNodes(L, gmt->GetPathID()));
3564 }
3565 
3566 
GetUnitLastAttacker(lua_State * L)3567 int LuaSyncedRead::GetUnitLastAttacker(lua_State* L)
3568 {
3569 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
3570 	if (unit == NULL) {
3571 		return 0;
3572 	}
3573 	if ((unit->lastAttacker == NULL) ||
3574 	    !IsUnitVisible(L, unit->lastAttacker)) {
3575 		return 0;
3576 	}
3577 	lua_pushnumber(L, unit->lastAttacker->id);
3578 	return 1;
3579 }
3580 
GetUnitLastAttackedPiece(lua_State * L)3581 int LuaSyncedRead::GetUnitLastAttackedPiece(lua_State* L)
3582 {
3583 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1); // ?
3584 
3585 	if (unit == NULL)
3586 		return 0;
3587 	if (unit->lastAttackedPiece == NULL)
3588 		return 0;
3589 
3590 	const LocalModelPiece* lmp = unit->lastAttackedPiece;
3591 	const S3DModelPiece* omp = lmp->original;
3592 
3593 	if (lua_isboolean(L, 1) && lua_toboolean(L, 1)) {
3594 		lua_pushnumber(L, lmp->GetLModelPieceIndex() + 1);
3595 	} else {
3596 		lua_pushsstring(L, omp->name);
3597 	}
3598 
3599 	lua_pushnumber(L, unit->lastAttackedPieceFrame);
3600 	return 2;
3601 }
3602 
3603 
GetUnitCollisionVolumeData(lua_State * L)3604 int LuaSyncedRead::GetUnitCollisionVolumeData(lua_State* L)
3605 {
3606 	const CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
3607 	if (unit == NULL) {
3608 		return 0;
3609 	}
3610 
3611 	return (PushCollisionVolumeData(L, unit->collisionVolume));
3612 }
3613 
GetUnitPieceCollisionVolumeData(lua_State * L)3614 int LuaSyncedRead::GetUnitPieceCollisionVolumeData(lua_State* L)
3615 {
3616 	const CUnit* unit = ParseInLosUnit(L, __FUNCTION__, 1);
3617 	if (unit == NULL) {
3618 		return 0;
3619 	}
3620 
3621 	const LocalModel* lm = unit->localModel;
3622 	const int pieceIndex = luaL_checkint(L, 2);
3623 
3624 	if (pieceIndex < 0 || pieceIndex >= lm->pieces.size()) {
3625 		return 0;
3626 	}
3627 
3628 	const LocalModelPiece* lmp = lm->pieces[pieceIndex];
3629 	const CollisionVolume* vol = lmp->GetCollisionVolume();
3630 
3631 	return (PushCollisionVolumeData(L, vol));
3632 }
3633 
3634 
GetUnitLosState(lua_State * L)3635 int LuaSyncedRead::GetUnitLosState(lua_State* L)
3636 {
3637 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
3638 	if (unit == NULL) {
3639 		return 0;
3640 	}
3641 
3642 	int allyTeam = CLuaHandle::GetHandleReadAllyTeam(L);
3643 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
3644 		if (!CLuaHandle::GetHandleFullRead(L)) {
3645 			return 0;
3646 		}
3647 		allyTeam = luaL_checkint(L, 2);
3648 	}
3649 	if (!teamHandler->IsValidAllyTeam(allyTeam)) {
3650 		return 0;
3651 	}
3652 	const unsigned short losStatus = unit->losStatus[allyTeam];
3653 
3654 	if (CLuaHandle::GetHandleFullRead(L) && luaL_optboolean(L, 3, false)) {
3655 		lua_pushnumber(L, losStatus); // return a numeric value
3656 		return 1;
3657 	}
3658 
3659 	lua_createtable(L, 0, 3);
3660 	if (losStatus & LOS_INLOS) {
3661 		HSTR_PUSH_BOOL(L, "los", true);
3662 	}
3663 	if (losStatus & LOS_INRADAR) {
3664 		HSTR_PUSH_BOOL(L, "radar", true);
3665 	}
3666 	const int prevMask = (LOS_PREVLOS | LOS_CONTRADAR);
3667 	if ((losStatus & LOS_INLOS) ||
3668 	    ((losStatus & prevMask) == prevMask)) {
3669 		HSTR_PUSH_BOOL(L, "typed", true);
3670 	}
3671 	return 1;
3672 }
3673 
3674 
GetUnitSeparation(lua_State * L)3675 int LuaSyncedRead::GetUnitSeparation(lua_State* L)
3676 {
3677 	CUnit* unit1 = ParseUnit(L, __FUNCTION__, 1);
3678 	CUnit* unit2 = ParseUnit(L, __FUNCTION__, 2);
3679 	if (unit1 == NULL || unit2 == NULL) {
3680 		return 0;
3681 	}
3682 
3683 	float3 pos1 = unit1->midPos;
3684 	float3 pos2 = unit2->midPos;
3685 
3686 	if (!IsAllyUnit(L, unit1)) {
3687 		pos1 = unit1->GetErrorPos(CLuaHandle::GetHandleReadAllyTeam(L));
3688 	}
3689 	if (!IsAllyUnit(L, unit2)) {
3690 		pos2 = unit2->GetErrorPos(CLuaHandle::GetHandleReadAllyTeam(L));
3691 	}
3692 
3693 	float dist;
3694 	if (luaL_optboolean(L, 3, false)) {
3695 		dist = pos1.distance2D(pos2);
3696 	} else {
3697 		dist = pos1.distance(pos2);
3698 	}
3699 	if (luaL_optboolean(L, 4, false)) {
3700 		dist = std::max(0.0f, dist - unit1->radius - unit2->radius);
3701 	}
3702 	lua_pushnumber(L, dist);
3703 
3704 	return 1;
3705 }
3706 
3707 
GetUnitDefDimensions(lua_State * L)3708 int LuaSyncedRead::GetUnitDefDimensions(lua_State* L)
3709 {
3710 	const int unitDefID = luaL_checkint(L, 1);
3711 	const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
3712 	if (ud == NULL) {
3713 		return 0;
3714 	}
3715 	const S3DModel* model = ud->LoadModel();
3716 	if (model == NULL) {
3717 		return 0;
3718 	}
3719 	const S3DModel& m = *model;
3720 	const float3& mid = model->relMidPos;
3721 	lua_newtable(L);
3722 	HSTR_PUSH_NUMBER(L, "height", m.height);
3723 	HSTR_PUSH_NUMBER(L, "radius", m.radius);
3724 	HSTR_PUSH_NUMBER(L, "midx",   mid.x);
3725 	HSTR_PUSH_NUMBER(L, "minx",   m.mins.x);
3726 	HSTR_PUSH_NUMBER(L, "maxx",   m.maxs.x);
3727 	HSTR_PUSH_NUMBER(L, "midy",   mid.y);
3728 	HSTR_PUSH_NUMBER(L, "miny",   m.mins.y);
3729 	HSTR_PUSH_NUMBER(L, "maxy",   m.maxs.y);
3730 	HSTR_PUSH_NUMBER(L, "midz",   mid.z);
3731 	HSTR_PUSH_NUMBER(L, "minz",   m.mins.z);
3732 	HSTR_PUSH_NUMBER(L, "maxz",   m.maxs.z);
3733 	return 1;
3734 }
3735 
3736 
GetUnitBlocking(lua_State * L)3737 int LuaSyncedRead::GetUnitBlocking(lua_State* L)
3738 {
3739 	return (GetSolidObjectBlocking(L, ParseTypedUnit(L, __FUNCTION__, 1)));
3740 }
3741 
3742 
GetUnitMoveTypeData(lua_State * L)3743 int LuaSyncedRead::GetUnitMoveTypeData(lua_State* L)
3744 {
3745 	const CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3746 	if (unit == NULL) {
3747 		return 0;
3748 	}
3749 
3750 	AMoveType* amt = unit->moveType;
3751 
3752 	lua_newtable(L);
3753 	HSTR_PUSH_NUMBER(L, "maxSpeed", amt->GetMaxSpeed() * GAME_SPEED);
3754 	HSTR_PUSH_NUMBER(L, "maxWantedSpeed", amt->GetMaxWantedSpeed() * GAME_SPEED);
3755 	HSTR_PUSH_NUMBER(L, "goalx", amt->goalPos.x);
3756 	HSTR_PUSH_NUMBER(L, "goaly", amt->goalPos.y);
3757 	HSTR_PUSH_NUMBER(L, "goalz", amt->goalPos.z);
3758 
3759 	const CGroundMoveType* groundmt = dynamic_cast<CGroundMoveType*>(unit->moveType);
3760 
3761 	if (groundmt != NULL) {
3762 		HSTR_PUSH_STRING(L, "name", "ground");
3763 
3764 		HSTR_PUSH_NUMBER(L, "turnRate", groundmt->GetTurnRate());
3765 		HSTR_PUSH_NUMBER(L, "accRate", groundmt->GetAccRate());
3766 		HSTR_PUSH_NUMBER(L, "decRate", groundmt->GetDecRate());
3767 
3768 		HSTR_PUSH_NUMBER(L, "maxReverseSpeed", groundmt->GetMaxReverseSpeed() * GAME_SPEED);
3769 		HSTR_PUSH_NUMBER(L, "wantedSpeed", groundmt->GetWantedSpeed() * GAME_SPEED);
3770 		HSTR_PUSH_NUMBER(L, "currentSpeed", groundmt->GetCurrentSpeed() * GAME_SPEED);
3771 
3772 		HSTR_PUSH_NUMBER(L, "goalRadius", groundmt->GetGoalRadius());
3773 
3774 		HSTR_PUSH_NUMBER(L, "currwaypointx", (groundmt->GetCurrWayPoint()).x);
3775 		HSTR_PUSH_NUMBER(L, "currwaypointy", (groundmt->GetCurrWayPoint()).y);
3776 		HSTR_PUSH_NUMBER(L, "currwaypointz", (groundmt->GetCurrWayPoint()).z);
3777 		HSTR_PUSH_NUMBER(L, "nextwaypointx", (groundmt->GetNextWayPoint()).x);
3778 		HSTR_PUSH_NUMBER(L, "nextwaypointy", (groundmt->GetNextWayPoint()).y);
3779 		HSTR_PUSH_NUMBER(L, "nextwaypointz", (groundmt->GetNextWayPoint()).z);
3780 
3781 		HSTR_PUSH_NUMBER(L, "requestedSpeed", 0.0f);
3782 
3783 		HSTR_PUSH_NUMBER(L, "pathFailures", 0);
3784 
3785 		return 1;
3786 	}
3787 
3788 	const AAirMoveType* aamt = dynamic_cast<AAirMoveType*>(unit->moveType);
3789 
3790 	if (aamt != NULL) {
3791 		HSTR_PUSH_NUMBER(L, "padStatus", aamt->GetPadStatus());
3792 		HSTR_PUSH_NUMBER(L, "repairBelowHealth", aamt->GetRepairBelowHealth());
3793 	}
3794 
3795 	const CHoverAirMoveType* hAMT = dynamic_cast<CHoverAirMoveType*>(unit->moveType);
3796 
3797 	if (hAMT != NULL) {
3798 		HSTR_PUSH_STRING(L, "name", "gunship");
3799 
3800 		HSTR_PUSH_NUMBER(L, "wantedHeight", hAMT->wantedHeight);
3801 		HSTR_PUSH_BOOL(L, "collide", hAMT->collide);
3802 		HSTR_PUSH_BOOL(L, "useSmoothMesh", hAMT->useSmoothMesh);
3803 
3804 		switch (hAMT->aircraftState) {
3805 			case AAirMoveType::AIRCRAFT_LANDED:
3806 				HSTR_PUSH_STRING(L, "aircraftState", "landed");
3807 				break;
3808 			case AAirMoveType::AIRCRAFT_FLYING:
3809 				HSTR_PUSH_STRING(L, "aircraftState", "flying");
3810 				break;
3811 			case AAirMoveType::AIRCRAFT_LANDING:
3812 				HSTR_PUSH_STRING(L, "aircraftState", "landing");
3813 				break;
3814 			case AAirMoveType::AIRCRAFT_CRASHING:
3815 				HSTR_PUSH_STRING(L, "aircraftState", "crashing");
3816 				break;
3817 			case AAirMoveType::AIRCRAFT_TAKEOFF:
3818 				HSTR_PUSH_STRING(L, "aircraftState", "takeoff");
3819 				break;
3820 			case AAirMoveType::AIRCRAFT_HOVERING:
3821 				HSTR_PUSH_STRING(L, "aircraftState", "hovering");
3822 				break;
3823 		};
3824 
3825 		switch (hAMT->flyState) {
3826 			case CHoverAirMoveType::FLY_CRUISING:
3827 				HSTR_PUSH_STRING(L, "flyState", "cruising");
3828 				break;
3829 			case CHoverAirMoveType::FLY_CIRCLING:
3830 				HSTR_PUSH_STRING(L, "flyState", "circling");
3831 				break;
3832 			case CHoverAirMoveType::FLY_ATTACKING:
3833 				HSTR_PUSH_STRING(L, "flyState", "attacking");
3834 				break;
3835 			case CHoverAirMoveType::FLY_LANDING:
3836 				HSTR_PUSH_STRING(L, "flyState", "landing");
3837 				break;
3838 		}
3839 
3840 		HSTR_PUSH_NUMBER(L, "goalDistance", hAMT->goalDistance);
3841 
3842 		HSTR_PUSH_BOOL(L, "bankingAllowed", hAMT->bankingAllowed);
3843 		HSTR_PUSH_NUMBER(L, "currentBank", hAMT->currentBank);
3844 		HSTR_PUSH_NUMBER(L, "currentPitch", hAMT->currentPitch);
3845 
3846 		HSTR_PUSH_NUMBER(L, "turnRate", hAMT->turnRate);
3847 		HSTR_PUSH_NUMBER(L, "accRate", hAMT->accRate);
3848 		HSTR_PUSH_NUMBER(L, "decRate", hAMT->decRate);
3849 		HSTR_PUSH_NUMBER(L, "altitudeRate", hAMT->altitudeRate);
3850 
3851 		HSTR_PUSH_NUMBER(L, "brakeDistance", -1.0f); // DEPRECATED
3852 		HSTR_PUSH_BOOL(L, "dontLand", hAMT->GetAllowLanding());
3853 		HSTR_PUSH_NUMBER(L, "maxDrift", hAMT->maxDrift);
3854 
3855 		return 1;
3856 	}
3857 
3858 	const CStrafeAirMoveType* sAMT = dynamic_cast<CStrafeAirMoveType*>(unit->moveType);
3859 
3860 	if (sAMT != NULL) {
3861 		HSTR_PUSH_STRING(L, "name", "airplane");
3862 
3863 		switch (sAMT->aircraftState) {
3864 			case AAirMoveType::AIRCRAFT_LANDED:
3865 				HSTR_PUSH_STRING(L, "aircraftState", "landed");
3866 				break;
3867 			case AAirMoveType::AIRCRAFT_FLYING:
3868 				HSTR_PUSH_STRING(L, "aircraftState", "flying");
3869 				break;
3870 			case AAirMoveType::AIRCRAFT_LANDING:
3871 				HSTR_PUSH_STRING(L, "aircraftState", "landing");
3872 				break;
3873 			case AAirMoveType::AIRCRAFT_CRASHING:
3874 				HSTR_PUSH_STRING(L, "aircraftState", "crashing");
3875 				break;
3876 			case AAirMoveType::AIRCRAFT_TAKEOFF:
3877 				HSTR_PUSH_STRING(L, "aircraftState", "takeoff");
3878 				break;
3879 			case AAirMoveType::AIRCRAFT_HOVERING:
3880 				HSTR_PUSH_STRING(L, "aircraftState", "hovering");
3881 				break;
3882 		};
3883 		HSTR_PUSH_NUMBER(L, "wantedHeight", sAMT->wantedHeight);
3884 		HSTR_PUSH_BOOL(L, "collide", sAMT->collide);
3885 		HSTR_PUSH_BOOL(L, "useSmoothMesh", sAMT->useSmoothMesh);
3886 
3887 		HSTR_PUSH_NUMBER(L, "myGravity", sAMT->myGravity);
3888 
3889 		HSTR_PUSH_NUMBER(L, "maxBank", sAMT->maxBank);
3890 		HSTR_PUSH_NUMBER(L, "maxPitch", sAMT->maxBank);
3891 		HSTR_PUSH_NUMBER(L, "turnRadius", sAMT->turnRadius);
3892 
3893 		HSTR_PUSH_NUMBER(L, "maxAcc", sAMT->accRate);
3894 		HSTR_PUSH_NUMBER(L, "maxAileron", sAMT->maxAileron);
3895 		HSTR_PUSH_NUMBER(L, "maxElevator", sAMT->maxElevator);
3896 		HSTR_PUSH_NUMBER(L, "maxRudder", sAMT->maxRudder);
3897 
3898 		return 1;
3899 	}
3900 
3901 	const CStaticMoveType* staticmt = dynamic_cast<CStaticMoveType*>(unit->moveType);
3902 
3903 	if (staticmt != NULL) {
3904 		HSTR_PUSH_STRING(L, "name", "static");
3905 		return 1;
3906 	}
3907 
3908 	const CScriptMoveType* scriptmt = dynamic_cast<CScriptMoveType*>(unit->moveType);
3909 
3910 	if (scriptmt != NULL) {
3911 		HSTR_PUSH_STRING(L, "name", "script");
3912 		return 1;
3913 	}
3914 
3915 	HSTR_PUSH_STRING(L, "name", "unknown");
3916 	return 1;
3917 }
3918 
3919 
3920 
3921 /******************************************************************************/
3922 
PackCommand(lua_State * L,const Command & cmd)3923 static void PackCommand(lua_State* L, const Command& cmd)
3924 {
3925 	lua_createtable(L, 0, 4);
3926 
3927 	HSTR_PUSH_NUMBER(L, "id", cmd.GetID());
3928 
3929 	// t["params"] = {[1] = param1, ...}
3930 	LuaUtils::PushCommandParamsTable(L, cmd, true);
3931 	// t["options"] = {key1 = val1, ...}
3932 	LuaUtils::PushCommandOptionsTable(L, cmd, true);
3933 
3934 	HSTR_PUSH_NUMBER(L, "tag", cmd.tag);
3935 }
3936 
3937 
PackCommandQueue(lua_State * L,const CCommandQueue & commands,size_t count)3938 static void PackCommandQueue(lua_State* L, const CCommandQueue& commands, size_t count)
3939 {
3940 	lua_createtable(L, commands.size(), 0);
3941 
3942 	size_t c = 0;
3943 
3944 	// get the desired number of commands to return
3945 	if (count == -1u) {
3946 		count = commands.size();
3947 	}
3948 
3949 	// {[1] = cq[0], [2] = cq[1], ...}
3950 	for (auto ci = commands.begin(); ci != commands.end(); ++ci) {
3951 		if (c >= count)
3952 			break;
3953 
3954 		PackCommand(L, *ci);
3955 		lua_rawseti(L, -2, ++c);
3956 	}
3957 }
3958 
3959 
GetUnitCommands(lua_State * L)3960 int LuaSyncedRead::GetUnitCommands(lua_State* L)
3961 {
3962 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
3963 
3964 	if (unit == NULL)
3965 		return 0;
3966 
3967 	const CCommandAI* commandAI = unit->commandAI;
3968 
3969 	if (commandAI == NULL)
3970 		return 0;
3971 
3972 	// send the new unit commands for factories, otherwise the normal commands
3973 	const CFactoryCAI* factoryCAI = dynamic_cast<const CFactoryCAI*>(commandAI);
3974 	const CCommandQueue* queue = (factoryCAI == NULL) ? &commandAI->commandQue : &factoryCAI->newUnitCommands;
3975 
3976 	// performance relevant `debug` message
3977 	if (lua_isnoneornil(L, 2)) {
3978 		static int calls = 0;
3979 		static spring_time nextWarning = spring_gettime();
3980 		calls++;
3981 		if (spring_gettime() >= nextWarning) {
3982 			nextWarning = spring_gettime() + spring_secs(5);
3983 			if (calls > 1000) {
3984 				luaL_error(L,
3985 					"[%s] called too often without a 2nd argument to define maxNumCmds returned in the table, please check your code!\n"
3986 					"Especially when you only read the first cmd or want to check if the queue is non-empty, this can be a huge performance leak!\n", __FUNCTION__);
3987 			}
3988 			calls = 0;
3989 		}
3990 	}
3991 
3992 	const int  numCmds   = luaL_optint(L, 2, -1);
3993 	const bool cmdsTable = luaL_optboolean(L, 3, true); // deprecated, prefer to set 2nd arg to 0
3994 
3995 	if (cmdsTable && (numCmds != 0)) {
3996 		// *get wants the actual commands
3997 		PackCommandQueue(L, *queue, numCmds);
3998 	} else {
3999 		// *get just wants the queue's size
4000 		lua_pushnumber(L, queue->size());
4001 	}
4002 
4003 	return 1;
4004 }
4005 
GetFactoryCommands(lua_State * L)4006 int LuaSyncedRead::GetFactoryCommands(lua_State* L)
4007 {
4008 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
4009 
4010 	if (unit == NULL)
4011 		return 0;
4012 
4013 	const CCommandAI* commandAI = unit->commandAI;
4014 	const CFactoryCAI* factoryCAI = dynamic_cast<const CFactoryCAI*>(commandAI);
4015 
4016 	// bail if not a factory
4017 	if (factoryCAI == NULL)
4018 		return 0;
4019 
4020 	const CCommandQueue& commandQue = factoryCAI->commandQue;
4021 
4022 	const int  numCmds   = luaL_optint(L, 2, -1);
4023 	const bool cmdsTable = luaL_optboolean(L, 3, true); // deprecated, prefer to set 2nd arg to 0
4024 
4025 	if (cmdsTable && (numCmds != 0)) {
4026 		PackCommandQueue(L, commandQue, numCmds);
4027 	} else {
4028 		lua_pushnumber(L, commandQue.size());
4029 	}
4030 
4031 	return 1;
4032 }
4033 
4034 
PackFactoryCounts(lua_State * L,const CCommandQueue & q,int count,bool noCmds)4035 static void PackFactoryCounts(lua_State* L,
4036                               const CCommandQueue& q, int count, bool noCmds)
4037 {
4038 	lua_newtable(L);
4039 
4040 	int entry = 0;
4041 	int currentCmd = 0;
4042 	int currentCount = 0;
4043 
4044 	CCommandQueue::const_iterator it = q.begin();
4045 	for (it = q.begin(); it != q.end(); ++it) {
4046 		if (entry >= count) {
4047 			currentCount = 0;
4048 			break;
4049 		}
4050 		const int& cmdID = it->GetID();
4051 		if (noCmds && (cmdID >= 0)) {
4052 			continue;
4053 		}
4054 
4055 		if (entry == 0) {
4056 			currentCmd = cmdID;
4057 			currentCount = 1;
4058 			entry = 1;
4059 		}
4060 		else if (cmdID == currentCmd) {
4061 			currentCount++;
4062 		}
4063 		else {
4064 			entry++;
4065 			lua_newtable(L); {
4066 				lua_pushnumber(L, currentCount);
4067 				lua_rawseti(L, -2, -currentCmd);
4068 			}
4069 			lua_rawseti(L, -2, entry);
4070 			currentCmd = cmdID;
4071 			currentCount = 1;
4072 		}
4073 	}
4074 	if (currentCount > 0) {
4075 		entry++;
4076 		lua_newtable(L); {
4077 			lua_pushnumber(L, currentCount);
4078 			lua_rawseti(L, -2, -currentCmd);
4079 		}
4080 		lua_rawseti(L, -2, entry);
4081 	}
4082 
4083 	hs_n.PushNumber(L, entry);
4084 }
4085 
4086 
GetFactoryCounts(lua_State * L)4087 int LuaSyncedRead::GetFactoryCounts(lua_State* L)
4088 {
4089 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
4090 	if (unit == NULL) {
4091 		return 0;
4092 	}
4093 
4094 	const CCommandAI* commandAI = unit->commandAI;
4095 	const CFactoryCAI* factoryCAI = dynamic_cast<const CFactoryCAI*>(commandAI);
4096 	if (!factoryCAI) {
4097 		return 0; // not a factory, bail
4098 	}
4099 	const CCommandQueue& commandQue = factoryCAI->commandQue;
4100 
4101 	// get the desired number of commands to return
4102 	int count = luaL_optint(L, 2, -1);
4103 	if (count < 0) {
4104 		count = (int)commandQue.size();
4105 	}
4106 
4107 	const bool noCmds = !luaL_optboolean(L, 3, false);
4108 
4109 	PackFactoryCounts(L, commandQue, count, noCmds);
4110 
4111 	return 1;
4112 }
4113 
4114 
GetCommandQueue(lua_State * L)4115 int LuaSyncedRead::GetCommandQueue(lua_State* L)
4116 {
4117 	return (GetUnitCommands(L));
4118 }
4119 
4120 
PackBuildQueue(lua_State * L,bool canBuild,const char * caller)4121 static int PackBuildQueue(lua_State* L, bool canBuild, const char* caller)
4122 {
4123 	CUnit* unit = ParseAllyUnit(L, caller, 1);
4124 	if (unit == NULL) {
4125 		return 0;
4126 	}
4127 	const CCommandAI* commandAI = unit->commandAI;
4128 	if (commandAI == NULL) {
4129 		return 0;
4130 	}
4131 	const CCommandQueue& commandQue = commandAI->commandQue;
4132 
4133 	lua_newtable(L);
4134 
4135 	int entry = 0;
4136 	int currentType = -1;
4137 	int currentCount = 0;
4138 	CCommandQueue::const_iterator it;
4139 	for (it = commandQue.begin(); it != commandQue.end(); ++it) {
4140 		if (it->GetID() < 0) { // a build command
4141 			const int unitDefID = -(it->GetID());
4142 
4143 			if (canBuild) {
4144 				// skip build orders that this unit can not start
4145 				const UnitDef* order_ud = unitDefHandler->GetUnitDefByID(unitDefID);
4146 				const UnitDef* builder_ud = unit->unitDef;
4147 				if ((order_ud == NULL) || (builder_ud == NULL)) {
4148 					continue; // something is wrong, bail
4149 				}
4150 				const string& orderName = StringToLower(order_ud->name);
4151 				const map<int, std::string>& bOpts = builder_ud->buildOptions;
4152 				map<int, std::string>::const_iterator it;
4153 				for (it = bOpts.begin(); it != bOpts.end(); ++it) {
4154 					if (it->second == orderName) {
4155 						break;
4156 					}
4157 				}
4158 				if (it == bOpts.end()) {
4159 					continue; // didn't find a matching entry
4160 				}
4161 			}
4162 
4163 			if (currentType == unitDefID) {
4164 				currentCount++;
4165 			} else if (currentType == -1) {
4166 				currentType = unitDefID;
4167 				currentCount = 1;
4168 			} else {
4169 				entry++;
4170 				lua_newtable(L);
4171 				lua_pushnumber(L, currentCount);
4172 				lua_rawseti(L, -2, currentType);
4173 				lua_rawseti(L, -2, entry);
4174 				currentType = unitDefID;
4175 				currentCount = 1;
4176 			}
4177 		}
4178 	}
4179 
4180 	if (currentCount > 0) {
4181 		entry++;
4182 		lua_newtable(L);
4183 		lua_pushnumber(L, currentCount);
4184 		lua_rawseti(L, -2, currentType);
4185 		lua_rawseti(L, -2, entry);
4186 	}
4187 
4188 	lua_pushnumber(L, entry);
4189 
4190 	return 2;
4191 }
4192 
4193 
GetFullBuildQueue(lua_State * L)4194 int LuaSyncedRead::GetFullBuildQueue(lua_State* L)
4195 {
4196 	return PackBuildQueue(L, false, __FUNCTION__);
4197 }
4198 
4199 
GetRealBuildQueue(lua_State * L)4200 int LuaSyncedRead::GetRealBuildQueue(lua_State* L)
4201 {
4202 	return PackBuildQueue(L, true, __FUNCTION__);
4203 }
4204 
4205 
4206 /******************************************************************************/
4207 
GetUnitRulesParams(lua_State * L)4208 int LuaSyncedRead::GetUnitRulesParams(lua_State* L)
4209 {
4210 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
4211 	if (unit == NULL) {
4212 		return 0;
4213 	}
4214 
4215 	int losMask = LuaRulesParams::RULESPARAMLOS_PUBLIC_MASK;
4216 
4217 	if (IsAllyUnit(L, unit) || game->IsGameOver()) {
4218 		losMask |= LuaRulesParams::RULESPARAMLOS_PRIVATE_MASK;
4219 	}
4220 	else if (teamHandler->AlliedTeams(unit->team, CLuaHandle::GetHandleReadTeam(L)) || ((CLuaHandle::GetHandleReadAllyTeam(L) < 0) && CLuaHandle::GetHandleFullRead(L))) {
4221 		losMask |= LuaRulesParams::RULESPARAMLOS_ALLIED_MASK;
4222 	}
4223 	else if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
4224 		//! NoAccessTeam
4225 	}
4226 	else if (unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)] & LOS_INLOS) {
4227 		losMask |= LuaRulesParams::RULESPARAMLOS_INLOS_MASK;
4228 	}
4229 	else if (unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)] & LOS_INRADAR) {
4230 		losMask |= LuaRulesParams::RULESPARAMLOS_INRADAR_MASK;
4231 	}
4232 
4233 	const LuaRulesParams::Params&  params    = unit->modParams;
4234 	const LuaRulesParams::HashMap& paramsMap = unit->modParamsMap;
4235 
4236 	return PushRulesParams(L, __FUNCTION__, params, paramsMap, losMask);
4237 }
4238 
4239 
GetUnitRulesParam(lua_State * L)4240 int LuaSyncedRead::GetUnitRulesParam(lua_State* L)
4241 {
4242 	CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
4243 	if (unit == NULL) {
4244 		return 0;
4245 	}
4246 
4247 	int losMask = LuaRulesParams::RULESPARAMLOS_PUBLIC_MASK;
4248 
4249 	if (IsAllyUnit(L, unit) || game->IsGameOver()) {
4250 		losMask |= LuaRulesParams::RULESPARAMLOS_PRIVATE_MASK;
4251 	}
4252 	else if (teamHandler->AlliedTeams(unit->team, CLuaHandle::GetHandleReadTeam(L)) || ((CLuaHandle::GetHandleReadAllyTeam(L) < 0) && CLuaHandle::GetHandleFullRead(L))) {
4253 		losMask |= LuaRulesParams::RULESPARAMLOS_ALLIED_MASK;
4254 	}
4255 	else if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
4256 		//! NoAccessTeam
4257 	}
4258 	else if (unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)] & LOS_INLOS) {
4259 		losMask |= LuaRulesParams::RULESPARAMLOS_INLOS_MASK;
4260 	}
4261 	else if (unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)] & LOS_INRADAR) {
4262 		losMask |= LuaRulesParams::RULESPARAMLOS_INRADAR_MASK;
4263 	}
4264 
4265 	const LuaRulesParams::Params&  params    = unit->modParams;
4266 	const LuaRulesParams::HashMap& paramsMap = unit->modParamsMap;
4267 
4268 	return GetRulesParam(L, __FUNCTION__, 2, params, paramsMap, losMask);
4269 }
4270 
4271 
4272 /******************************************************************************/
4273 
GetUnitCmdDescs(lua_State * L)4274 int LuaSyncedRead::GetUnitCmdDescs(lua_State* L)
4275 {
4276 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
4277 	if (unit == NULL) {
4278 		return 0;
4279 	}
4280 	vector<CommandDescription>& cmdDescs = unit->commandAI->possibleCommands;
4281 	const int lastDesc = (int)cmdDescs.size() - 1;
4282 
4283 	const int args = lua_gettop(L); // number of arguments
4284 	int startIndex = 0;
4285 	int endIndex = lastDesc;
4286 	if ((args >= 2) && lua_isnumber(L, 2)) {
4287 		startIndex = lua_toint(L, 2) - 1;
4288 		startIndex = max(0, min(lastDesc, startIndex));
4289 		if ((args >= 3) && lua_isnumber(L, 3)) {
4290 			endIndex = lua_toint(L, 3) - 1;
4291 			endIndex = max(0, min(lastDesc, endIndex));
4292 		} else {
4293 			endIndex = startIndex;
4294 		}
4295 	}
4296 	lua_createtable(L, endIndex - startIndex, 0);
4297 	int count = 1;
4298 	for (int i = startIndex; i <= endIndex; i++) {
4299 		LuaUtils::PushCommandDesc(L, cmdDescs[i]);
4300 		lua_rawseti(L, -2, count++);
4301 	}
4302 
4303 	return 1;
4304 }
4305 
4306 
FindUnitCmdDesc(lua_State * L)4307 int LuaSyncedRead::FindUnitCmdDesc(lua_State* L)
4308 {
4309 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
4310 	if (unit == NULL) {
4311 		return 0;
4312 	}
4313 	const int cmdID = luaL_checkint(L, 2);
4314 
4315 	vector<CommandDescription>& cmdDescs = unit->commandAI->possibleCommands;
4316 	for (int i = 0; i < (int)cmdDescs.size(); i++) {
4317 		if (cmdDescs[i].id == cmdID) {
4318 			lua_pushnumber(L, i + 1);
4319 			return 1;
4320 		}
4321 	}
4322 	return 0;
4323 }
4324 
4325 
4326 /******************************************************************************/
4327 /******************************************************************************/
4328 
ValidFeatureID(lua_State * L)4329 int LuaSyncedRead::ValidFeatureID(lua_State* L)
4330 {
4331 	CFeature* feature = ParseFeature(L, NULL, 1); // note the NULL
4332 	lua_pushboolean(L, feature != NULL);
4333 	return 1;
4334 }
4335 
4336 
GetAllFeatures(lua_State * L)4337 int LuaSyncedRead::GetAllFeatures(lua_State* L)
4338 {
4339 	int count = 0;
4340 	const CFeatureSet& activeFeatures = featureHandler->GetActiveFeatures();
4341 	CFeatureSet::const_iterator fit;
4342 	if (CLuaHandle::GetHandleFullRead(L)) {
4343 		lua_createtable(L, activeFeatures.size(), 0);
4344 		for (fit = activeFeatures.begin(); fit != activeFeatures.end(); ++fit) {
4345 			lua_pushnumber(L, (*fit)->id);
4346 			lua_rawseti(L, -2, ++count);
4347 		}
4348 	}
4349 	else {
4350 		lua_newtable(L);
4351 		for (fit = activeFeatures.begin(); fit != activeFeatures.end(); ++fit) {
4352 			if (IsFeatureVisible(L, *fit)) {
4353 				lua_pushnumber(L, (*fit)->id);
4354 				lua_rawseti(L, -2, ++count);
4355 			}
4356 		}
4357 	}
4358 	return 1;
4359 }
4360 
4361 
GetFeatureDefID(lua_State * L)4362 int LuaSyncedRead::GetFeatureDefID(lua_State* L)
4363 {
4364 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4365 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4366 		return 0;
4367 	}
4368 	lua_pushnumber(L, feature->def->id);
4369 	return 1;
4370 }
4371 
4372 
GetFeatureTeam(lua_State * L)4373 int LuaSyncedRead::GetFeatureTeam(lua_State* L)
4374 {
4375 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4376 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4377 		return 0;
4378 	}
4379 	if (feature->allyteam < 0) {
4380 		lua_pushnumber(L, -1);
4381 	} else {
4382 		lua_pushnumber(L, feature->team);
4383 	}
4384 	return 1;
4385 }
4386 
4387 
GetFeatureAllyTeam(lua_State * L)4388 int LuaSyncedRead::GetFeatureAllyTeam(lua_State* L)
4389 {
4390 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4391 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4392 		return 0;
4393 	}
4394 	lua_pushnumber(L, feature->allyteam);
4395 	return 1;
4396 }
4397 
4398 
GetFeatureHealth(lua_State * L)4399 int LuaSyncedRead::GetFeatureHealth(lua_State* L)
4400 {
4401 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4402 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4403 		return 0;
4404 	}
4405 	lua_pushnumber(L, feature->health);
4406 	lua_pushnumber(L, feature->def->health);
4407 	lua_pushnumber(L, feature->resurrectProgress);
4408 	return 3;
4409 }
4410 
4411 
GetFeatureHeight(lua_State * L)4412 int LuaSyncedRead::GetFeatureHeight(lua_State* L)
4413 {
4414 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4415 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4416 		return 0;
4417 	}
4418 	lua_pushnumber(L, feature->height);
4419 	return 1;
4420 }
4421 
4422 
GetFeatureRadius(lua_State * L)4423 int LuaSyncedRead::GetFeatureRadius(lua_State* L)
4424 {
4425 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4426 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4427 		return 0;
4428 	}
4429 	lua_pushnumber(L, feature->radius);
4430 	return 1;
4431 }
4432 
4433 
GetFeaturePosition(lua_State * L)4434 int LuaSyncedRead::GetFeaturePosition(lua_State* L)
4435 {
4436 	return (GetSolidObjectPosition(L, ParseFeature(L, __FUNCTION__, 1), true));
4437 }
4438 
GetFeatureDirection(lua_State * L)4439 int LuaSyncedRead::GetFeatureDirection(lua_State* L)
4440 {
4441 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4442 
4443 	if (feature == NULL || !IsFeatureVisible(L, feature))
4444 		return 0;
4445 
4446 	const CMatrix44f& mat = feature->GetTransformMatrixRef();
4447 	const float3& dir = mat.GetZ();
4448 
4449 	lua_pushnumber(L, dir.x);
4450 	lua_pushnumber(L, dir.y);
4451 	lua_pushnumber(L, dir.z);
4452 	return 3;
4453 }
4454 
GetFeatureVelocity(lua_State * L)4455 int LuaSyncedRead::GetFeatureVelocity(lua_State* L)
4456 {
4457 	return (GetWorldObjectVelocity(L, ParseFeature(L, __FUNCTION__, 1), true));
4458 }
4459 
4460 
GetFeatureHeading(lua_State * L)4461 int LuaSyncedRead::GetFeatureHeading(lua_State* L)
4462 {
4463 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4464 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4465 		return 0;
4466 	}
4467 	lua_pushnumber(L, feature->heading);
4468 	return 1;
4469 }
4470 
4471 
GetFeatureResources(lua_State * L)4472 int LuaSyncedRead::GetFeatureResources(lua_State* L)
4473 {
4474 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4475 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4476 		return 0;
4477 	}
4478 	lua_pushnumber(L,  feature->RemainingMetal());
4479 	lua_pushnumber(L,  feature->def->metal);
4480 	lua_pushnumber(L,  feature->RemainingEnergy());
4481 	lua_pushnumber(L,  feature->def->energy);
4482 	lua_pushnumber(L,  feature->reclaimLeft);
4483 	return 5;
4484 }
4485 
4486 
GetFeatureBlocking(lua_State * L)4487 int LuaSyncedRead::GetFeatureBlocking(lua_State* L)
4488 {
4489 	return (GetSolidObjectBlocking(L, ParseFeature(L, __FUNCTION__, 1)));
4490 }
4491 
4492 
GetFeatureNoSelect(lua_State * L)4493 int LuaSyncedRead::GetFeatureNoSelect(lua_State* L)
4494 {
4495 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4496 	if (feature == NULL || !IsFeatureVisible(L, feature)) {
4497 		return 0;
4498 	}
4499 	lua_pushboolean(L, feature->noSelect);
4500 	return 1;
4501 }
4502 
4503 
GetFeatureResurrect(lua_State * L)4504 int LuaSyncedRead::GetFeatureResurrect(lua_State* L)
4505 {
4506 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4507 	if (feature == NULL) {
4508 		return 0;
4509 	}
4510 
4511 	if (feature->udef == NULL) {
4512 		lua_pushliteral(L, "");
4513 	} else {
4514 		lua_pushsstring(L, feature->udef->name);
4515 	}
4516 
4517 	lua_pushnumber(L, feature->buildFacing);
4518 	return 2;
4519 }
4520 
4521 
GetFeatureCollisionVolumeData(lua_State * L)4522 int LuaSyncedRead::GetFeatureCollisionVolumeData(lua_State* L)
4523 {
4524 	CFeature* feature = ParseFeature(L, __FUNCTION__, 1);
4525 	if (feature == NULL) {
4526 		return 0;
4527 	}
4528 
4529 	return (PushCollisionVolumeData(L, feature->collisionVolume));
4530 }
4531 
4532 
4533 /******************************************************************************/
4534 /******************************************************************************/
4535 
GetProjectilePosition(lua_State * L)4536 int LuaSyncedRead::GetProjectilePosition(lua_State* L)
4537 {
4538 	const CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
4539 
4540 	if (pro == NULL)
4541 		return 0;
4542 
4543 	lua_pushnumber(L, pro->pos.x);
4544 	lua_pushnumber(L, pro->pos.y);
4545 	lua_pushnumber(L, pro->pos.z);
4546 	return 3;
4547 }
4548 
GetProjectileDirection(lua_State * L)4549 int LuaSyncedRead::GetProjectileDirection(lua_State* L)
4550 {
4551 	const CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
4552 
4553 	if (pro == NULL)
4554 		return 0;
4555 
4556 	lua_pushnumber(L, pro->dir.x);
4557 	lua_pushnumber(L, pro->dir.y);
4558 	lua_pushnumber(L, pro->dir.z);
4559 	return 3;
4560 }
4561 
GetProjectileVelocity(lua_State * L)4562 int LuaSyncedRead::GetProjectileVelocity(lua_State* L)
4563 {
4564 	return (GetWorldObjectVelocity(L, ParseProjectile(L, __FUNCTION__, 1), false));
4565 }
4566 
4567 
GetProjectileGravity(lua_State * L)4568 int LuaSyncedRead::GetProjectileGravity(lua_State* L)
4569 {
4570 	const CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
4571 
4572 	if (pro == NULL)
4573 		return 0;
4574 
4575 	lua_pushnumber(L, pro->mygravity);
4576 	return 1;
4577 }
4578 
GetProjectileSpinAngle(lua_State * L)4579 int LuaSyncedRead::GetProjectileSpinAngle(lua_State* L) { lua_pushnumber(L, 0.0f); return 1; } // DEPRECATED
GetProjectileSpinSpeed(lua_State * L)4580 int LuaSyncedRead::GetProjectileSpinSpeed(lua_State* L) { lua_pushnumber(L, 0.0f); return 1; } // DEPRECATED
GetProjectileSpinVec(lua_State * L)4581 int LuaSyncedRead::GetProjectileSpinVec(lua_State* L) {
4582 	lua_pushnumber(L, 0.0f);
4583 	lua_pushnumber(L, 0.0f);
4584 	lua_pushnumber(L, 0.0f);
4585 	return 3;
4586 } // DEPRECATED
4587 
GetPieceProjectileParams(lua_State * L)4588 int LuaSyncedRead::GetPieceProjectileParams(lua_State* L)
4589 {
4590 	const CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
4591 
4592 	if (pro == NULL || !pro->piece)
4593 		return 0;
4594 
4595 	const CPieceProjectile* ppro = static_cast<const CPieceProjectile*>(pro);
4596 
4597 	lua_pushnumber(L, ppro->explFlags);
4598 	lua_pushnumber(L, ppro->spinAngle);
4599 	lua_pushnumber(L, ppro->spinSpeed);
4600 	lua_pushnumber(L, ppro->spinVec.x);
4601 	lua_pushnumber(L, ppro->spinVec.y);
4602 	lua_pushnumber(L, ppro->spinVec.z);
4603 	return (1 + 1 + 1 + 3);
4604 }
4605 
4606 
GetProjectileTarget(lua_State * L)4607 int LuaSyncedRead::GetProjectileTarget(lua_State* L)
4608 {
4609 	const CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
4610 
4611 	if (pro == NULL || !pro->weapon)
4612 		return 0;
4613 
4614 	const CWeaponProjectile* wpro = static_cast<const CWeaponProjectile*>(pro);
4615 	const CWorldObject* wtgt = wpro->GetTargetObject();
4616 
4617 	if (wtgt == NULL) {
4618 		lua_pushnumber(L, int('g')); // ground
4619 		lua_createtable(L, 3, 0);
4620 		lua_pushnumber(L, (wpro->GetTargetPos()).x); lua_rawseti(L, -2, 1);
4621 		lua_pushnumber(L, (wpro->GetTargetPos()).y); lua_rawseti(L, -2, 2);
4622 		lua_pushnumber(L, (wpro->GetTargetPos()).z); lua_rawseti(L, -2, 3);
4623 		return 2;
4624 	}
4625 
4626 	if (dynamic_cast<const CUnit*>(wtgt) != NULL) {
4627 		lua_pushnumber(L, int('u'));
4628 		lua_pushnumber(L, wtgt->id);
4629 		return 2;
4630 	}
4631 	if (dynamic_cast<const CFeature*>(wtgt) != NULL) {
4632 		lua_pushnumber(L, int('f'));
4633 		lua_pushnumber(L, wtgt->id);
4634 		return 2;
4635 	}
4636 	if (dynamic_cast<const CWeaponProjectile*>(wtgt) != NULL) {
4637 		lua_pushnumber(L, int('p'));
4638 		lua_pushnumber(L, wtgt->id);
4639 		return 2;
4640 	}
4641 
4642 	// projectile target cannot be anything else
4643 	assert(false);
4644 	return 0;
4645 }
4646 
GetProjectileType(lua_State * L)4647 int LuaSyncedRead::GetProjectileType(lua_State* L)
4648 {
4649 	const CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
4650 
4651 	if (pro == NULL)
4652 		return 0;
4653 
4654 	lua_pushboolean(L, pro->weapon);
4655 	lua_pushboolean(L, pro->piece);
4656 	return 2;
4657 }
4658 
GetProjectileDefID(lua_State * L)4659 int LuaSyncedRead::GetProjectileDefID(lua_State* L)
4660 {
4661 	const CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
4662 
4663 	if (pro == NULL)
4664 		return 0;
4665 	if (!pro->weapon)
4666 		return 0;
4667 
4668 	const CWeaponProjectile* wpro = static_cast<const CWeaponProjectile*>(pro);
4669 	const WeaponDef* wdef = wpro->GetWeaponDef();
4670 
4671 	if (wdef == NULL)
4672 		return 0;
4673 
4674 	lua_pushnumber(L, wdef->id);
4675 	return 1;
4676 }
4677 
GetProjectileName(lua_State * L)4678 int LuaSyncedRead::GetProjectileName(lua_State* L)
4679 {
4680 	const CProjectile* pro = ParseProjectile(L, __FUNCTION__, 1);
4681 
4682 	if (pro == NULL)
4683 		return 0;
4684 
4685 	if (pro->weapon) {
4686 		const CWeaponProjectile* wpro = static_cast<const CWeaponProjectile*>(pro);
4687 
4688 		if (wpro != NULL && wpro->GetWeaponDef() != NULL) {
4689 			// maybe CWeaponProjectile derivatives
4690 			// should have actual names themselves?
4691 			lua_pushsstring(L, wpro->GetWeaponDef()->name);
4692 			return 1;
4693 		}
4694 	}
4695 	if (pro->piece) {
4696 		const CPieceProjectile* ppro = static_cast<const CPieceProjectile*>(pro);
4697 
4698 		if (ppro != NULL && ppro->omp != NULL) {
4699 			lua_pushsstring(L, ppro->omp->name);
4700 			return 1;
4701 		}
4702 	}
4703 
4704 	// neither weapon nor piece likely means the projectile is CExpGenSpawner, should we return any name in this case?
4705 	return 0;
4706 }
4707 
4708 
4709 /******************************************************************************/
4710 /******************************************************************************/
4711 
GetGroundHeight(lua_State * L)4712 int LuaSyncedRead::GetGroundHeight(lua_State* L)
4713 {
4714 	const float x = luaL_checkfloat(L, 1);
4715 	const float z = luaL_checkfloat(L, 2);
4716 	lua_pushnumber(L, CGround::GetHeightReal(x, z, CLuaHandle::GetHandleSynced(L)));
4717 	return 1;
4718 }
4719 
4720 
GetGroundOrigHeight(lua_State * L)4721 int LuaSyncedRead::GetGroundOrigHeight(lua_State* L)
4722 {
4723 	const float x = luaL_checkfloat(L, 1);
4724 	const float z = luaL_checkfloat(L, 2);
4725 	lua_pushnumber(L, CGround::GetOrigHeight(x, z));
4726 	return 1;
4727 }
4728 
4729 
GetGroundNormal(lua_State * L)4730 int LuaSyncedRead::GetGroundNormal(lua_State* L)
4731 {
4732 	const float x = luaL_checkfloat(L, 1);
4733 	const float z = luaL_checkfloat(L, 2);
4734 	const float3 normal = CGround::GetSmoothNormal(x, z, CLuaHandle::GetHandleSynced(L));
4735 	lua_pushnumber(L, normal.x);
4736 	lua_pushnumber(L, normal.y);
4737 	lua_pushnumber(L, normal.z);
4738 	return 3;
4739 }
4740 
4741 
GetGroundInfo(lua_State * L)4742 int LuaSyncedRead::GetGroundInfo(lua_State* L)
4743 {
4744 	const float x = luaL_checkfloat(L, 1);
4745 	const float z = luaL_checkfloat(L, 2);
4746 
4747 	const int ix = Clamp(x, 0.0f, float3::maxxpos) / (SQUARE_SIZE * 2);
4748 	const int iz = Clamp(z, 0.0f, float3::maxzpos) / (SQUARE_SIZE * 2);
4749 
4750 	const int maxIndex = (gs->hmapx * gs->hmapy) - 1;
4751 	const int sqrIndex = std::min(maxIndex, (gs->hmapx * iz) + ix);
4752 	const int typeIndex = readMap->GetTypeMapSynced()[sqrIndex];
4753 
4754 	// arguments to LuaMetalMap::GetMetalAmount in half-heightmap coors
4755 	lua_pop(L, 2);
4756 	lua_pushnumber(L, ix);
4757 	lua_pushnumber(L, iz);
4758 
4759 	return (PushTerrainTypeData(L, &mapInfo->terrainTypes[typeIndex], true));
4760 }
4761 
4762 
4763 // similar to ParseMapParams in LuaSyncedCtrl
ParseMapCoords(lua_State * L,const char * caller,int & tx1,int & tz1,int & tx2,int & tz2)4764 static void ParseMapCoords(lua_State* L, const char* caller,
4765                            int& tx1, int& tz1, int& tx2, int& tz2)
4766 {
4767 	float fx1 = 0, fz1 = 0, fx2 = 0, fz2 = 0;
4768 
4769 	const int args = lua_gettop(L); // number of arguments
4770 	if (args == 2) {
4771 		fx1 = fx2 = luaL_checkfloat(L, 1);
4772 		fz1 = fz2 = luaL_checkfloat(L, 2);
4773 	}
4774 	else if (args == 4) {
4775 		fx1 = luaL_checkfloat(L, 1);
4776 		fz1 = luaL_checkfloat(L, 2);
4777 		fx2 = luaL_checkfloat(L, 3);
4778 		fz2 = luaL_checkfloat(L, 4);
4779 	}
4780 	else {
4781 		luaL_error(L, "Incorrect arguments to %s()", caller);
4782 	}
4783 
4784 	// quantize and clamp
4785 	tx1 = Clamp((int)(fx1 / SQUARE_SIZE), 0, gs->mapxm1);
4786 	tx2 = Clamp((int)(fx2 / SQUARE_SIZE), 0, gs->mapxm1);
4787 	tz1 = Clamp((int)(fz1 / SQUARE_SIZE), 0, gs->mapym1);
4788 	tz2 = Clamp((int)(fz2 / SQUARE_SIZE), 0, gs->mapym1);
4789 
4790 	return;
4791 }
4792 
4793 
GetGroundBlocked(lua_State * L)4794 int LuaSyncedRead::GetGroundBlocked(lua_State* L)
4795 {
4796 	if ((CLuaHandle::GetHandleReadAllyTeam(L) < 0) && !CLuaHandle::GetHandleFullRead(L)) {
4797 		return 0;
4798 	}
4799 
4800 	int tx1, tx2, tz1, tz2;
4801 	ParseMapCoords(L, __FUNCTION__, tx1, tz1, tx2, tz2);
4802 
4803 	for(int z = tz1; z <= tz2; z++){
4804 		for(int x = tx1; x <= tx2; x++){
4805 			const CSolidObject* s = groundBlockingObjectMap->GroundBlocked(x, z);
4806 
4807 			const CFeature* feature = dynamic_cast<const CFeature*>(s);
4808 			if (feature) {
4809 				if (IsFeatureVisible(L, feature)) {
4810 					HSTR_PUSH(L, "feature");
4811 					lua_pushnumber(L, feature->id);
4812 					return 2;
4813 				} else {
4814 					continue;
4815 				}
4816 			}
4817 
4818 			const CUnit* unit = dynamic_cast<const CUnit*>(s);
4819 			if (unit) {
4820 				if (CLuaHandle::GetHandleFullRead(L) || (unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)] & LOS_INLOS)) {
4821 					HSTR_PUSH(L, "unit");
4822 					lua_pushnumber(L, unit->id);
4823 					return 2;
4824 				} else {
4825 					continue;
4826 				}
4827 			}
4828 		}
4829 	}
4830 
4831 	lua_pushboolean(L, false);
4832 	return 1;
4833 }
4834 
4835 
GetGroundExtremes(lua_State * L)4836 int LuaSyncedRead::GetGroundExtremes(lua_State* L)
4837 {
4838 	lua_pushnumber(L, readMap->GetInitMinHeight());
4839 	lua_pushnumber(L, readMap->GetInitMaxHeight());
4840 	return 2;
4841 }
4842 
4843 
GetTerrainTypeData(lua_State * L)4844 int LuaSyncedRead::GetTerrainTypeData(lua_State* L)
4845 {
4846 	const int tti = luaL_checkint(L, 1);
4847 
4848 	if (tti < 0 || tti >= CMapInfo::NUM_TERRAIN_TYPES) {
4849 		return 0;
4850 	}
4851 
4852 	return (PushTerrainTypeData(L, &mapInfo->terrainTypes[tti], false));
4853 }
4854 
4855 /******************************************************************************/
4856 
GetSmoothMeshHeight(lua_State * L)4857 int LuaSyncedRead::GetSmoothMeshHeight(lua_State* L)
4858 {
4859 	const float x = luaL_checkfloat(L, 1);
4860 	const float z = luaL_checkfloat(L, 2);
4861 
4862 	lua_pushnumber(L, smoothGround->GetHeight(x, z));
4863 	return 1;
4864 }
4865 
4866 /******************************************************************************/
4867 /******************************************************************************/
4868 
4869 
TestMoveOrder(lua_State * L)4870 int LuaSyncedRead::TestMoveOrder(lua_State* L)
4871 {
4872 	const int unitDefID = luaL_checkint(L, 1);
4873 	const UnitDef* unitDef = unitDefHandler->GetUnitDefByID(unitDefID);
4874 
4875 	if (unitDef == NULL || unitDef->pathType == -1U) {
4876 		lua_pushboolean(L, false);
4877 		return 1;
4878 	}
4879 
4880 	const MoveDef* moveDef = moveDefHandler->GetMoveDefByPathType(unitDef->pathType);
4881 
4882 	if (moveDef == NULL) {
4883 		lua_pushboolean(L, !unitDef->IsImmobileUnit());
4884 		return 1;
4885 	}
4886 
4887 	const float3 pos(luaL_checkfloat(L, 2), luaL_checkfloat(L, 3), luaL_checkfloat(L, 4));
4888 	const float3 dir(luaL_optfloat(L, 5, 0.0f), luaL_optfloat(L, 6, 0.0f), luaL_optfloat(L, 7, 0.0f));
4889 
4890 	const bool testTerrain = luaL_optboolean(L, 8, true);
4891 	const bool testObjects = luaL_optboolean(L, 9, true);
4892 	const bool centerOnly = luaL_optboolean(L, 10, false);
4893 
4894 	bool los = false;
4895 	bool ret = false;
4896 
4897 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
4898 		los = CLuaHandle::GetHandleFullRead(L);
4899 	} else {
4900 		los = losHandler->InLos(pos, CLuaHandle::GetHandleReadAllyTeam(L));
4901 	}
4902 
4903 	if (los) {
4904 		ret = moveDef->TestMoveSquare(NULL, pos, dir, testTerrain, testObjects, centerOnly);
4905 	}
4906 
4907 	lua_pushboolean(L, ret);
4908 	return 1;
4909 }
4910 
TestBuildOrder(lua_State * L)4911 int LuaSyncedRead::TestBuildOrder(lua_State* L)
4912 {
4913 	const int unitDefID = luaL_checkint(L, 1);
4914 	const UnitDef* unitDef = unitDefHandler->GetUnitDefByID(unitDefID);
4915 
4916 	if (unitDef == NULL) {
4917 		lua_pushnumber(L, 0);
4918 		return 1;
4919 	}
4920 
4921 	const float3 pos(luaL_checkfloat(L, 2),
4922 	                 luaL_checkfloat(L, 3),
4923 	                 luaL_checkfloat(L, 4));
4924 	const int facing = LuaUtils::ParseFacing(L, __FUNCTION__, 5);
4925 
4926 	BuildInfo bi;
4927 	bi.buildFacing = facing;
4928 	bi.def = unitDef;
4929 	bi.pos = pos;
4930 	bi.pos = CGameHelper::Pos2BuildPos(bi, CLuaHandle::GetHandleSynced(L));
4931 	CFeature* feature;
4932 
4933 	// negative allyTeam values have full visibility in TestUnitBuildSquare()
4934 	// 0 = BUILDSQUARE_BLOCKED
4935 	// 1 = BUILDSQUARE_OCCUPIED
4936 	// 2 = BUILDSQUARE_RECLAIMABLE
4937 	// 3 = BUILDSQUARE_OPEN
4938 	int retval = CGameHelper::TestUnitBuildSquare(bi, feature, CLuaHandle::GetHandleReadAllyTeam(L), CLuaHandle::GetHandleSynced(L));
4939 
4940 	// output of TestUnitBuildSquare was changed after this lua function was writen
4941 	// keep backward-compability by mapping:
4942 	// BUILDSQUARE_OPEN = BUILDSQUARE_RECLAIMABLE = 2
4943 	if (retval == CGameHelper::BUILDSQUARE_OPEN)
4944 		retval = 2;
4945 
4946 	if (feature == NULL) {
4947 		lua_pushnumber(L, retval);
4948 		return 1;
4949 	}
4950 	lua_pushnumber(L, retval);
4951 	lua_pushnumber(L, feature->id);
4952 	return 2;
4953 }
4954 
4955 
Pos2BuildPos(lua_State * L)4956 int LuaSyncedRead::Pos2BuildPos(lua_State* L)
4957 {
4958 	const int unitDefID = luaL_checkint(L, 1);
4959 	const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
4960 	if (ud == NULL) {
4961 		return 0;
4962 	}
4963 
4964 	const float3 pos(luaL_checkfloat(L, 2),
4965 	                 luaL_checkfloat(L, 3),
4966 	                 luaL_checkfloat(L, 4));
4967 	const int facing = luaL_optint(L, 5, FACING_SOUTH);
4968 
4969 	const BuildInfo buildInfo(ud, pos, facing);
4970 	const float3 buildPos = CGameHelper::Pos2BuildPos(buildInfo, CLuaHandle::GetHandleSynced(L));
4971 
4972 	lua_pushnumber(L, buildPos.x);
4973 	lua_pushnumber(L, buildPos.y);
4974 	lua_pushnumber(L, buildPos.z);
4975 
4976 	return 3;
4977 }
4978 
4979 
4980 /******************************************************************************/
4981 /******************************************************************************/
4982 
GetEffectiveLosAllyTeam(lua_State * L,int arg)4983 static int GetEffectiveLosAllyTeam(lua_State* L, int arg)
4984 {
4985 	if (lua_isnoneornil(L, arg)) {
4986 		return CLuaHandle::GetHandleReadAllyTeam(L);
4987 	}
4988 	const bool isGaia = (CLuaHandle::GetHandleReadAllyTeam(L) == teamHandler->GaiaAllyTeamID());
4989 	if (CLuaHandle::GetHandleFullRead(L) || isGaia) {
4990 		const int at = luaL_checkint(L, arg);
4991 		if (at >= teamHandler->ActiveAllyTeams()) {
4992 			luaL_error(L, "Invalid allyTeam");
4993 		}
4994 		if (isGaia && (at >= 0) && (at != teamHandler->GaiaAllyTeamID())) {
4995 			luaL_error(L, "Invalid gaia access");
4996 		}
4997 		return at;
4998 	}
4999 	else if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
5000 		luaL_error(L, "Invalid access");
5001 	}
5002 	return CLuaHandle::GetHandleReadAllyTeam(L);
5003 }
5004 
5005 
GetPositionLosState(lua_State * L)5006 int LuaSyncedRead::GetPositionLosState(lua_State* L)
5007 {
5008 	const float3 pos(luaL_checkfloat(L, 1),
5009 	                 luaL_checkfloat(L, 2),
5010 	                 luaL_checkfloat(L, 3));
5011 
5012 	const int allyTeamID = GetEffectiveLosAllyTeam(L, 4);
5013 
5014 	bool inLos    = false;
5015 	bool inRadar  = false;
5016 	bool inJammer = false;
5017 
5018 	if (allyTeamID >= 0) {
5019 		inLos    = losHandler->InLos(pos, allyTeamID);
5020 		inRadar  = radarHandler->InRadar(pos, allyTeamID);
5021 		inJammer = radarHandler->InJammer(pos, allyTeamID);
5022 	} else {
5023 		// this does not seem useful
5024 		for (int at = 0; at < teamHandler->ActiveAllyTeams(); at++) {
5025 			if (losHandler->InLos(pos, at)) {
5026 				inLos = true;
5027 			}
5028 			if (radarHandler->InRadar(pos, at)) {
5029 				inRadar = true;
5030 			}
5031 			if (radarHandler->InJammer(pos, at)) {
5032 				inJammer = true;
5033 			}
5034 		}
5035 	}
5036 
5037 	lua_pushboolean(L, inLos || inRadar);
5038 	lua_pushboolean(L, inLos);
5039 	lua_pushboolean(L, inRadar);
5040 	lua_pushboolean(L, inJammer);
5041 	return 4;
5042 }
5043 
5044 
IsPosInLos(lua_State * L)5045 int LuaSyncedRead::IsPosInLos(lua_State* L)
5046 {
5047 	const float3 pos(luaL_checkfloat(L, 1),
5048 	                 luaL_checkfloat(L, 2),
5049 	                 luaL_checkfloat(L, 3));
5050 
5051 	const int allyTeamID = GetEffectiveLosAllyTeam(L, 4);
5052 
5053 	bool state = false;
5054 	if (allyTeamID >= 0) {
5055 		state = losHandler->InLos(pos, allyTeamID);
5056 	}
5057 	else {
5058 		for (int at = 0; at < teamHandler->ActiveAllyTeams(); at++) {
5059 			if (losHandler->InLos(pos, at)) {
5060 				state = true;
5061 				break;
5062 			}
5063 		}
5064 	}
5065 	lua_pushboolean(L, state);
5066 
5067 	return 1;
5068 }
5069 
5070 
IsPosInRadar(lua_State * L)5071 int LuaSyncedRead::IsPosInRadar(lua_State* L)
5072 {
5073 	const float3 pos(luaL_checkfloat(L, 1),
5074 	                 luaL_checkfloat(L, 2),
5075 	                 luaL_checkfloat(L, 3));
5076 
5077 	const int allyTeamID = GetEffectiveLosAllyTeam(L, 4);
5078 
5079 	bool state = false;
5080 	if (allyTeamID >= 0) {
5081 		state = radarHandler->InRadar(pos, allyTeamID);
5082 	}
5083 	else {
5084 		for (int at = 0; at < teamHandler->ActiveAllyTeams(); at++) {
5085 			if (radarHandler->InRadar(pos, at)) {
5086 				state = true;
5087 				break;
5088 			}
5089 		}
5090 	}
5091 	lua_pushboolean(L, state);
5092 
5093 	return 1;
5094 }
5095 
5096 
IsPosInAirLos(lua_State * L)5097 int LuaSyncedRead::IsPosInAirLos(lua_State* L)
5098 {
5099 	const float3 pos(luaL_checkfloat(L, 1),
5100 	                 luaL_checkfloat(L, 2),
5101 	                 luaL_checkfloat(L, 3));
5102 
5103 	const int allyTeamID = GetEffectiveLosAllyTeam(L, 4);
5104 
5105 	bool state = false;
5106 	if (allyTeamID >= 0) {
5107 		state = losHandler->InAirLos(pos, allyTeamID);
5108 	}
5109 	else {
5110 		for (int at = 0; at < teamHandler->ActiveAllyTeams(); at++) {
5111 			if (losHandler->InAirLos(pos, at)) {
5112 				state = true;
5113 				break;
5114 			}
5115 		}
5116 	}
5117 	lua_pushboolean(L, state);
5118 
5119 	return 1;
5120 }
5121 
5122 
5123 /******************************************************************************/
5124 
GetClosestValidPosition(lua_State * L)5125 int LuaSyncedRead::GetClosestValidPosition(lua_State* L)
5126 {
5127 	// FIXME -- finish this
5128 	/*const int unitDefID = luaL_checkint(L, 1);
5129 	const float x     = luaL_checkfloat(L, 2);
5130 	const float z     = luaL_checkfloat(L, 3);
5131 	const float r     = luaL_checkfloat(L, 4);*/
5132 	//const int mx = (int)max(0 , min(gs->mapxm1, (int)(x / SQUARE_SIZE)));
5133 	//const int mz = (int)max(0 , min(gs->mapym1, (int)(z / SQUARE_SIZE)));
5134 	return 0;
5135 }
5136 
5137 
5138 /******************************************************************************/
5139 /******************************************************************************/
5140 
GetUnitPieceMap(lua_State * L)5141 int LuaSyncedRead::GetUnitPieceMap(lua_State* L)
5142 {
5143 	const CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5144 
5145 	if (unit == NULL)
5146 		return 0;
5147 
5148 	const LocalModel* localModel = unit->localModel;
5149 
5150 	lua_createtable(L, 0, localModel->pieces.size());
5151 
5152 	// {"piece" = 123, ...}
5153 	for (size_t i = 0; i < localModel->pieces.size(); i++) {
5154 		const LocalModelPiece& lp = *localModel->pieces[i];
5155 		lua_pushsstring(L, lp.original->name);
5156 		lua_pushnumber(L, i + 1);
5157 		lua_rawset(L, -3);
5158 	}
5159 	return 1;
5160 }
5161 
5162 
GetUnitPieceList(lua_State * L)5163 int LuaSyncedRead::GetUnitPieceList(lua_State* L)
5164 {
5165 	const CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5166 
5167 	if (unit == NULL)
5168 		return 0;
5169 
5170 	const LocalModel* localModel = unit->localModel;
5171 
5172 	lua_createtable(L, localModel->pieces.size(), 0);
5173 
5174 	// {[1] = "piece", ...}
5175 	for (size_t i = 0; i < localModel->pieces.size(); i++) {
5176 		const LocalModelPiece& lp = *localModel->pieces[i];
5177 		lua_pushsstring(L, lp.original->name);
5178 		lua_rawseti(L, -2, i + 1);
5179 	}
5180 	return 1;
5181 }
5182 
5183 
5184 template<class ModelType>
GetUnitPieceInfo(lua_State * L,const ModelType & op)5185 static int GetUnitPieceInfo(lua_State* L, const ModelType& op)
5186 {
5187 	lua_newtable(L);
5188 	HSTR_PUSH_STRING(L, "name", op.name.c_str());
5189 	HSTR_PUSH_STRING(L, "parent", op.parentName.c_str());
5190 
5191 	HSTR_PUSH(L, "children");
5192 	lua_newtable(L);
5193 	for (int c = 0; c < (int)op.children.size(); c++) {
5194 		lua_pushsstring(L, op.children[c]->name);
5195 		lua_rawseti(L, -2, c + 1);
5196 	}
5197 	lua_rawset(L, -3);
5198 
5199 	HSTR_PUSH(L, "isEmpty");
5200 	lua_pushboolean(L, !op.HasGeometryData());
5201 	lua_rawset(L, -3);
5202 
5203 	HSTR_PUSH(L, "min");
5204 	lua_newtable(L); {
5205 		lua_pushnumber(L, op.mins.x); lua_rawseti(L, -2, 1);
5206 		lua_pushnumber(L, op.mins.y); lua_rawseti(L, -2, 2);
5207 		lua_pushnumber(L, op.mins.z); lua_rawseti(L, -2, 3);
5208 	}
5209 	lua_rawset(L, -3);
5210 
5211 	HSTR_PUSH(L, "max");
5212 	lua_newtable(L); {
5213 		lua_pushnumber(L, op.maxs.x); lua_rawseti(L, -2, 1);
5214 		lua_pushnumber(L, op.maxs.y); lua_rawseti(L, -2, 2);
5215 		lua_pushnumber(L, op.maxs.z); lua_rawseti(L, -2, 3);
5216 	}
5217 	lua_rawset(L, -3);
5218 
5219 	HSTR_PUSH(L, "offset");
5220 	lua_newtable(L); {
5221 		lua_pushnumber(L, op.offset.x); lua_rawseti(L, -2, 1);
5222 		lua_pushnumber(L, op.offset.y); lua_rawseti(L, -2, 2);
5223 		lua_pushnumber(L, op.offset.z); lua_rawseti(L, -2, 3);
5224 	}
5225 	lua_rawset(L, -3);
5226 	return 1;
5227 }
5228 
5229 
GetUnitPieceInfo(lua_State * L)5230 int LuaSyncedRead::GetUnitPieceInfo(lua_State* L)
5231 {
5232 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5233 	if (unit == NULL) {
5234 		return 0;
5235 	}
5236 	const LocalModel* localModel = unit->localModel;
5237 
5238 	const int piece = luaL_checkint(L, 2) - 1;
5239 	if ((piece < 0) || ((size_t)piece >= localModel->pieces.size())) {
5240 		return 0;
5241 	}
5242 
5243 	const S3DModelPiece& op = *localModel->pieces[piece]->original;
5244 	return ::GetUnitPieceInfo(L, op);
5245 }
5246 
5247 
GetUnitPiecePosition(lua_State * L)5248 int LuaSyncedRead::GetUnitPiecePosition(lua_State* L)
5249 {
5250 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5251 	if (unit == NULL) {
5252 		return 0;
5253 	}
5254 	const LocalModel* localModel = unit->localModel;
5255 	if (localModel == NULL) {
5256 		return 0;
5257 	}
5258 	const int piece = luaL_checkint(L, 2) - 1;
5259 	if ((piece < 0) || ((size_t)piece >= localModel->pieces.size())) {
5260 		return 0;
5261 	}
5262 	const float3 pos = localModel->GetRawPiecePos(piece);
5263 	lua_pushnumber(L, pos.x);
5264 	lua_pushnumber(L, pos.y);
5265 	lua_pushnumber(L, pos.z);
5266 	return 3;
5267 }
5268 
5269 
GetUnitPiecePosDir(lua_State * L)5270 int LuaSyncedRead::GetUnitPiecePosDir(lua_State* L)
5271 {
5272 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5273 	if (unit == NULL) {
5274 		return 0;
5275 	}
5276 	const LocalModel* localModel = unit->localModel;
5277 	if (localModel == NULL) {
5278 		return 0;
5279 	}
5280 	const int piece = luaL_checkint(L, 2) - 1;
5281 	if ((piece < 0) || ((size_t)piece >= localModel->pieces.size())) {
5282 		return 0;
5283 	}
5284 
5285 	float3 dir;
5286 	float3 pos;
5287 	localModel->GetRawEmitDirPos(piece, pos, dir);
5288 
5289 	// transform
5290 	pos = unit->GetObjectSpacePos(pos);
5291 	dir = unit->GetObjectSpaceVec(dir);
5292 
5293 	lua_pushnumber(L, pos.x);
5294 	lua_pushnumber(L, pos.y);
5295 	lua_pushnumber(L, pos.z);
5296 	lua_pushnumber(L, dir.x);
5297 	lua_pushnumber(L, dir.y);
5298 	lua_pushnumber(L, dir.z);
5299 	return 6;
5300 }
5301 
5302 
GetUnitPieceDirection(lua_State * L)5303 int LuaSyncedRead::GetUnitPieceDirection(lua_State* L)
5304 {
5305 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5306 	if (unit == NULL) {
5307 		return 0;
5308 	}
5309 	const LocalModel* localModel = unit->localModel;
5310 	if (localModel == NULL) {
5311 		return 0;
5312 	}
5313 	const int piece = luaL_checkint(L, 2) - 1;
5314 	if ((piece < 0) || ((size_t)piece >= localModel->pieces.size())) {
5315 		return 0;
5316 	}
5317 	const float3 dir = localModel->GetRawPieceDirection(piece);
5318 	lua_pushnumber(L, dir.x);
5319 	lua_pushnumber(L, dir.y);
5320 	lua_pushnumber(L, dir.z);
5321 	return 3;
5322 }
5323 
5324 
GetUnitPieceMatrix(lua_State * L)5325 int LuaSyncedRead::GetUnitPieceMatrix(lua_State* L)
5326 {
5327 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5328 	if (unit == NULL) {
5329 		return 0;
5330 	}
5331 	const LocalModel* localModel = unit->localModel;
5332 	if (localModel == NULL) {
5333 		return 0;
5334 	}
5335 	const int piece = luaL_checkint(L, 2) - 1;
5336 	if ((piece < 0) || ((size_t)piece >= localModel->pieces.size())) {
5337 		return 0;
5338 	}
5339 	const CMatrix44f& mat = localModel->GetRawPieceMatrix(piece);
5340 	for (int m = 0; m < 16; m++) {
5341 		lua_pushnumber(L, mat.m[m]);
5342 	}
5343 	return 16;
5344 }
5345 
5346 
GetUnitScriptPiece(lua_State * L)5347 int LuaSyncedRead::GetUnitScriptPiece(lua_State* L)
5348 {
5349 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5350 	if (unit == NULL) {
5351 		return 0;
5352 	}
5353 	const CUnitScript* script = unit->script;
5354 
5355 	if (!lua_isnumber(L, 2)) {
5356 		// return the whole script->piece map
5357 		lua_newtable(L);
5358 		for (size_t sp = 0; sp < script->pieces.size(); sp++) {
5359 			const int piece = script->ScriptToModel(sp);
5360 			if (piece != -1) {
5361 				lua_pushnumber(L, piece + 1);
5362 				lua_rawseti(L, -2, sp);
5363 			}
5364 		}
5365 		return 1;
5366 	}
5367 
5368 	const int scriptPiece = lua_toint(L, 2);
5369 	const int piece = script->ScriptToModel(scriptPiece);
5370 	if (piece < 0) {
5371 		return 0;
5372 	}
5373 
5374 	lua_pushnumber(L, piece + 1);
5375 	return 1;
5376 }
5377 
5378 
GetUnitScriptNames(lua_State * L)5379 int LuaSyncedRead::GetUnitScriptNames(lua_State* L)
5380 {
5381 	CUnit* unit = ParseTypedUnit(L, __FUNCTION__, 1);
5382 	if (unit == NULL) {
5383 		return 0;
5384 	}
5385 	const vector<LocalModelPiece*>& pieces = unit->script->pieces;
5386 
5387 	lua_newtable(L);
5388 	for (size_t sp = 0; sp < pieces.size(); sp++) {
5389 		lua_pushsstring(L, pieces[sp]->original->name);
5390 		lua_pushnumber(L, sp);
5391 		lua_rawset(L, -3);
5392 	}
5393 
5394 	return 1;
5395 }
5396 
5397 
GetRadarErrorParams(lua_State * L)5398 int LuaSyncedRead::GetRadarErrorParams(lua_State* L)
5399 {
5400 	const int allyTeamID = lua_tonumber(L, 1);
5401 
5402 	if (!teamHandler->IsValidAllyTeam(allyTeamID))
5403 		return 0;
5404 
5405 	lua_pushnumber(L, radarHandler->GetAllyTeamRadarErrorSize(allyTeamID));
5406 	lua_pushnumber(L, radarHandler->GetBaseRadarErrorSize());
5407 	lua_pushnumber(L, radarHandler->GetBaseRadarErrorMult());
5408 	return 3;
5409 }
5410 
5411 
5412 /******************************************************************************/
5413 /******************************************************************************/
5414 
GetCOBUnitVar(lua_State * L)5415 int LuaSyncedRead::GetCOBUnitVar(lua_State* L)
5416 {
5417 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
5418 	if (unit == NULL) {
5419 		return 0;
5420 	}
5421 	const int varID = luaL_checkint(L, 2);
5422 	if ((varID < 0) || (varID >= CUnitScript::UNIT_VAR_COUNT)) {
5423 		return 0;
5424 	}
5425 	const int value = unit->script->GetUnitVars()[varID];
5426 	if (luaL_optboolean(L, 3, false)) {
5427 		lua_pushnumber(L, UNPACKX(value));
5428 		lua_pushnumber(L, UNPACKZ(value));
5429 		return 2;
5430 	}
5431 	lua_pushnumber(L, value);
5432 	return 1;
5433 }
5434 
5435 
GetCOBTeamVar(lua_State * L)5436 int LuaSyncedRead::GetCOBTeamVar(lua_State* L)
5437 {
5438 	const int teamID = luaL_checkint(L, 1);
5439 	if (!teamHandler->IsValidTeam(teamID)) {
5440 		return 0;
5441 	}
5442 	if (!IsAlliedTeam(L, teamID)) {
5443 		return 0;
5444 	}
5445 	const int varID = luaL_checkint(L, 2);
5446 	if ((varID < 0) || (varID >= CUnitScript::TEAM_VAR_COUNT)) {
5447 		return 0;
5448 	}
5449 	const int value = CUnitScript::GetTeamVars(teamID)[varID];
5450 	if (luaL_optboolean(L, 3, false)) {
5451 		lua_pushnumber(L, UNPACKX(value));
5452 		lua_pushnumber(L, UNPACKZ(value));
5453 		return 2;
5454 	}
5455 	lua_pushnumber(L, value);
5456 	return 1;
5457 
5458 }
5459 
5460 
GetCOBAllyTeamVar(lua_State * L)5461 int LuaSyncedRead::GetCOBAllyTeamVar(lua_State* L)
5462 {
5463 	const int allyTeamID = luaL_checkint(L, 1);
5464 	if (!teamHandler->IsValidAllyTeam(allyTeamID)) {
5465 		return 0;
5466 	}
5467 	if (!IsAlliedAllyTeam(L, allyTeamID)) {
5468 		return 0;
5469 	}
5470 	const int varID = luaL_checkint(L, 2);
5471 	if ((varID < 0) || (varID >= CUnitScript::ALLY_VAR_COUNT)) {
5472 		return 0;
5473 	}
5474 	const int value = CUnitScript::GetAllyVars(allyTeamID)[varID];
5475 	if (luaL_optboolean(L, 3, false)) {
5476 		lua_pushnumber(L, UNPACKX(value));
5477 		lua_pushnumber(L, UNPACKZ(value));
5478 		return 2;
5479 	}
5480 	lua_pushnumber(L, value);
5481 	return 1;
5482 }
5483 
5484 
GetCOBGlobalVar(lua_State * L)5485 int LuaSyncedRead::GetCOBGlobalVar(lua_State* L)
5486 {
5487 	const int varID = luaL_checkint(L, 1);
5488 	if ((varID < 0) || (varID >= CUnitScript::GLOBAL_VAR_COUNT)) {
5489 		return 0;
5490 	}
5491 	const int value = CUnitScript::GetGlobalVars()[varID];
5492 	if (luaL_optboolean(L, 2, false)) {
5493 		lua_pushnumber(L, UNPACKX(value));
5494 		lua_pushnumber(L, UNPACKZ(value));
5495 		return 2;
5496 	}
5497 	lua_pushnumber(L, value);
5498 	return 1;
5499 }
5500 
5501 
5502 /******************************************************************************/
5503 /******************************************************************************/
5504