1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3
4 #include "LuaSyncedMoveCtrl.h"
5
6 #include "LuaInclude.h"
7
8 #include "LuaHandle.h"
9 #include "LuaHashString.h"
10 #include "LuaUtils.h"
11 #include "Sim/MoveTypes/MoveDefHandler.h"
12 #include "Sim/MoveTypes/ScriptMoveType.h"
13 #include "Sim/MoveTypes/GroundMoveType.h"
14 #include "Sim/MoveTypes/AAirMoveType.h"
15 #include "Sim/MoveTypes/StrafeAirMoveType.h"
16 #include "Sim/MoveTypes/HoverAirMoveType.h"
17 #include "Sim/Units/Unit.h"
18 #include "Sim/Units/UnitHandler.h"
19 #include "System/myMath.h"
20 #include "System/Log/ILog.h"
21 #include "System/Sync/HsiehHash.h"
22
23 #include <cctype>
24
25
26 /******************************************************************************/
27 /******************************************************************************/
28
29
PushMoveCtrl(lua_State * L)30 bool LuaSyncedMoveCtrl::PushMoveCtrl(lua_State* L)
31 {
32 lua_pushliteral(L, "MoveCtrl");
33 lua_newtable(L);
34
35 #define REGISTER_LUA_CFUNC(x) \
36 lua_pushstring(L, #x); \
37 lua_pushcfunction(L, x); \
38 lua_rawset(L, -3)
39
40 REGISTER_LUA_CFUNC(IsEnabled);
41
42 REGISTER_LUA_CFUNC(Enable);
43 REGISTER_LUA_CFUNC(Disable);
44
45 REGISTER_LUA_CFUNC(SetTag);
46 REGISTER_LUA_CFUNC(GetTag);
47
48 REGISTER_LUA_CFUNC(SetProgressState);
49
50 REGISTER_LUA_CFUNC(SetExtrapolate);
51
52 REGISTER_LUA_CFUNC(SetPhysics);
53 REGISTER_LUA_CFUNC(SetPosition);
54 REGISTER_LUA_CFUNC(SetVelocity);
55 REGISTER_LUA_CFUNC(SetRelativeVelocity);
56 REGISTER_LUA_CFUNC(SetRotation);
57 REGISTER_LUA_CFUNC(SetRotationVelocity);
58 REGISTER_LUA_CFUNC(SetRotationOffset);
59 REGISTER_LUA_CFUNC(SetHeading);
60
61 REGISTER_LUA_CFUNC(SetTrackSlope);
62 REGISTER_LUA_CFUNC(SetTrackGround);
63 REGISTER_LUA_CFUNC(SetGroundOffset);
64 REGISTER_LUA_CFUNC(SetGravity);
65 REGISTER_LUA_CFUNC(SetDrag);
66
67 REGISTER_LUA_CFUNC(SetWindFactor);
68
69 REGISTER_LUA_CFUNC(SetLimits);
70
71 REGISTER_LUA_CFUNC(SetNoBlocking);
72
73 REGISTER_LUA_CFUNC(SetShotStop);
74 REGISTER_LUA_CFUNC(SetSlopeStop);
75 REGISTER_LUA_CFUNC(SetCollideStop);
76
77 REGISTER_LUA_CFUNC(SetAirMoveTypeData);
78 REGISTER_LUA_CFUNC(SetGroundMoveTypeData);
79 REGISTER_LUA_CFUNC(SetGunshipMoveTypeData);
80
81 REGISTER_LUA_CFUNC(SetMoveDef);
82
83 lua_rawset(L, -3);
84
85 return true;
86 }
87
88
ParseUnit(lua_State * L,const char * caller,int index)89 static inline CUnit* ParseUnit(lua_State* L, const char* caller, int index)
90 {
91 // handles negative and out-of-bound ID's
92 CUnit* unit = unitHandler->GetUnit(luaL_checkint(L, index));
93
94 if (unit == NULL)
95 return NULL;
96 if (!CanControlUnit(L, unit))
97 return NULL;
98
99 return unit;
100 }
101
102
ParseScriptMoveType(lua_State * L,const char * caller,int index)103 static inline CScriptMoveType* ParseScriptMoveType(lua_State* L, const char* caller, int index)
104 {
105 CUnit* unit = ParseUnit(L, caller, index);
106
107 if (unit == NULL)
108 return NULL;
109 if (!unit->UsingScriptMoveType())
110 return NULL;
111
112 return (static_cast<CScriptMoveType*>(unit->moveType));
113 }
114
115 template<typename DerivedMoveType>
ParseDerivedMoveType(lua_State * L,const char * caller,int index)116 static inline DerivedMoveType* ParseDerivedMoveType(lua_State* L, const char* caller, int index)
117 {
118 CUnit* unit = ParseUnit(L, caller, index);
119
120 if (unit == NULL)
121 return NULL;
122
123 return (dynamic_cast<DerivedMoveType*>(unit->moveType));
124 }
125
126
127
128 /******************************************************************************/
129 /******************************************************************************/
130
IsEnabled(lua_State * L)131 int LuaSyncedMoveCtrl::IsEnabled(lua_State* L)
132 {
133 CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
134
135 if (unit == NULL)
136 return 0;
137
138 lua_pushboolean(L, unit->UsingScriptMoveType());
139 return 1;
140 }
141
142
Enable(lua_State * L)143 int LuaSyncedMoveCtrl::Enable(lua_State* L)
144 {
145 CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
146
147 if (unit == NULL)
148 return 0;
149
150 unit->EnableScriptMoveType();
151 return 0;
152 }
153
154
Disable(lua_State * L)155 int LuaSyncedMoveCtrl::Disable(lua_State* L)
156 {
157 CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
158
159 if (unit == NULL)
160 return 0;
161
162 unit->DisableScriptMoveType();
163 return 0;
164 }
165
166
167 /******************************************************************************/
168
SetTag(lua_State * L)169 int LuaSyncedMoveCtrl::SetTag(lua_State* L)
170 {
171 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
172
173 if (moveType == NULL)
174 return 0;
175
176 moveType->tag = luaL_checkint(L, 2);
177 return 0;
178 }
179
180
GetTag(lua_State * L)181 int LuaSyncedMoveCtrl::GetTag(lua_State* L)
182 {
183 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
184
185 if (moveType == NULL)
186 return 0;
187
188 lua_pushnumber(L, moveType->tag);
189 return 1;
190 }
191
192
193 /******************************************************************************/
194 /******************************************************************************/
195
SetProgressState(lua_State * L)196 int LuaSyncedMoveCtrl::SetProgressState(lua_State* L)
197 {
198 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
199
200 if (moveType == NULL)
201 return 0;
202
203 const int args = lua_gettop(L); // number of arguments
204 if (args < 2) {
205 luaL_error(L, "Incorrect arguments to SetProgressState()");
206 }
207
208 if (lua_isnumber(L, 2)) {
209 const int state = lua_toint(L, 2);
210 if ((state < AMoveType::Done) || (state > AMoveType::Failed)) {
211 luaL_error(L, "SetProgressState(): bad state value (%d)", state);
212 }
213 moveType->progressState = (AMoveType::ProgressState) state;
214 }
215 else if (lua_isstring(L, 2)) {
216 const string state = lua_tostring(L, 2);
217 if (state == "done") {
218 moveType->progressState = AMoveType::Done;
219 } else if (state == "active") {
220 moveType->progressState = AMoveType::Active;
221 } else if (state == "failed") {
222 moveType->progressState = AMoveType::Failed;
223 } else {
224 luaL_error(L, "SetProgressState(): bad state value (%s)", state.c_str());
225 }
226 }
227 else {
228 luaL_error(L, "Incorrect arguments to SetProgressState()");
229 }
230 return 0;
231 }
232
233
234 /******************************************************************************/
235
SetExtrapolate(lua_State * L)236 int LuaSyncedMoveCtrl::SetExtrapolate(lua_State* L)
237 {
238 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
239
240 if (moveType == NULL)
241 return 0;
242
243 moveType->extrapolate = luaL_checkboolean(L, 2);
244 return 0;
245 }
246
247
248 /******************************************************************************/
249
SetPhysics(lua_State * L)250 int LuaSyncedMoveCtrl::SetPhysics(lua_State* L)
251 {
252 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
253
254 if (moveType == NULL)
255 return 0;
256
257 const float3 pos(luaL_checkfloat(L, 2),
258 luaL_checkfloat(L, 3),
259 luaL_checkfloat(L, 4));
260 const float3 vel(luaL_checkfloat(L, 5),
261 luaL_checkfloat(L, 6),
262 luaL_checkfloat(L, 7));
263 const float3 rot(luaL_checkfloat(L, 8),
264 luaL_checkfloat(L, 9),
265 luaL_checkfloat(L, 10));
266 ASSERT_SYNCED(pos);
267 ASSERT_SYNCED(vel);
268 ASSERT_SYNCED(rot);
269 moveType->SetPhysics(pos, vel, rot);
270 return 0;
271 }
272
273
SetPosition(lua_State * L)274 int LuaSyncedMoveCtrl::SetPosition(lua_State* L)
275 {
276 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
277
278 if (moveType == NULL)
279 return 0;
280
281 const float3 pos(luaL_checkfloat(L, 2),
282 luaL_checkfloat(L, 3),
283 luaL_checkfloat(L, 4));
284 ASSERT_SYNCED(pos);
285 moveType->SetPosition(pos);
286 return 0;
287 }
288
289
SetVelocity(lua_State * L)290 int LuaSyncedMoveCtrl::SetVelocity(lua_State* L)
291 {
292 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
293
294 if (moveType == NULL)
295 return 0;
296
297 const float3 vel(luaL_checkfloat(L, 2),
298 luaL_checkfloat(L, 3),
299 luaL_checkfloat(L, 4));
300 ASSERT_SYNCED(vel);
301 moveType->SetVelocity(vel);
302 return 0;
303 }
304
305
SetRelativeVelocity(lua_State * L)306 int LuaSyncedMoveCtrl::SetRelativeVelocity(lua_State* L)
307 {
308 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
309
310 if (moveType == NULL)
311 return 0;
312
313 const float3 relVel(luaL_checkfloat(L, 2),
314 luaL_checkfloat(L, 3),
315 luaL_checkfloat(L, 4));
316 ASSERT_SYNCED(relVel);
317 moveType->SetRelativeVelocity(relVel);
318 return 0;
319 }
320
321
SetRotation(lua_State * L)322 int LuaSyncedMoveCtrl::SetRotation(lua_State* L)
323 {
324 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
325
326 if (moveType == NULL)
327 return 0;
328
329 const float3 rot(luaL_checkfloat(L, 2),
330 luaL_checkfloat(L, 3),
331 luaL_checkfloat(L, 4));
332 ASSERT_SYNCED(rot);
333 moveType->SetRotation(rot);
334 return 0;
335 }
336
337
SetRotationOffset(lua_State * L)338 int LuaSyncedMoveCtrl::SetRotationOffset(lua_State* L)
339 {
340 // DEPRECATED
341 return 0;
342 }
343
344
SetRotationVelocity(lua_State * L)345 int LuaSyncedMoveCtrl::SetRotationVelocity(lua_State* L)
346 {
347 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
348
349 if (moveType == NULL)
350 return 0;
351
352 const float3 rotVel(luaL_checkfloat(L, 2),
353 luaL_checkfloat(L, 3),
354 luaL_checkfloat(L, 4));
355 ASSERT_SYNCED(rotVel);
356 moveType->SetRotationVelocity(rotVel);
357 return 0;
358 }
359
360
SetHeading(lua_State * L)361 int LuaSyncedMoveCtrl::SetHeading(lua_State* L)
362 {
363 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
364
365 if (moveType == NULL)
366 return 0;
367
368 const short heading = (short)luaL_checknumber(L, 2);
369 ASSERT_SYNCED((short)heading);
370 moveType->SetHeading(heading);
371 return 0;
372 }
373
374
375 /******************************************************************************/
376
SetTrackSlope(lua_State * L)377 int LuaSyncedMoveCtrl::SetTrackSlope(lua_State* L)
378 {
379 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
380
381 if (moveType == NULL)
382 return 0;
383
384 moveType->trackSlope = luaL_checkboolean(L, 2);
385 return 0;
386 }
387
388
SetTrackGround(lua_State * L)389 int LuaSyncedMoveCtrl::SetTrackGround(lua_State* L)
390 {
391 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
392
393 if (moveType == NULL)
394 return 0;
395
396 moveType->trackGround = luaL_checkboolean(L, 2);
397 return 0;
398 }
399
400
SetGroundOffset(lua_State * L)401 int LuaSyncedMoveCtrl::SetGroundOffset(lua_State* L)
402 {
403 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
404
405 if (moveType == NULL)
406 return 0;
407
408 moveType->groundOffset = luaL_checkfloat(L, 2);
409 return 0;
410 }
411
412
SetGravity(lua_State * L)413 int LuaSyncedMoveCtrl::SetGravity(lua_State* L)
414 {
415 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
416
417 if (moveType == NULL)
418 return 0;
419
420 moveType->gravityFactor = luaL_checkfloat(L, 2);
421 return 0;
422 }
423
424
SetDrag(lua_State * L)425 int LuaSyncedMoveCtrl::SetDrag(lua_State* L)
426 {
427 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
428
429 if (moveType == NULL)
430 return 0;
431
432 moveType->drag = luaL_checkfloat(L, 2);
433 return 0;
434 }
435
436
SetWindFactor(lua_State * L)437 int LuaSyncedMoveCtrl::SetWindFactor(lua_State* L)
438 {
439 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
440
441 if (moveType == NULL)
442 return 0;
443
444 moveType->windFactor = luaL_checkfloat(L, 2);
445 return 0;
446 }
447
448
SetLimits(lua_State * L)449 int LuaSyncedMoveCtrl::SetLimits(lua_State* L)
450 {
451 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
452
453 if (moveType == NULL)
454 return 0;
455
456 const float3 mins(luaL_checkfloat(L, 2),
457 luaL_checkfloat(L, 3),
458 luaL_checkfloat(L, 4));
459 const float3 maxs(luaL_checkfloat(L, 5),
460 luaL_checkfloat(L, 6),
461 luaL_checkfloat(L, 7));
462 moveType->mins = mins;
463 moveType->maxs = maxs;
464 return 0;
465 }
466
467
468 /******************************************************************************/
469
SetNoBlocking(lua_State * L)470 int LuaSyncedMoveCtrl::SetNoBlocking(lua_State* L)
471 {
472 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
473
474 if (moveType == NULL)
475 return 0;
476
477 // marks or unmarks the unit on the blocking-map, but
478 // does not change its blocking (collidable) state
479 moveType->SetNoBlocking(luaL_checkboolean(L, 2));
480 return 0;
481 }
482
483
SetShotStop(lua_State * L)484 int LuaSyncedMoveCtrl::SetShotStop(lua_State* L)
485 {
486 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
487
488 if (moveType == NULL)
489 return 0;
490
491 moveType->shotStop = luaL_checkboolean(L, 2);
492 return 0;
493 }
494
495
SetSlopeStop(lua_State * L)496 int LuaSyncedMoveCtrl::SetSlopeStop(lua_State* L)
497 {
498 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
499
500 if (moveType == NULL)
501 return 0;
502
503 moveType->slopeStop = luaL_checkboolean(L, 2);
504 return 0;
505 }
506
507
SetCollideStop(lua_State * L)508 int LuaSyncedMoveCtrl::SetCollideStop(lua_State* L)
509 {
510 CScriptMoveType* moveType = ParseScriptMoveType(L, __FUNCTION__, 1);
511
512 if (moveType == NULL)
513 return 0;
514
515 moveType->gndStop = luaL_checkboolean(L, 2); // FIXME
516 moveType->collideStop = lua_toboolean(L, 2);
517 return 0;
518 }
519
520
521
522 /******************************************************************************/
523 /* MoveType member-value handling */
524
525 template<typename value_type>
SetMoveTypeValue(AMoveType * mt,const string & key,value_type value)526 static bool SetMoveTypeValue(AMoveType* mt, const string& key, value_type value)
527 {
528 return (mt->SetMemberValue(HsiehHash(key.c_str(), key.size(), 0), &value));
529 }
530
SetMoveTypeValue(lua_State * L,AMoveType * moveType,int keyIdx,int valIdx)531 static inline bool SetMoveTypeValue(lua_State* L, AMoveType* moveType, int keyIdx, int valIdx)
532 {
533 if (lua_isnumber(L, valIdx))
534 return (SetMoveTypeValue<float>(moveType, lua_tostring(L, keyIdx), lua_tofloat(L, valIdx)));
535
536 if (lua_isboolean(L, valIdx))
537 return (SetMoveTypeValue<bool>(moveType, lua_tostring(L, keyIdx), lua_toboolean(L, valIdx)));
538
539 return false;
540 }
541
542
SetMoveTypeData(lua_State * L,AMoveType * moveType,const char * caller)543 static int SetMoveTypeData(lua_State* L, AMoveType* moveType, const char* caller)
544 {
545 int numAssignedValues = 0;
546
547 if (moveType == NULL) {
548 luaL_error(L, "[%s] unit %d has incompatible movetype for %s", __FUNCTION__, caller, lua_tonumber(L, 1));
549 return numAssignedValues;
550 }
551
552 switch (lua_gettop(L)) {
553 case 2: {
554 // two args (unitID, {"key1" = (number | bool) value1, ...})
555 const int tableIdx = 2;
556
557 if (lua_istable(L, tableIdx)) {
558 for (lua_pushnil(L); lua_next(L, tableIdx) != 0; lua_pop(L, 1)) {
559 if (lua_israwstring(L, -2) && SetMoveTypeValue(L, moveType, -2, -1)) {
560 numAssignedValues++;
561 } else {
562 LOG_L(L_WARNING, "[%s] incompatible movetype key for %s", __FUNCTION__, caller);
563 }
564 }
565 }
566 } break;
567
568 case 3: {
569 // three args (unitID, "key", (number | bool) value)
570 if (lua_isstring(L, 2) && SetMoveTypeValue(L, moveType, 2, 3)) {
571 numAssignedValues++;
572 } else {
573 LOG_L(L_WARNING, "[%s] incompatible movetype key for %s", __FUNCTION__, caller);
574 }
575 } break;
576 }
577
578 lua_pushnumber(L, numAssignedValues);
579 return 1;
580 }
581
582
SetGunshipMoveTypeData(lua_State * L)583 int LuaSyncedMoveCtrl::SetGunshipMoveTypeData(lua_State* L)
584 {
585 return (SetMoveTypeData(L, ParseDerivedMoveType<CHoverAirMoveType>(L, __FUNCTION__, 1), __FUNCTION__));
586 }
587
SetAirMoveTypeData(lua_State * L)588 int LuaSyncedMoveCtrl::SetAirMoveTypeData(lua_State* L)
589 {
590 return (SetMoveTypeData(L, ParseDerivedMoveType<CStrafeAirMoveType>(L, __FUNCTION__, 1), __FUNCTION__));
591 }
592
SetGroundMoveTypeData(lua_State * L)593 int LuaSyncedMoveCtrl::SetGroundMoveTypeData(lua_State* L)
594 {
595 return (SetMoveTypeData(L, ParseDerivedMoveType<CGroundMoveType>(L, __FUNCTION__, 1), __FUNCTION__));
596 }
597
598
599
600 /******************************************************************************/
601 /******************************************************************************/
602
SetMoveDef(lua_State * L)603 int LuaSyncedMoveCtrl::SetMoveDef(lua_State* L)
604 {
605 CUnit* unit = ParseUnit(L, __FUNCTION__, 1);
606 MoveDef* moveDef = NULL;
607
608 if (unit == NULL) {
609 lua_pushboolean(L, false);
610 return 1;
611 }
612 if (unit->moveDef == NULL) {
613 // aircraft or structure, not supported
614 lua_pushboolean(L, false);
615 return 1;
616 }
617
618 // MoveType instance must already have been assigned
619 assert(unit->moveType != NULL);
620
621 if (lua_isnumber(L, 2))
622 moveDef = moveDefHandler->GetMoveDefByPathType(Clamp(luaL_checkint(L, 2), 0, int(moveDefHandler->GetNumMoveDefs()) - 1));
623 if (lua_isstring(L, 2))
624 moveDef = moveDefHandler->GetMoveDefByName(lua_tostring(L, 2));
625
626 if (moveDef == NULL) {
627 lua_pushboolean(L, false);
628 return 1;
629 }
630
631 if (moveDef->udRefCount == 0) {
632 // pathfinders contain optimizations that
633 // make unreferenced movedef's non-usable
634 lua_pushboolean(L, false);
635 return 1;
636 }
637
638 // PFS might have cached data by path-type which must be cleared
639 if (unit->UsingScriptMoveType()) {
640 unit->prevMoveType->StopMoving();
641 } else {
642 unit->moveType->StopMoving();
643 }
644
645 // the case where moveDef->pathType == unit->moveDef->pathType does no harm
646 // note: if a unit (ID) is available, then its current MoveDef should always
647 // be taken over the MoveDef corresponding to its UnitDef::pathType wherever
648 // MoveDef properties are used in decision logic
649 lua_pushboolean(L, (unit->moveDef = moveDef) != NULL);
650 return 1;
651 }
652
653