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