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