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