1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 #include "LuaUnsyncedCtrl.h"
4 
5 #include "LuaInclude.h"
6 #include "LuaHandle.h"
7 #include "LuaHashString.h"
8 #include "LuaUtils.h"
9 #include "LuaTextures.h"
10 
11 #include "ExternalAI/EngineOutHandler.h"
12 #include "ExternalAI/SkirmishAIHandler.h"
13 #include "Game/Camera.h"
14 #include "Game/CameraHandler.h"
15 #include "Game/Camera/CameraController.h"
16 #include "Game/Game.h"
17 #include "Game/GlobalUnsynced.h"
18 #include "Game/SelectedUnitsHandler.h"
19 #include "Game/Players/Player.h"
20 #include "Game/Players/PlayerHandler.h"
21 #include "Game/InMapDraw.h"
22 #include "Game/InMapDrawModel.h"
23 #include "Game/UI/CommandColors.h"
24 #include "Game/UI/CursorIcons.h"
25 #include "Game/UI/GuiHandler.h"
26 #include "Game/UI/InfoConsole.h"
27 #include "Game/UI/KeyCodes.h"
28 #include "Game/UI/KeySet.h"
29 #include "Game/UI/KeyBindings.h"
30 #include "Game/UI/MiniMap.h"
31 #include "Game/UI/MouseHandler.h"
32 #include "Map/MapInfo.h"
33 #include "Map/ReadMap.h"
34 #include "Map/BaseGroundDrawer.h"
35 #include "Map/BaseGroundTextures.h"
36 #include "Rendering/Env/ISky.h"
37 #include "Rendering/GL/myGL.h"
38 #include "Rendering/CommandDrawer.h"
39 #include "Rendering/IconHandler.h"
40 #include "Rendering/LineDrawer.h"
41 #include "Rendering/UnitDrawer.h"
42 #include "Rendering/Textures/Bitmap.h"
43 #include "Rendering/Textures/NamedTextures.h"
44 #include "Sim/Misc/TeamHandler.h"
45 #include "Sim/Projectiles/Projectile.h"
46 #include "Sim/Projectiles/ProjectileHandler.h"
47 #include "Sim/Units/Unit.h"
48 #include "Sim/Units/UnitDefHandler.h"
49 #include "Sim/Units/UnitHandler.h"
50 #include "Sim/Units/CommandAI/CommandAI.h"
51 #include "Sim/Units/Groups/Group.h"
52 #include "Sim/Units/Groups/GroupHandler.h"
53 #include "System/Config/ConfigHandler.h"
54 #include "System/EventHandler.h"
55 #include "System/GlobalConfig.h"
56 #include "System/Log/DefaultFilter.h"
57 #include "System/Log/ILog.h"
58 #include "Net/Protocol/NetProtocol.h"
59 #include "System/Net/PackPacket.h"
60 #include "System/Util.h"
61 #include "System/Sound/ISound.h"
62 #include "System/Sound/ISoundChannels.h"
63 #include "System/FileSystem/FileHandler.h"
64 #include "System/FileSystem/DataDirLocater.h"
65 #include "System/FileSystem/FileSystem.h"
66 #include "System/Platform/Watchdog.h"
67 #include "System/Platform/WindowManagerHelper.h"
68 
69 #include <boost/cstdint.hpp>
70 #include "System/Platform/Misc.h"
71 
72 #include "System/Sound/OpenAL/EFX.h"
73 #include "System/Sound/OpenAL/EFXPresets.h"
74 
75 #include <map>
76 #include <set>
77 #include <cctype>
78 #include <cfloat>
79 
80 #include <fstream>
81 
82 #include <SDL_clipboard.h>
83 #include <SDL_mouse.h>
84 
85 using std::min;
86 using std::max;
87 
88 // MinGW defines this for a WINAPI function
89 #undef SendMessage
90 #undef CreateDirectory
91 
92 const int CMD_INDEX_OFFSET = 1; // starting index for command descriptions
93 
94 
95 /******************************************************************************/
96 /******************************************************************************/
97 
98 std::set<int> drawCmdQueueUnits;
99 
100 /******************************************************************************/
101 /******************************************************************************/
102 
PushEntries(lua_State * L)103 bool LuaUnsyncedCtrl::PushEntries(lua_State* L)
104 {
105 #define REGISTER_LUA_CFUNC(x) \
106 	lua_pushstring(L, #x);      \
107 	lua_pushcfunction(L, x);    \
108 	lua_rawset(L, -3)
109 
110 	REGISTER_LUA_CFUNC(Echo);
111 	REGISTER_LUA_CFUNC(Log);
112 
113 	REGISTER_LUA_CFUNC(SendMessage);
114 	REGISTER_LUA_CFUNC(SendMessageToPlayer);
115 	REGISTER_LUA_CFUNC(SendMessageToTeam);
116 	REGISTER_LUA_CFUNC(SendMessageToAllyTeam);
117 	REGISTER_LUA_CFUNC(SendMessageToSpectators);
118 
119 	REGISTER_LUA_CFUNC(LoadSoundDef);
120 	REGISTER_LUA_CFUNC(PlaySoundFile);
121 	REGISTER_LUA_CFUNC(PlaySoundStream);
122 	REGISTER_LUA_CFUNC(StopSoundStream);
123 	REGISTER_LUA_CFUNC(PauseSoundStream);
124 	REGISTER_LUA_CFUNC(SetSoundStreamVolume);
125 	REGISTER_LUA_CFUNC(SetSoundEffectParams);
126 
127 	REGISTER_LUA_CFUNC(SetCameraState);
128 	REGISTER_LUA_CFUNC(SetCameraTarget);
129 
130 	REGISTER_LUA_CFUNC(SelectUnitMap);
131 	REGISTER_LUA_CFUNC(SelectUnitArray);
132 
133 	REGISTER_LUA_CFUNC(AddWorldIcon);
134 	REGISTER_LUA_CFUNC(AddWorldText);
135 	REGISTER_LUA_CFUNC(AddWorldUnit);
136 
137 	REGISTER_LUA_CFUNC(DrawUnitCommands);
138 
139 	REGISTER_LUA_CFUNC(SetTeamColor);
140 
141 	REGISTER_LUA_CFUNC(AssignMouseCursor);
142 	REGISTER_LUA_CFUNC(ReplaceMouseCursor);
143 
144 	REGISTER_LUA_CFUNC(SetCustomCommandDrawData);
145 
146 	REGISTER_LUA_CFUNC(SetDrawSky);
147 	REGISTER_LUA_CFUNC(SetDrawWater);
148 	REGISTER_LUA_CFUNC(SetDrawGround);
149 	REGISTER_LUA_CFUNC(SetDrawGroundDeferred);
150 	REGISTER_LUA_CFUNC(SetDrawModelsDeferred);
151 
152 	REGISTER_LUA_CFUNC(SetWaterParams);
153 
154 	REGISTER_LUA_CFUNC(AddMapLight);
155 	REGISTER_LUA_CFUNC(AddModelLight);
156 	REGISTER_LUA_CFUNC(UpdateMapLight);
157 	REGISTER_LUA_CFUNC(UpdateModelLight);
158 	REGISTER_LUA_CFUNC(SetMapLightTrackingState);
159 	REGISTER_LUA_CFUNC(SetModelLightTrackingState);
160 	REGISTER_LUA_CFUNC(SetMapSquareTexture);
161 
162 	REGISTER_LUA_CFUNC(SetUnitNoDraw);
163 	REGISTER_LUA_CFUNC(SetUnitNoMinimap);
164 	REGISTER_LUA_CFUNC(SetUnitNoSelect);
165 	REGISTER_LUA_CFUNC(SetUnitLeaveTracks);
166 
167 	REGISTER_LUA_CFUNC(AddUnitIcon);
168 	REGISTER_LUA_CFUNC(FreeUnitIcon);
169 
170 	REGISTER_LUA_CFUNC(ExtractModArchiveFile);
171 
172 	// moved from LuaUI
173 
174 //FIXME	REGISTER_LUA_CFUNC(SetShockFrontFactors);
175 
176 	REGISTER_LUA_CFUNC(GetConfigInt);
177 	REGISTER_LUA_CFUNC(SetConfigInt);
178 	REGISTER_LUA_CFUNC(GetConfigString);
179 	REGISTER_LUA_CFUNC(SetConfigString);
180 
181 	REGISTER_LUA_CFUNC(CreateDir);
182 
183 	REGISTER_LUA_CFUNC(SendCommands);
184 	REGISTER_LUA_CFUNC(GiveOrder);
185 	REGISTER_LUA_CFUNC(GiveOrderToUnit);
186 	REGISTER_LUA_CFUNC(GiveOrderToUnitMap);
187 	REGISTER_LUA_CFUNC(GiveOrderToUnitArray);
188 	REGISTER_LUA_CFUNC(GiveOrderArrayToUnitMap);
189 	REGISTER_LUA_CFUNC(GiveOrderArrayToUnitArray);
190 
191 	REGISTER_LUA_CFUNC(SendLuaUIMsg);
192 	REGISTER_LUA_CFUNC(SendLuaGaiaMsg);
193 	REGISTER_LUA_CFUNC(SendLuaRulesMsg);
194 
195 	REGISTER_LUA_CFUNC(LoadCmdColorsConfig);
196 	REGISTER_LUA_CFUNC(LoadCtrlPanelConfig);
197 
198 	REGISTER_LUA_CFUNC(SetActiveCommand);
199 	REGISTER_LUA_CFUNC(ForceLayoutUpdate);
200 
201 	REGISTER_LUA_CFUNC(SetMouseCursor);
202 	REGISTER_LUA_CFUNC(WarpMouse);
203 
204 	REGISTER_LUA_CFUNC(SetClipboard);
205 
206 	REGISTER_LUA_CFUNC(SetCameraOffset);
207 
208 	REGISTER_LUA_CFUNC(UpdateInfoTexture);
209 	REGISTER_LUA_CFUNC(SetLosViewColors);
210 
211 	REGISTER_LUA_CFUNC(Restart);
212 	REGISTER_LUA_CFUNC(SetWMIcon);
213 	REGISTER_LUA_CFUNC(SetWMCaption);
214 
215 	REGISTER_LUA_CFUNC(SetUnitDefIcon);
216 	REGISTER_LUA_CFUNC(SetUnitDefImage);
217 
218 	REGISTER_LUA_CFUNC(SetUnitGroup);
219 
220 	REGISTER_LUA_CFUNC(SetShareLevel);
221 	REGISTER_LUA_CFUNC(ShareResources);
222 
223 	REGISTER_LUA_CFUNC(SetLastMessagePosition);
224 
225 	REGISTER_LUA_CFUNC(MarkerAddPoint);
226 	REGISTER_LUA_CFUNC(MarkerAddLine);
227 	REGISTER_LUA_CFUNC(MarkerErasePosition);
228 
229 	REGISTER_LUA_CFUNC(SetDrawSelectionInfo);
230 
231 	REGISTER_LUA_CFUNC(SetBuildSpacing);
232 	REGISTER_LUA_CFUNC(SetBuildFacing);
233 
234 	REGISTER_LUA_CFUNC(SetSunParameters);
235 	REGISTER_LUA_CFUNC(SetSunManualControl);
236 	REGISTER_LUA_CFUNC(SetSunDirection);
237 
238 	REGISTER_LUA_CFUNC(SendSkirmishAIMessage);
239 
240 	REGISTER_LUA_CFUNC(SetLogSectionFilterLevel);
241 
242 	REGISTER_LUA_CFUNC(ClearWatchDogTimer);
243 
244 	return true;
245 }
246 
247 
248 /******************************************************************************/
249 /******************************************************************************/
250 //
251 //  Parsing helpers
252 //
253 
ParseRawProjectile(lua_State * L,const char * caller,int index,bool synced)254 static inline CProjectile* ParseRawProjectile(lua_State* L, const char* caller, int index, bool synced)
255 {
256 	if (!lua_isnumber(L, index)) {
257 		if (caller != NULL) {
258 			luaL_error(L, "[%s] projectile ID parameter in %s() not a number\n", __FUNCTION__, caller);
259 		}
260 		return NULL;
261 	}
262 
263 	const int projID = lua_toint(L, index);
264 
265 	const ProjectileMapValPair* pp = NULL;
266 	if (synced) {
267 		pp = projectileHandler->GetMapPairBySyncedID(projID);
268 	} else {
269 		pp = projectileHandler->GetMapPairByUnsyncedID(projID);
270 	}
271 
272 	return (pp) ? pp->first : NULL;
273 }
274 
ParseRawUnit(lua_State * L,const char * caller,int index)275 static inline CUnit* ParseRawUnit(lua_State* L, const char* caller, int index)
276 {
277 	if (!lua_isnumber(L, index)) {
278 		if (caller != NULL) {
279 			luaL_error(L, "[%s] unit ID parameter in %s() not a number\n", __FUNCTION__, caller);
280 		}
281 		return NULL;
282 	}
283 
284 	const int unitID = lua_toint(L, index);
285 	if ((unitID < 0) || (static_cast<size_t>(unitID) >= unitHandler->MaxUnits())) {
286 		luaL_error(L, "%s(): Bad unitID: %d\n", caller, unitID);
287 	}
288 
289 	return unitHandler->units[unitID];
290 }
291 
292 
ParseAllyUnit(lua_State * L,const char * caller,int index)293 static inline CUnit* ParseAllyUnit(lua_State* L, const char* caller, int index)
294 {
295 	CUnit* unit = ParseRawUnit(L, caller, index);
296 	if (unit == NULL) {
297 		return NULL;
298 	}
299 	if (CLuaHandle::GetHandleReadAllyTeam(L) < 0) {
300 		return CLuaHandle::GetHandleFullRead(L) ? unit : NULL;
301 	}
302 	return (unit->allyteam == CLuaHandle::GetHandleReadAllyTeam(L)) ? unit : NULL;
303 }
304 
305 
ParseCtrlUnit(lua_State * L,const char * caller,int index)306 static inline CUnit* ParseCtrlUnit(lua_State* L,
307                                      const char* caller, int index)
308 {
309 	CUnit* unit = ParseRawUnit(L, caller, index);
310 	if (unit == NULL) {
311 		return NULL;
312 	}
313 	return (CanControlTeam(L, unit->team) ? unit : NULL);
314 }
315 
316 
ParseSelectUnit(lua_State * L,const char * caller,int index)317 static inline CUnit* ParseSelectUnit(lua_State* L,
318                                      const char* caller, int index)
319 {
320 	CUnit* unit = ParseRawUnit(L, caller, index);
321 	if (unit == NULL || unit->noSelect) {
322 		return NULL;
323 	}
324 	const int selectTeam = CLuaHandle::GetHandleSelectTeam(L);
325 	if (selectTeam < 0) {
326 		return (selectTeam == CEventClient::AllAccessTeam) ? unit : NULL;
327 	}
328 	if (selectTeam == unit->team) {
329 		return unit;
330 	}
331 	return NULL;
332 }
333 
334 
335 /******************************************************************************/
336 /******************************************************************************/
337 
DrawUnitCommandQueues()338 void LuaUnsyncedCtrl::DrawUnitCommandQueues()
339 {
340 	if (drawCmdQueueUnits.empty()) {
341 		return;
342 	}
343 
344 	glDisable(GL_TEXTURE_2D);
345 	glDisable(GL_DEPTH_TEST);
346 
347 	lineDrawer.Configure(cmdColors.UseColorRestarts(),
348 	                     cmdColors.UseRestartColor(),
349 	                     cmdColors.restart,
350 	                     cmdColors.RestartAlpha());
351 	lineDrawer.SetupLineStipple();
352 
353 	glEnable(GL_BLEND);
354 	glBlendFunc((GLenum)cmdColors.QueuedBlendSrc(),
355 	            (GLenum)cmdColors.QueuedBlendDst());
356 
357 	glLineWidth(cmdColors.QueuedLineWidth());
358 
359 	std::set<int>::const_iterator ui;
360 
361 	for (ui = drawCmdQueueUnits.begin(); ui != drawCmdQueueUnits.end(); ++ui) {
362 		const CUnit* unit = unitHandler->GetUnit(*ui);
363 
364 		if (unit == NULL || unit->commandAI == NULL) {
365 			continue;
366 		}
367 
368 		commandDrawer->Draw(unit->commandAI);
369 	}
370 
371 	glLineWidth(1.0f);
372 	glEnable(GL_DEPTH_TEST);
373 }
374 
375 
ClearUnitCommandQueues()376 void LuaUnsyncedCtrl::ClearUnitCommandQueues()
377 {
378 	drawCmdQueueUnits.clear();
379 }
380 
381 
382 /******************************************************************************/
383 /******************************************************************************/
384 //
385 //  The call-outs
386 //
387 
Echo(lua_State * L)388 int LuaUnsyncedCtrl::Echo(lua_State* L)
389 {
390 	return LuaUtils::Echo(L);
391 }
392 
Log(lua_State * L)393 int LuaUnsyncedCtrl::Log(lua_State* L)
394 {
395 	return LuaUtils::Log(L);
396 }
397 
ParseMessage(lua_State * L,const string & msg)398 static string ParseMessage(lua_State* L, const string& msg)
399 {
400 	string::size_type start = msg.find("<PLAYER");
401 	if (start == string::npos) {
402 		return msg;
403 	}
404 
405 	const char* number = msg.c_str() + start + strlen("<PLAYER");
406 	char* endPtr;
407 	const int playerID = (int)strtol(number, &endPtr, 10);
408 	if ((endPtr == number) || (*endPtr != '>')) {
409 		luaL_error(L, "Bad message format: %s", msg.c_str());
410 	}
411 
412 	if (!playerHandler->IsValidPlayer(playerID)) {
413 		luaL_error(L, "Invalid message playerID: %c", playerID); //FIXME
414 	}
415 	const CPlayer* player = playerHandler->Player(playerID);
416 	if ((player == NULL) || !player->active || player->name.empty()) {
417 		luaL_error(L, "Invalid message playerID: %c", playerID);
418 	}
419 
420 	const string head = msg.substr(0, start);
421 	const string tail = msg.substr(endPtr - msg.c_str() + 1);
422 
423 	return head + player->name + ParseMessage(L, tail);
424 }
425 
426 
PrintMessage(lua_State * L,const string & msg)427 static void PrintMessage(lua_State* L, const string& msg)
428 {
429 	LOG("%s", ParseMessage(L, msg).c_str());
430 }
431 
432 
SendMessage(lua_State * L)433 int LuaUnsyncedCtrl::SendMessage(lua_State* L)
434 {
435 	PrintMessage(L, luaL_checksstring(L, 1));
436 	return 0;
437 }
438 
439 
SendMessageToSpectators(lua_State * L)440 int LuaUnsyncedCtrl::SendMessageToSpectators(lua_State* L)
441 {
442 	if (gu->spectating) {
443 		PrintMessage(L, luaL_checksstring(L, 1));
444 	}
445 	return 0;
446 }
447 
448 
SendMessageToPlayer(lua_State * L)449 int LuaUnsyncedCtrl::SendMessageToPlayer(lua_State* L)
450 {
451 	const int playerID = luaL_checkint(L, 1);
452 	if (playerID == gu->myPlayerNum) {
453 		PrintMessage(L, luaL_checksstring(L, 2));
454 	}
455 	return 0;
456 }
457 
458 
SendMessageToTeam(lua_State * L)459 int LuaUnsyncedCtrl::SendMessageToTeam(lua_State* L)
460 {
461 	const int teamID = luaL_checkint(L, 1);
462 	if (teamID == gu->myTeam) {
463 		PrintMessage(L, luaL_checksstring(L, 2));
464 	}
465 	return 0;
466 }
467 
468 
SendMessageToAllyTeam(lua_State * L)469 int LuaUnsyncedCtrl::SendMessageToAllyTeam(lua_State* L)
470 {
471 	const int allyTeamID = luaL_checkint(L, 1);
472 	if (allyTeamID == gu->myAllyTeam) {
473 		PrintMessage(L, luaL_checksstring(L, 2));
474 	}
475 	return 0;
476 }
477 
478 
479 /******************************************************************************/
480 
LoadSoundDef(lua_State * L)481 int LuaUnsyncedCtrl::LoadSoundDef(lua_State* L)
482 {
483 	const string soundFile = luaL_checksstring(L, 1);
484 	bool success = sound->LoadSoundDefs(soundFile);
485 
486 	if (!CLuaHandle::GetHandleSynced(L)) {
487 		lua_pushboolean(L, success);
488 		return 1;
489 	} else {
490 		return 0;
491 	}
492 }
493 
PlaySoundFile(lua_State * L)494 int LuaUnsyncedCtrl::PlaySoundFile(lua_State* L)
495 {
496 	const int args = lua_gettop(L);
497 	bool success = false;
498 	const string soundFile = luaL_checksstring(L, 1);
499 	const unsigned int soundID = sound->GetSoundId(soundFile);
500 	if (soundID > 0) {
501 		float volume = luaL_optfloat(L, 2, 1.0f);
502 		float3 pos;
503 		float3 speed;
504 		bool pos_given = false;
505 		bool speed_given = false;
506 
507 		int index = 3;
508 		if (args >= 5 && lua_isnumber(L, 3) && lua_isnumber(L, 4) && lua_isnumber(L, 5)) {
509 			pos = float3(lua_tofloat(L, 3), lua_tofloat(L, 4), lua_tofloat(L, 5));
510 			pos_given = true;
511 			index += 3;
512 
513 			if (args >= 8 && lua_isnumber(L, 6) && lua_isnumber(L, 7) && lua_isnumber(L, 8))
514 			{
515 				speed = float3(lua_tofloat(L, 6), lua_tofloat(L, 7), lua_tofloat(L, 8));
516 				speed_given = true;
517 				index += 3;
518 			}
519 		}
520 
521 		//! last argument (with and without pos/speed arguments) is the optional `sfx channel`
522 		IAudioChannel** channel = &Channels::General;
523 		if (args >= index) {
524 			if (lua_isstring(L, index)) {
525 				string channelStr = lua_tostring(L, index);
526 				StringToLowerInPlace(channelStr);
527 
528 				if (channelStr == "battle" || channelStr == "sfx") {
529 					channel = &Channels::Battle;
530 				}
531 				else if (channelStr == "unitreply" || channelStr == "voice") {
532 					channel = &Channels::UnitReply;
533 				}
534 				else if (channelStr == "userinterface" || channelStr == "ui") {
535 					channel = &Channels::UserInterface;
536 				}
537 			} else if (lua_isnumber(L, index)) {
538 				const int channelNum = lua_toint(L, index);
539 
540 				if (channelNum == 1) {
541 					channel = &Channels::Battle;
542 				}
543 				else if (channelNum == 2) {
544 					channel = &Channels::UnitReply;
545 				}
546 				else if (channelNum == 3) {
547 					channel = &Channels::UserInterface;
548 				}
549 			}
550 		}
551 
552 		if (pos_given) {
553 			if (speed_given) {
554 				channel[0]->PlaySample(soundID, pos, speed, volume);
555 			} else {
556 				channel[0]->PlaySample(soundID, pos, volume);
557 			}
558 		} else
559 			channel[0]->PlaySample(soundID, volume);
560 
561 		success = true;
562 	}
563 
564 	if (!CLuaHandle::GetHandleSynced(L)) {
565 		lua_pushboolean(L, success);
566 		return 1;
567 	} else {
568 		return 0;
569 	}
570 }
571 
572 
PlaySoundStream(lua_State * L)573 int LuaUnsyncedCtrl::PlaySoundStream(lua_State* L)
574 {
575 	const string soundFile = luaL_checksstring(L, 1);
576 	const float volume = luaL_optnumber(L, 2, 1.0f);
577 	bool enqueue = luaL_optboolean(L, 3, false);
578 
579 	Channels::BGMusic->StreamPlay(soundFile, volume, enqueue);
580 
581 	// .ogg files don't have sound ID's generated
582 	// for them (yet), so we always succeed here
583 	if (!CLuaHandle::GetHandleSynced(L)) {
584 		lua_pushboolean(L, true);
585 		return 1;
586 	} else {
587 		return 0;
588 	}
589 }
590 
StopSoundStream(lua_State *)591 int LuaUnsyncedCtrl::StopSoundStream(lua_State*)
592 {
593 	Channels::BGMusic->StreamStop();
594 	return 0;
595 }
PauseSoundStream(lua_State *)596 int LuaUnsyncedCtrl::PauseSoundStream(lua_State*)
597 {
598 	Channels::BGMusic->StreamPause();
599 	return 0;
600 }
SetSoundStreamVolume(lua_State * L)601 int LuaUnsyncedCtrl::SetSoundStreamVolume(lua_State* L)
602 {
603 	Channels::BGMusic->SetVolume(luaL_checkfloat(L, 1));
604 	return 0;
605 }
606 
607 
SetSoundEffectParams(lua_State * L)608 int LuaUnsyncedCtrl::SetSoundEffectParams(lua_State* L)
609 {
610 #if !defined(HEADLESS) && !defined(NO_SOUND)
611 	if (!efx)
612 		return 0;
613 
614 	//! only a preset name given?
615 	if (lua_israwstring(L, 1)) {
616 		const std::string presetname = lua_tostring(L, 1);
617 		efx->SetPreset(presetname, false);
618 		return 0;
619 	}
620 
621 	if (!lua_istable(L, 1)) {
622 		luaL_error(L, "Incorrect arguments to SetSoundEffectParams()");
623 	}
624 
625 	//! first parse the 'preset' key (so all following params use it as base and override it)
626 	lua_pushliteral(L, "preset");
627 	lua_gettable(L, -2);
628 	if (lua_israwstring(L, -1)) {
629 		std::string presetname = lua_tostring(L, -1);
630 		efx->SetPreset(presetname, false, false);
631 	}
632 	lua_pop(L, 1);
633 
634 
635 	if (!efx->sfxProperties)
636 		return 0;
637 
638 	EAXSfxProps* efxprops = efx->sfxProperties;
639 
640 
641 	//! parse pass filter
642 	lua_pushliteral(L, "passfilter");
643 	lua_gettable(L, -2);
644 	if (lua_istable(L, -1)) {
645 		for (lua_pushnil(L); lua_next(L, -2) != 0; lua_pop(L, 1)) {
646 			if (!lua_israwstring(L, -2))
647 				continue;
648 
649 			const string key = StringToLower(lua_tostring(L, -2));
650 			std::map<std::string, ALuint>::iterator it = nameToALFilterParam.find(key);
651 
652 			if (it == nameToALFilterParam.end())
653 				continue;
654 
655 			ALuint param = it->second;
656 
657 			if (!lua_isnumber(L, -1))
658 				continue;
659 			if (alParamType[param] != EFXParamTypes::FLOAT)
660 				continue;
661 
662 			efxprops->filter_properties_f[param] = lua_tofloat(L, -1);
663 		}
664 	}
665 	lua_pop(L, 1);
666 
667 	//! parse EAX Reverb
668 	lua_pushliteral(L, "reverb");
669 	lua_gettable(L, -2);
670 	if (lua_istable(L, -1)) {
671 		for (lua_pushnil(L); lua_next(L, -2) != 0; lua_pop(L, 1)) {
672 			if (!lua_israwstring(L, -2))
673 				continue;
674 
675 			const string key = StringToLower(lua_tostring(L, -2));
676 			std::map<std::string, ALuint>::iterator it = nameToALParam.find(key);
677 
678 			if (it == nameToALParam.end())
679 				continue;
680 
681 			ALuint param = it->second;
682 			if (lua_istable(L, -1)) {
683 				if (alParamType[param] == EFXParamTypes::VECTOR) {
684 					float3 v;
685 
686 					if (LuaUtils::ParseFloatArray(L, -1, &v[0], 3) >= 3) {
687 						efxprops->properties_v[param] = v;
688 					}
689 				}
690 			}
691 			else if (lua_isnumber(L, -1)) {
692 				if (alParamType[param] == EFXParamTypes::FLOAT) {
693 					efxprops->properties_f[param] = lua_tofloat(L, -1);
694 				}
695 			}
696 			else if (lua_isboolean(L, -1)) {
697 				if (alParamType[param] == EFXParamTypes::BOOL) {
698 					efxprops->properties_i[param] = lua_toboolean(L, -1);
699 				}
700 			}
701 		}
702 	}
703 	lua_pop(L, 1);
704 
705 	//! commit effects
706 	efx->CommitEffects();
707 #endif /// !defined(HEADLESS) && !defined(NO_SOUND)
708 
709 	return 0;
710 }
711 
712 
713 /******************************************************************************/
714 /******************************************************************************/
715 
AddWorldIcon(lua_State * L)716 int LuaUnsyncedCtrl::AddWorldIcon(lua_State* L)
717 {
718 	const int cmdID = luaL_checkint(L, 1);
719 	const float3 pos(luaL_checkfloat(L, 2),
720 	                 luaL_checkfloat(L, 3),
721 	                 luaL_checkfloat(L, 4));
722 	cursorIcons.AddIcon(cmdID, pos);
723 	return 0;
724 }
725 
726 
AddWorldText(lua_State * L)727 int LuaUnsyncedCtrl::AddWorldText(lua_State* L)
728 {
729 	const string text = luaL_checksstring(L, 1);
730 	const float3 pos(luaL_checkfloat(L, 2),
731 	                 luaL_checkfloat(L, 3),
732 	                 luaL_checkfloat(L, 4));
733 	cursorIcons.AddIconText(text, pos);
734 	return 0;
735 }
736 
737 
AddWorldUnit(lua_State * L)738 int LuaUnsyncedCtrl::AddWorldUnit(lua_State* L)
739 {
740 	const int unitDefID = luaL_checkint(L, 1);
741 	if (!unitDefHandler->IsValidUnitDefID(unitDefID)) {
742 		return 0;
743 	}
744 	const float3 pos(luaL_checkfloat(L, 2),
745 	                 luaL_checkfloat(L, 3),
746 	                 luaL_checkfloat(L, 4));
747 	const int teamId = luaL_checkint(L, 5);
748 	if (!teamHandler->IsValidTeam(teamId)) {
749 		return 0;
750 	}
751 	const int facing = luaL_checkint(L, 6);
752 	cursorIcons.AddBuildIcon(-unitDefID, pos, teamId, facing);
753 	return 0;
754 }
755 
756 
DrawUnitCommands(lua_State * L)757 int LuaUnsyncedCtrl::DrawUnitCommands(lua_State* L)
758 {
759 	if (lua_istable(L, 1)) {
760 		const bool isMap = luaL_optboolean(L, 2, false);
761 		const int unitArg = isMap ? -2 : -1;
762 		const int table = 1;
763 		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
764 			if (lua_israwnumber(L, -2)) {
765 				CUnit* unit = ParseAllyUnit(L, __FUNCTION__, unitArg);
766 				if (unit != NULL) {
767 					drawCmdQueueUnits.insert(unit->id);
768 				}
769 			}
770 		}
771 		return 0;
772 	}
773 
774 	CUnit* unit = ParseAllyUnit(L, __FUNCTION__, 1);
775 	if (unit != NULL) {
776 		drawCmdQueueUnits.insert(unit->id);
777 	}
778 	return 0;
779 }
780 
781 
782 /******************************************************************************/
783 /******************************************************************************/
784 
SetCameraTarget(lua_State * L)785 int LuaUnsyncedCtrl::SetCameraTarget(lua_State* L)
786 {
787 	if (mouse == NULL) {
788 		return 0;
789 	}
790 
791 	const float3 pos(luaL_checkfloat(L, 1),
792 	                 luaL_checkfloat(L, 2),
793 	                 luaL_checkfloat(L, 3));
794 
795 	const float transTime = luaL_optfloat(L, 4, 0.5f);
796 
797 	camHandler->CameraTransition(transTime);
798 	camHandler->GetCurrentController().SetPos(pos);
799 
800 	return 0;
801 }
802 
803 
SetCameraState(lua_State * L)804 int LuaUnsyncedCtrl::SetCameraState(lua_State* L)
805 {
806 	if (mouse == NULL) {
807 		return 0;
808 	}
809 
810 	if (!lua_istable(L, 1)) {
811 		luaL_error(L, "Incorrect arguments to SetCameraState(table, camTime)");
812 	}
813 
814 	const float camTime = luaL_checkfloat(L, 2);
815 
816 	CCameraController::StateMap camState;
817 
818 	const int table = 1;
819 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
820 		if (lua_israwstring(L, -2)) {
821 			const string key = lua_tostring(L, -2);
822 			if (lua_isnumber(L, -1)) {
823 				camState[key] = lua_tofloat(L, -1);
824 			}
825 			else if (lua_isboolean(L, -1)) {
826 				camState[key] = lua_toboolean(L, -1) ? +1.0f : -1.0f;
827 			}
828 		}
829 	}
830 
831 	camHandler->CameraTransition(camTime);
832 	lua_pushboolean(L, camHandler->SetState(camState));
833 
834 	if (!CLuaHandle::GetHandleSynced(L)) {
835 		return 1;
836 	} else {
837 		return 0;
838 	}
839 }
840 
841 
842 /******************************************************************************/
843 /******************************************************************************/
844 
SelectUnitArray(lua_State * L)845 int LuaUnsyncedCtrl::SelectUnitArray(lua_State* L)
846 {
847 	if (!lua_istable(L, 1)) {
848 		luaL_error(L, "Incorrect arguments to SelectUnitArray()");
849 	}
850 
851 	// clear the current units, unless the append flag is present
852 	if (!luaL_optboolean(L, 2, false)) {
853 		selectedUnitsHandler.ClearSelected();
854 	}
855 
856 	const int table = 1;
857 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
858 		if (lua_israwnumber(L, -2) && lua_isnumber(L, -1)) {     // avoid 'n'
859 			CUnit* unit = ParseSelectUnit(L, __FUNCTION__, -1); // the value
860 			if (unit != NULL) {
861 				selectedUnitsHandler.AddUnit(unit);
862 			}
863 		}
864 	}
865 	return 0;
866 }
867 
868 
SelectUnitMap(lua_State * L)869 int LuaUnsyncedCtrl::SelectUnitMap(lua_State* L)
870 {
871 	if (!lua_istable(L, 1)) {
872 		luaL_error(L, "Incorrect arguments to SelectUnitMap()");
873 	}
874 
875 	// clear the current units, unless the append flag is present
876 	if (!luaL_optboolean(L, 2, false)) {
877 		selectedUnitsHandler.ClearSelected();
878 	}
879 
880 	const int table = 1;
881 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
882 		if (lua_israwnumber(L, -2)) {
883 			CUnit* unit = ParseSelectUnit(L, __FUNCTION__, -2); // the key
884 			if (unit != NULL) {
885 				selectedUnitsHandler.AddUnit(unit);
886 			}
887 		}
888 	}
889 
890 	return 0;
891 }
892 
893 
894 /******************************************************************************/
895 
SetTeamColor(lua_State * L)896 int LuaUnsyncedCtrl::SetTeamColor(lua_State* L)
897 {
898 	const int teamID = luaL_checkint(L, 1);
899 	if (!teamHandler->IsValidTeam(teamID)) {
900 		return 0;
901 	}
902 	CTeam* team = teamHandler->Team(teamID);
903 	if (team == NULL) {
904 		return 0;
905 	}
906 	const float r = max(0.0f, min(1.0f, luaL_checkfloat(L, 2)));
907 	const float g = max(0.0f, min(1.0f, luaL_checkfloat(L, 3)));
908 	const float b = max(0.0f, min(1.0f, luaL_checkfloat(L, 4)));
909 	team->color[0] = (unsigned char)(r * 255.0f);
910 	team->color[1] = (unsigned char)(g * 255.0f);
911 	team->color[2] = (unsigned char)(b * 255.0f);
912 	return 0;
913 }
914 
915 
916 /******************************************************************************/
917 
AssignMouseCursor(lua_State * L)918 int LuaUnsyncedCtrl::AssignMouseCursor(lua_State* L)
919 {
920 	const string cmdName  = luaL_checksstring(L, 1);
921 	const string fileName = luaL_checksstring(L, 2);
922 
923 	const bool overwrite = luaL_optboolean(L, 3, true);
924 
925 	CMouseCursor::HotSpot hotSpot = CMouseCursor::Center;
926 	if (luaL_optboolean(L, 4, false)) {
927 		hotSpot = CMouseCursor::TopLeft;
928 	}
929 
930 	const bool worked = mouse->AssignMouseCursor(cmdName, fileName, hotSpot, overwrite);
931 
932 	if (!CLuaHandle::GetHandleSynced(L)) {
933 		lua_pushboolean(L, worked);
934 		return 1;
935 	}
936 
937 	return 0;
938 }
939 
940 
ReplaceMouseCursor(lua_State * L)941 int LuaUnsyncedCtrl::ReplaceMouseCursor(lua_State* L)
942 {
943 	const string oldName = luaL_checksstring(L, 1);
944 	const string newName = luaL_checksstring(L, 2);
945 
946 	CMouseCursor::HotSpot hotSpot = CMouseCursor::Center;
947 	if (luaL_optboolean(L, 3, false)) {
948 		hotSpot = CMouseCursor::TopLeft;
949 	}
950 
951 	const bool worked = mouse->ReplaceMouseCursor(oldName, newName, hotSpot);
952 
953 	if (!CLuaHandle::GetHandleSynced(L)) {
954 		lua_pushboolean(L, worked);
955 		return 1;
956 	}
957 
958 	return 0;
959 }
960 
961 /******************************************************************************/
962 
SetCustomCommandDrawData(lua_State * L)963 int LuaUnsyncedCtrl::SetCustomCommandDrawData(lua_State* L)
964 {
965 	const int cmdID = luaL_checkint(L, 1);
966 
967 	int iconID = 0;
968 	if (lua_israwnumber(L, 2)) {
969 		iconID = lua_toint(L, 2);
970 	}
971 	else if (lua_israwstring(L, 2)) {
972 		iconID = cmdID;
973 		const string icon = lua_tostring(L, 2);
974 		cursorIcons.SetCustomType(cmdID, icon);
975 	}
976 	else if (lua_isnoneornil(L, 2)) {
977 		cursorIcons.SetCustomType(cmdID, "");
978 		cmdColors.ClearCustomCmdData(cmdID);
979 		return 0;
980 	}
981 	else {
982 		luaL_error(L, "Incorrect arguments to SetCustomCommandDrawData");
983 	}
984 
985 	float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
986 	/*const int size =*/ LuaUtils::ParseFloatArray(L, 3, color, 4);
987 
988 	const bool showArea = luaL_optboolean(L, 4, false);
989 
990 	cmdColors.SetCustomCmdData(cmdID, iconID, color, showArea);
991 
992 	return 0;
993 }
994 
995 
996 /******************************************************************************/
997 /******************************************************************************/
998 
SetDrawSky(lua_State * L)999 int LuaUnsyncedCtrl::SetDrawSky(lua_State* L)
1000 {
1001 	if (game == NULL) {
1002 		return 0;
1003 	}
1004 	globalRendering->drawSky = !!luaL_checkboolean(L, 1);
1005 	return 0;
1006 }
1007 
1008 
SetDrawWater(lua_State * L)1009 int LuaUnsyncedCtrl::SetDrawWater(lua_State* L)
1010 {
1011 	if (game == NULL) {
1012 		return 0;
1013 	}
1014 	globalRendering->drawWater = !!luaL_checkboolean(L, 1);
1015 	return 0;
1016 }
1017 
1018 
SetDrawGround(lua_State * L)1019 int LuaUnsyncedCtrl::SetDrawGround(lua_State* L)
1020 {
1021 	if (game == NULL) {
1022 		return 0;
1023 	}
1024 	globalRendering->drawGround = !!luaL_checkboolean(L, 1);
1025 	return 0;
1026 }
1027 
1028 
SetDrawGroundDeferred(lua_State * L)1029 int LuaUnsyncedCtrl::SetDrawGroundDeferred(lua_State* L)
1030 {
1031 	readMap->GetGroundDrawer()->SetDrawDeferredPass(luaL_checkboolean(L, 1));
1032 	lua_pushboolean(L, readMap->GetGroundDrawer()->DrawDeferred());
1033 	return 1;
1034 }
1035 
SetDrawModelsDeferred(lua_State * L)1036 int LuaUnsyncedCtrl::SetDrawModelsDeferred(lua_State* L)
1037 {
1038 	unitDrawer->SetDrawDeferredPass(luaL_checkboolean(L, 1));
1039 	lua_pushboolean(L, unitDrawer->DrawDeferred());
1040 	return 1;
1041 }
1042 
1043 
1044 /******************************************************************************/
1045 
SetWaterParams(lua_State * L)1046 int LuaUnsyncedCtrl::SetWaterParams(lua_State* L)
1047 {
1048 	if (game == NULL) {
1049 		return 0;
1050 	}
1051 	if (!gs->cheatEnabled) {
1052 		LOG("SetWaterParams() needs cheating enabled");
1053 		return 0;
1054 	}
1055 	if (!lua_istable(L, 1)) {
1056 		luaL_error(L, "Incorrect arguments to SetWaterParams()");
1057 	}
1058 
1059 	CMapInfo::water_t& w = const_cast<CMapInfo*>(mapInfo)->water;
1060 	for (lua_pushnil(L); lua_next(L, 1) != 0; lua_pop(L, 1)) {
1061 		if (lua_israwstring(L, -2)) {
1062 			const string key = lua_tostring(L, -2);
1063 			if (lua_istable(L, -1)) {
1064 				float color[3];
1065 				const int size = LuaUtils::ParseFloatArray(L, -1, color, 3);
1066 				if (size >= 3) {
1067 					if (key == "absorb") {
1068 						w.absorb = color;
1069 					} else if (key == "baseColor") {
1070 						w.baseColor = color;
1071 					} else if (key == "minColor") {
1072 						w.minColor = color;
1073 					} else if (key == "surfaceColor") {
1074 						w.surfaceColor = color;
1075 					} else if (key == "diffuseColor") {
1076 						w.diffuseColor = color;
1077 					} else if (key == "specularColor") {
1078 						w.specularColor = color;
1079  					} else if (key == "planeColor") {
1080 						w.planeColor.x = color[0];
1081 						w.planeColor.y = color[1];
1082 						w.planeColor.z = color[2];
1083 					}
1084 				}
1085 			}
1086 			else if (lua_israwstring(L, -1)) {
1087 				const std::string value = lua_tostring(L, -1);
1088 				if (key == "texture") {
1089 					w.texture = value;
1090 				} else if (key == "foamTexture") {
1091 					w.foamTexture = value;
1092 				} else if (key == "normalTexture") {
1093 					w.normalTexture = value;
1094 				}
1095 			}
1096 			else if (lua_isnumber(L, -1)) {
1097 				const float value = lua_tofloat(L, -1);
1098 				if (key == "damage") {
1099 					w.damage = value;
1100 				} else if (key == "repeatX") {
1101 					w.repeatX = value;
1102 				} else if (key == "repeatY") {
1103 					w.repeatY = value;
1104 				} else if (key == "surfaceAlpha") {
1105 					w.surfaceAlpha = value;
1106 				} else if (key == "ambientFactor") {
1107 					w.ambientFactor = value;
1108 				} else if (key == "diffuseFactor") {
1109 					w.diffuseFactor = value;
1110 				} else if (key == "specularFactor") {
1111 					w.specularFactor = value;
1112 				} else if (key == "specularPower") {
1113 					w.specularPower = value;
1114 				} else if (key == "fresnelMin") {
1115 					w.fresnelMin = value;
1116 				} else if (key == "fresnelMax") {
1117 					w.fresnelMax = value;
1118 				} else if (key == "fresnelPower") {
1119 					w.fresnelPower = value;
1120 				} else if (key == "reflectionDistortion") {
1121 					w.reflDistortion = value;
1122 				} else if (key == "blurBase") {
1123 					w.blurBase = value;
1124 				} else if (key == "blurExponent") {
1125 					w.blurExponent = value;
1126 				} else if (key == "perlinStartFreq") {
1127 					w.perlinStartFreq = value;
1128 				} else if (key == "perlinLacunarity") {
1129 					w.perlinLacunarity = value;
1130 				} else if (key == "perlinAmplitude") {
1131 					w.perlinAmplitude = value;
1132 				} else if (key == "numTiles") {
1133 					w.numTiles = (unsigned char)value;
1134 				}
1135 			}
1136 			else if (lua_isboolean(L, -1)) {
1137 				const bool value = lua_toboolean(L, -1);
1138 				if (key == "shoreWaves") {
1139 					w.shoreWaves = value;
1140 				} else if (key == "forceRendering") {
1141 					w.forceRendering = value;
1142 				} else if (key == "hasWaterPlane") {
1143 					w.hasWaterPlane = value;
1144 				}
1145 			}
1146 		}
1147 	}
1148 
1149 	return 0;
1150 }
1151 
1152 
1153 
ParseLight(lua_State * L,GL::Light & light,const int tblIdx,const char * caller)1154 static bool ParseLight(lua_State* L, GL::Light& light, const int tblIdx, const char* caller)
1155 {
1156 	if (!lua_istable(L, tblIdx)) {
1157 		luaL_error(L, "[%s] argument %c must be a table!", caller, tblIdx);
1158 		return false;
1159 	}
1160 
1161 	for (lua_pushnil(L); lua_next(L, tblIdx) != 0; lua_pop(L, 1)) {
1162 		if (lua_israwstring(L, -2)) {
1163 			const std::string& key = lua_tostring(L, -2);
1164 
1165 			if (lua_istable(L, -1)) {
1166 				float array[3] = {0.0f, 0.0f, 0.0f};
1167 
1168 				if (LuaUtils::ParseFloatArray(L, -1, array, 3) == 3) {
1169 					if (key == "position") {
1170 						light.SetPosition(array);
1171 					} else if (key == "direction") {
1172 						light.SetDirection(array);
1173 					} else if (key == "ambientColor") {
1174 						light.SetAmbientColor(array);
1175 					} else if (key == "diffuseColor") {
1176 						light.SetDiffuseColor(array);
1177 					} else if (key == "specularColor") {
1178 						light.SetSpecularColor(array);
1179 					} else if (key == "intensityWeight") {
1180 						light.SetIntensityWeight(array);
1181 					} else if (key == "attenuation") {
1182 						light.SetAttenuation(array);
1183 					} else if (key == "ambientDecayRate") {
1184 						light.SetAmbientDecayRate(array);
1185 					} else if (key == "diffuseDecayRate") {
1186 						light.SetDiffuseDecayRate(array);
1187 					} else if (key == "specularDecayRate") {
1188 						light.SetSpecularDecayRate(array);
1189 					} else if (key == "decayFunctionType") {
1190 						light.SetDecayFunctionType(array);
1191 					}
1192 				}
1193 
1194 				continue;
1195 			}
1196 
1197 			if (lua_isnumber(L, -1)) {
1198 				if (key == "radius") {
1199 					light.SetRadius(std::max(1.0f, lua_tofloat(L, -1)));
1200 				} else if (key == "fov") {
1201 					light.SetFOV(std::max(0.0f, std::min(180.0f, lua_tofloat(L, -1))));
1202 				} else if (key == "ttl") {
1203 					light.SetTTL(lua_tofloat(L, -1));
1204 				} else if (key == "priority") {
1205 					light.SetPriority(lua_tofloat(L, -1));
1206 				} else if (key == "ignoreLOS") {
1207 					light.SetIgnoreLOS(lua_toboolean(L, -1));
1208 				}
1209 
1210 				continue;
1211 			}
1212 		}
1213 	}
1214 
1215 	return true;
1216 }
1217 
1218 
AddMapLight(lua_State * L)1219 int LuaUnsyncedCtrl::AddMapLight(lua_State* L)
1220 {
1221 	if (CLuaHandle::GetHandleSynced(L) || !CLuaHandle::GetHandleFullRead(L)) {
1222 		return 0;
1223 	}
1224 
1225 	GL::LightHandler* lightHandler = readMap->GetGroundDrawer()->GetLightHandler();
1226 	GL::Light light;
1227 
1228 	unsigned int lightHandle = -1U;
1229 
1230 	if (lightHandler != NULL) {
1231 		if (ParseLight(L, light, 1, __FUNCTION__)) {
1232 			lightHandle = lightHandler->AddLight(light);
1233 		}
1234 	}
1235 
1236 	lua_pushnumber(L, lightHandle);
1237 	return 1;
1238 }
1239 
AddModelLight(lua_State * L)1240 int LuaUnsyncedCtrl::AddModelLight(lua_State* L)
1241 {
1242 	if (CLuaHandle::GetHandleSynced(L) || !CLuaHandle::GetHandleFullRead(L)) {
1243 		return 0;
1244 	}
1245 
1246 	GL::LightHandler* lightHandler = unitDrawer->GetLightHandler();
1247 	GL::Light light;
1248 
1249 	unsigned int lightHandle = -1U;
1250 
1251 	if (lightHandler != NULL) {
1252 		if (ParseLight(L, light, 1, __FUNCTION__)) {
1253 			lightHandle = lightHandler->AddLight(light);
1254 		}
1255 	}
1256 
1257 	lua_pushnumber(L, lightHandle);
1258 	return 1;
1259 }
1260 
1261 
UpdateMapLight(lua_State * L)1262 int LuaUnsyncedCtrl::UpdateMapLight(lua_State* L)
1263 {
1264 	const unsigned int lightHandle = luaL_checkint(L, 1);
1265 
1266 	if (CLuaHandle::GetHandleSynced(L) || !CLuaHandle::GetHandleFullRead(L)) {
1267 		return 0;
1268 	}
1269 
1270 	GL::LightHandler* lightHandler = readMap->GetGroundDrawer()->GetLightHandler();
1271 	GL::Light* light = (lightHandler != NULL)? lightHandler->GetLight(lightHandle): NULL;
1272 
1273 	bool ret = false;
1274 
1275 	if (light != NULL) {
1276 		ret = ParseLight(L, *light, 2, __FUNCTION__);
1277 	}
1278 
1279 	lua_pushboolean(L, ret);
1280 	return 1;
1281 }
1282 
UpdateModelLight(lua_State * L)1283 int LuaUnsyncedCtrl::UpdateModelLight(lua_State* L)
1284 {
1285 	const unsigned int lightHandle = luaL_checkint(L, 1);
1286 
1287 	if (CLuaHandle::GetHandleSynced(L) || !CLuaHandle::GetHandleFullRead(L)) {
1288 		return 0;
1289 	}
1290 
1291 	GL::LightHandler* lightHandler = unitDrawer->GetLightHandler();
1292 	GL::Light* light = (lightHandler != NULL)? lightHandler->GetLight(lightHandle): NULL;
1293 	bool ret = false;
1294 
1295 	if (light != NULL) {
1296 		ret = ParseLight(L, *light, 2, __FUNCTION__);
1297 	}
1298 
1299 	lua_pushboolean(L, ret);
1300 	return 1;
1301 }
1302 
1303 
AddLightTrackingTarget(lua_State * L,GL::Light * light,bool trackEnable,bool trackUnit,const char * caller)1304 static bool AddLightTrackingTarget(lua_State* L, GL::Light* light, bool trackEnable, bool trackUnit, const char* caller)
1305 {
1306 	bool ret = false;
1307 
1308 	if (trackUnit) {
1309 		// interpret argument #2 as a unit ID
1310 		CUnit* unit = ParseAllyUnit(L, caller, 2);
1311 
1312 		if (unit != NULL) {
1313 			if (trackEnable) {
1314 				if (light->GetTrackPosition() == NULL) {
1315 					light->AddDeathDependence(unit, DEPENDENCE_LIGHT);
1316 					light->SetTrackPosition(&unit->drawPos);
1317 					light->SetTrackDirection(&unit->speed); //! non-normalized
1318 					ret = true;
1319 				}
1320 			} else {
1321 				// assume <light> was tracking <unit>
1322 				if (light->GetTrackPosition() == &unit->drawPos) {
1323 					light->DeleteDeathDependence(unit, DEPENDENCE_LIGHT);
1324 					light->SetTrackPosition(NULL);
1325 					light->SetTrackDirection(NULL);
1326 					ret = true;
1327 				}
1328 			}
1329 		}
1330 	} else {
1331 		// interpret argument #2 as a projectile ID
1332 		//
1333 		// only track synced projectiles (LuaSynced
1334 		// does not know about unsynced ID's anyway)
1335 		CProjectile* proj = ParseRawProjectile(L, caller, 2, true);
1336 
1337 		if (proj != NULL) {
1338 			if (trackEnable) {
1339 				if (light->GetTrackPosition() == NULL) {
1340 					light->AddDeathDependence(proj, DEPENDENCE_LIGHT);
1341 					light->SetTrackPosition(&proj->drawPos);
1342 					light->SetTrackDirection(&proj->dir);
1343 					ret = true;
1344 				}
1345 			} else {
1346 				// assume <light> was tracking <proj>
1347 				if (light->GetTrackPosition() == &proj->drawPos) {
1348 					light->DeleteDeathDependence(proj, DEPENDENCE_LIGHT);
1349 					light->SetTrackPosition(NULL);
1350 					light->SetTrackDirection(NULL);
1351 					ret = true;
1352 				}
1353 			}
1354 		}
1355 	}
1356 
1357 	return ret;
1358 }
1359 
1360 // set a map-illuminating light to start/stop tracking
1361 // the position of a moving object (unit or projectile)
SetMapLightTrackingState(lua_State * L)1362 int LuaUnsyncedCtrl::SetMapLightTrackingState(lua_State* L)
1363 {
1364 	if (CLuaHandle::GetHandleSynced(L) || !CLuaHandle::GetHandleFullRead(L)) {
1365 		return 0;
1366 	}
1367 	if (!lua_isnumber(L, 2)) {
1368 		luaL_error(L, "[%s] 1st and 2nd arguments should be numbers, 3rd and 4th should be booleans", __FUNCTION__);
1369 		return 0;
1370 	}
1371 
1372 	const unsigned int lightHandle = luaL_checkint(L, 1);
1373 	const bool trackEnable = luaL_optboolean(L, 3, true);
1374 	const bool trackUnit = luaL_optboolean(L, 4, true);
1375 
1376 	GL::LightHandler* lightHandler = readMap->GetGroundDrawer()->GetLightHandler();
1377 	GL::Light* light = (lightHandler != NULL)? lightHandler->GetLight(lightHandle): NULL;
1378 
1379 	bool ret = false;
1380 
1381 	if (light != NULL) {
1382 		ret = AddLightTrackingTarget(L, light, trackEnable, trackUnit, __FUNCTION__);
1383 	}
1384 
1385 	lua_pushboolean(L, ret);
1386 	return 1;
1387 }
1388 
1389 // set a model-illuminating light to start/stop tracking
1390 // the position of a moving object (unit or projectile)
SetModelLightTrackingState(lua_State * L)1391 int LuaUnsyncedCtrl::SetModelLightTrackingState(lua_State* L)
1392 {
1393 	if (CLuaHandle::GetHandleSynced(L) || !CLuaHandle::GetHandleFullRead(L)) {
1394 		return 0;
1395 	}
1396 	if (!lua_isnumber(L, 2)) {
1397 		luaL_error(L, "[%s] 1st and 2nd arguments should be numbers, 3rd and 4th should be booleans", __FUNCTION__);
1398 		return 0;
1399 	}
1400 
1401 	const unsigned int lightHandle = luaL_checkint(L, 1);
1402 	const bool trackEnable = luaL_optboolean(L, 3, true);
1403 	const bool trackUnit = luaL_optboolean(L, 4, true);
1404 
1405 	GL::LightHandler* lightHandler = unitDrawer->GetLightHandler();
1406 	GL::Light* light = (lightHandler != NULL)? lightHandler->GetLight(lightHandle): NULL;
1407 	bool ret = false;
1408 
1409 	if (light != NULL) {
1410 		ret = AddLightTrackingTarget(L, light, trackEnable, trackUnit, __FUNCTION__);
1411 	}
1412 
1413 	lua_pushboolean(L, ret);
1414 	return 1;
1415 }
1416 
1417 
1418 /******************************************************************************/
1419 
SetMapSquareTexture(lua_State * L)1420 int LuaUnsyncedCtrl::SetMapSquareTexture(lua_State* L)
1421 {
1422 	if (CLuaHandle::GetHandleSynced(L)) {
1423 		return 0;
1424 	}
1425 
1426 	const int texSquareX = luaL_checkint(L, 1);
1427 	const int texSquareY = luaL_checkint(L, 2);
1428 	const std::string& texName = luaL_checkstring(L, 3);
1429 
1430 	CBaseGroundDrawer* groundDrawer = readMap->GetGroundDrawer();
1431 	CBaseGroundTextures* groundTextures = groundDrawer->GetGroundTextures();
1432 
1433 	if (groundTextures == NULL) {
1434 		lua_pushboolean(L, false);
1435 		return 1;
1436 	}
1437 	if (texName.empty()) {
1438 		// restore default texture for this square
1439 		lua_pushboolean(L, groundTextures->SetSquareLuaTexture(texSquareX, texSquareY, 0));
1440 		return 1;
1441 	}
1442 
1443 	// TODO: leaking ID's like this means we need to guard against texture deletion
1444 	const LuaTextures& luaTextures = CLuaHandle::GetActiveTextures(L);
1445 	const LuaTextures::Texture* luaTexture = luaTextures.GetInfo(texName);
1446 	const CNamedTextures::TexInfo* namedTexture = CNamedTextures::GetInfo(texName);
1447 
1448 	if (luaTexture != NULL) {
1449 		if (luaTexture->xsize != luaTexture->ysize) {
1450 			// square textures only
1451 			lua_pushboolean(L, false);
1452 			return 1;
1453 		}
1454 
1455 		lua_pushboolean(L, groundTextures->SetSquareLuaTexture(texSquareX, texSquareY, luaTexture->id));
1456 		return 1;
1457 	}
1458 
1459 	if (namedTexture != NULL) {
1460 		if (namedTexture->xsize != namedTexture->ysize) {
1461 			// square textures only
1462 			lua_pushboolean(L, false);
1463 			return 1;
1464 		}
1465 
1466 		lua_pushboolean(L, groundTextures->SetSquareLuaTexture(texSquareX, texSquareY, namedTexture->id));
1467 		return 1;
1468 	}
1469 
1470 	lua_pushboolean(L, false);
1471 	return 1;
1472 }
1473 
1474 /******************************************************************************/
1475 
SetUnitNoDraw(lua_State * L)1476 int LuaUnsyncedCtrl::SetUnitNoDraw(lua_State* L)
1477 {
1478 	if (CLuaHandle::GetHandleUserMode(L)) {
1479 		return 0;
1480 	}
1481 	CUnit* unit = ParseCtrlUnit(L, __FUNCTION__, 1);
1482 	if (unit == NULL) {
1483 		return 0;
1484 	}
1485 	unit->noDraw = luaL_checkboolean(L, 2);
1486 	return 0;
1487 }
1488 
1489 
SetUnitNoMinimap(lua_State * L)1490 int LuaUnsyncedCtrl::SetUnitNoMinimap(lua_State* L)
1491 {
1492 	if (CLuaHandle::GetHandleUserMode(L)) {
1493 		return 0;
1494 	}
1495 	CUnit* unit = ParseCtrlUnit(L, __FUNCTION__, 1);
1496 	if (unit == NULL) {
1497 		return 0;
1498 	}
1499 	unit->noMinimap = luaL_checkboolean(L, 2);
1500 	return 0;
1501 }
1502 
1503 
SetUnitNoSelect(lua_State * L)1504 int LuaUnsyncedCtrl::SetUnitNoSelect(lua_State* L)
1505 {
1506 	if (CLuaHandle::GetHandleUserMode(L)) {
1507 		return 0;
1508 	}
1509 	CUnit* unit = ParseCtrlUnit(L, __FUNCTION__, 1);
1510 	if (unit == NULL) {
1511 		return 0;
1512 	}
1513 	unit->noSelect = luaL_checkboolean(L, 2);
1514 
1515 	// deselect the unit if it's selected and shouldn't be
1516 	if (unit->noSelect) {
1517 		const CUnitSet& selUnits = selectedUnitsHandler.selectedUnits;
1518 		if (selUnits.find(unit) != selUnits.end()) {
1519 			selectedUnitsHandler.RemoveUnit(unit);
1520 		}
1521 	}
1522 	return 0;
1523 }
1524 
1525 
SetUnitLeaveTracks(lua_State * L)1526 int LuaUnsyncedCtrl::SetUnitLeaveTracks(lua_State* L)
1527 {
1528 	CUnit* unit = ParseCtrlUnit(L, __FUNCTION__, 1);
1529 	if (unit == NULL) {
1530 		return 0;
1531 	}
1532 
1533 	unit->leaveTracks = lua_toboolean(L, 2);
1534 	return 0;
1535 }
1536 
1537 
1538 /******************************************************************************/
1539 
AddUnitIcon(lua_State * L)1540 int LuaUnsyncedCtrl::AddUnitIcon(lua_State* L)
1541 {
1542 	if (CLuaHandle::GetHandleSynced(L)) {
1543 		return 0;
1544 	}
1545 	const string iconName  = luaL_checkstring(L, 1);
1546 	const string texName   = luaL_checkstring(L, 2);
1547 	const float  size      = luaL_optnumber(L, 3, 1.0f);
1548 	const float  dist      = luaL_optnumber(L, 4, 1.0f);
1549 	const bool   radAdjust = luaL_optboolean(L, 5, false);
1550 	lua_pushboolean(L, icon::iconHandler->AddIcon(iconName, texName,
1551 	                                        size, dist, radAdjust));
1552 	return 1;
1553 }
1554 
1555 
FreeUnitIcon(lua_State * L)1556 int LuaUnsyncedCtrl::FreeUnitIcon(lua_State* L)
1557 {
1558 	if (CLuaHandle::GetHandleSynced(L)) {
1559 		return 0;
1560 	}
1561 	const string iconName  = luaL_checkstring(L, 1);
1562 	lua_pushboolean(L, icon::iconHandler->FreeIcon(iconName));
1563 	return 1;
1564 }
1565 
1566 
1567 /******************************************************************************/
1568 
1569 // TODO: move this to LuaVFS?
ExtractModArchiveFile(lua_State * L)1570 int LuaUnsyncedCtrl::ExtractModArchiveFile(lua_State* L)
1571 {
1572 	if (!CLuaHandle::CheckModUICtrl(L)) {
1573 		return 0;
1574 	}
1575 
1576 	const string path = luaL_checkstring(L, 1);
1577 
1578 	CFileHandler fhVFS(path, SPRING_VFS_MOD);
1579 	CFileHandler fhRAW(path, SPRING_VFS_RAW);
1580 
1581 	if (!fhVFS.FileExists()) {
1582 		luaL_error(L, "file \"%s\" not found in mod archive", path.c_str());
1583 		return 0;
1584 	}
1585 
1586 	if (fhRAW.FileExists()) {
1587 		luaL_error(L, "cannot extract file \"%s\": already exists", path.c_str());
1588 		return 0;
1589 	}
1590 
1591 
1592 	string dname = FileSystem::GetDirectory(path);
1593 	string fname = FileSystem::GetFilename(path);
1594 
1595 #ifdef WIN32
1596 	const size_t s = dname.size();
1597 	// get rid of any trailing slashes (CreateDirectory()
1598 	// fails on at least XP and Vista if they are present,
1599 	// ie. it creates the dir but actually returns false)
1600 	if ((s > 0) && ((dname[s - 1] == '/') || (dname[s - 1] == '\\'))) {
1601 		dname = dname.substr(0, s - 1);
1602 	}
1603 #endif
1604 
1605 	if (!dname.empty() && !FileSystem::CreateDirectory(dname)) {
1606 		luaL_error(L, "Could not create directory \"%s\" for file \"%s\"",
1607 		           dname.c_str(), fname.c_str());
1608 	}
1609 
1610 	const int numBytes = fhVFS.FileSize();
1611 	char* buffer = new char[numBytes];
1612 
1613 	fhVFS.Read(buffer, numBytes);
1614 
1615 	std::fstream fstr(path.c_str(), std::ios::out | std::ios::binary);
1616 	fstr.write((const char*) buffer, numBytes);
1617 	fstr.close();
1618 
1619 	if (!dname.empty()) {
1620 		LOG("Extracted file \"%s\" to directory \"%s\"",
1621 				fname.c_str(), dname.c_str());
1622 	} else {
1623 		LOG("Extracted file \"%s\"", fname.c_str());
1624 	}
1625 
1626 	delete[] buffer;
1627 
1628 	lua_pushboolean(L, true);
1629 	return 1;
1630 }
1631 
1632 
1633 /******************************************************************************/
1634 /******************************************************************************/
1635 //
1636 // moved from LuaUI
1637 //
1638 /******************************************************************************/
1639 /******************************************************************************/
1640 
1641 
SendCommands(lua_State * L)1642 int LuaUnsyncedCtrl::SendCommands(lua_State* L)
1643 {
1644 	if (!CLuaHandle::CheckModUICtrl(L)) {
1645 		return 0;
1646 	}
1647 	if ((guihandler == NULL) || gs->noHelperAIs) {
1648 		return 0;
1649 	}
1650 
1651 	vector<string> cmds;
1652 
1653 	if (lua_istable(L, 1)) { // old style -- table
1654 		const int table = 1;
1655 		for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
1656 			if (lua_israwstring(L, -1)) {
1657 				string action = lua_tostring(L, -1);
1658 				if (action[0] != '@') {
1659 					action = "@@" + action;
1660 				}
1661 				cmds.push_back(action);
1662 			}
1663 		}
1664 	}
1665 	else if (lua_israwstring(L, 1)) { // new style -- function parameters
1666 		for (int i = 1; lua_israwstring(L, i); i++) {
1667 			string action = lua_tostring(L, i);
1668 			if (action[0] != '@') {
1669 				action = "@@" + action;
1670 			}
1671 			cmds.push_back(action);
1672 		}
1673 	}
1674 	else {
1675 		luaL_error(L, "Incorrect arguments to SendCommands()");
1676 	}
1677 
1678 	lua_settop(L, 0); // pop the input arguments
1679 
1680 	configHandler->EnableWriting(globalConfig->luaWritableConfigFile);
1681 	guihandler->RunCustomCommands(cmds, false);
1682 	configHandler->EnableWriting(true);
1683 
1684 	return 0;
1685 }
1686 
1687 
1688 /******************************************************************************/
1689 
SetActiveCommandByIndex(lua_State * L)1690 static int SetActiveCommandByIndex(lua_State* L)
1691 {
1692 	if (!CLuaHandle::CheckModUICtrl(L)) {
1693 		return 0;
1694 	}
1695 	if (guihandler == NULL) {
1696 		return 0;
1697 	}
1698 	const int args = lua_gettop(L); // number of arguments
1699 	const int cmdIndex = lua_toint(L, 1) - CMD_INDEX_OFFSET;
1700 	int button = luaL_optint(L, 2, 1); // LMB
1701 
1702 	if (args <= 2) {
1703 		const bool rmb = (button == SDL_BUTTON_LEFT) ? false : true;
1704 		const bool success = guihandler->SetActiveCommand(cmdIndex, rmb);
1705 		lua_pushboolean(L, success);
1706 		return 1;
1707 	}
1708 
1709 	const bool lmb   = luaL_checkboolean(L, 3);
1710 	const bool rmb   = luaL_checkboolean(L, 4);
1711 	const bool alt   = luaL_checkboolean(L, 5);
1712 	const bool ctrl  = luaL_checkboolean(L, 6);
1713 	const bool meta  = luaL_checkboolean(L, 7);
1714 	const bool shift = luaL_checkboolean(L, 8);
1715 
1716 	const bool success = guihandler->SetActiveCommand(cmdIndex, button, lmb, rmb,
1717 	                                                  alt, ctrl, meta, shift);
1718 	lua_pushboolean(L, success);
1719 	return 1;
1720 }
1721 
1722 
SetActiveCommandByAction(lua_State * L)1723 static int SetActiveCommandByAction(lua_State* L)
1724 {
1725 	if (!CLuaHandle::CheckModUICtrl(L)) {
1726 		return 0;
1727 	}
1728 	if (guihandler == NULL) {
1729 		return 0;
1730 	}
1731 	const int args = lua_gettop(L); // number of arguments
1732 	const string text = lua_tostring(L, 1);
1733 	const Action action(text);
1734 	CKeySet ks;
1735 	if (args >= 2) {
1736 		const string ksText = lua_tostring(L, 2);
1737 		ks.Parse(ksText);
1738 	}
1739 	const bool success = guihandler->SetActiveCommand(action, ks, 0);
1740 	lua_pushboolean(L, success);
1741 	return 1;
1742 }
1743 
1744 
SetActiveCommand(lua_State * L)1745 int LuaUnsyncedCtrl::SetActiveCommand(lua_State* L)
1746 {
1747 	if (!CLuaHandle::CheckModUICtrl(L)) {
1748 		return 0;
1749 	}
1750 	if (guihandler == NULL) {
1751 		return 0;
1752 	}
1753 	const int args = lua_gettop(L); // number of arguments
1754 	if (args < 1) {
1755 		luaL_error(L, "Incorrect arguments to SetActiveCommand()");
1756 	}
1757 	if (lua_isnumber(L, 1)) {
1758 		return SetActiveCommandByIndex(L);
1759 	}
1760 	if (lua_isstring(L, 1)) {
1761 		return SetActiveCommandByAction(L);
1762 	}
1763 	luaL_error(L, "Incorrect arguments to SetActiveCommand()");
1764 	return 0;
1765 }
1766 
1767 
LoadCmdColorsConfig(lua_State * L)1768 int LuaUnsyncedCtrl::LoadCmdColorsConfig(lua_State* L)
1769 {
1770 	if (!CLuaHandle::CheckModUICtrl(L)) {
1771 		return 0;
1772 	}
1773 	const string cfg = luaL_checkstring(L, 1);
1774 	cmdColors.LoadConfigFromString(cfg);
1775 	return 0;
1776 }
1777 
1778 
LoadCtrlPanelConfig(lua_State * L)1779 int LuaUnsyncedCtrl::LoadCtrlPanelConfig(lua_State* L)
1780 {
1781 	if (!CLuaHandle::CheckModUICtrl(L)) {
1782 		return 0;
1783 	}
1784 	if (guihandler == NULL) {
1785 		return 0;
1786 	}
1787 	const string cfg = luaL_checkstring(L, 1);
1788 	guihandler->ReloadConfigFromString(cfg);
1789 	return 0;
1790 }
1791 
1792 
ForceLayoutUpdate(lua_State * L)1793 int LuaUnsyncedCtrl::ForceLayoutUpdate(lua_State* L)
1794 {
1795 	if (!CLuaHandle::CheckModUICtrl(L)) {
1796 		return 0;
1797 	}
1798 	if (guihandler == NULL) {
1799 		return 0;
1800 	}
1801 	guihandler->ForceLayoutUpdate();
1802 	return 0;
1803 }
1804 
1805 
1806 /******************************************************************************/
1807 
WarpMouse(lua_State * L)1808 int LuaUnsyncedCtrl::WarpMouse(lua_State* L)
1809 {
1810 	if (!CLuaHandle::CheckModUICtrl(L)) {
1811 		return 0;
1812 	}
1813 	const int x = luaL_checkint(L, 1);
1814 	const int y = globalRendering->viewSizeY - luaL_checkint(L, 2) - 1;
1815 	mouse->WarpMouse(x, y);
1816 	return 0;
1817 }
1818 
1819 
SetMouseCursor(lua_State * L)1820 int LuaUnsyncedCtrl::SetMouseCursor(lua_State* L)
1821 {
1822 	if (!CLuaHandle::CheckModUICtrl(L)) {
1823 		return 0;
1824 	}
1825 
1826 	const std::string& cursorName = luaL_checkstring(L, 1);
1827 	const float cursorScale = luaL_optfloat(L, 2, 1.0f);
1828 
1829 	mouse->ChangeCursor(cursorName, cursorScale);
1830 
1831 	return 0;
1832 }
1833 
1834 /******************************************************************************/
1835 
SetClipboard(lua_State * L)1836 int LuaUnsyncedCtrl::SetClipboard(lua_State* L)
1837 {
1838 	if (!CLuaHandle::CheckModUICtrl(L)) {
1839 		return 0;
1840 	}
1841 
1842 	SDL_SetClipboardText(luaL_checkstring(L, 1));
1843 	return 0;
1844 }
1845 
1846 /******************************************************************************/
1847 
SetCameraOffset(lua_State * L)1848 int LuaUnsyncedCtrl::SetCameraOffset(lua_State* L)
1849 {
1850 	if (!CLuaHandle::CheckModUICtrl(L)) {
1851 		return 0;
1852 	}
1853 	if (camera == NULL) {
1854 		return 0;
1855 	}
1856 
1857 	camera->posOffset.x = luaL_optfloat(L, 1, 0.0f);
1858 	camera->posOffset.y = luaL_optfloat(L, 2, 0.0f);
1859 	camera->posOffset.z = luaL_optfloat(L, 3, 0.0f);
1860 	camera->tiltOffset.x = luaL_optfloat(L, 4, 0.0f);
1861 	camera->tiltOffset.y = luaL_optfloat(L, 5, 0.0f);
1862 	camera->tiltOffset.z = luaL_optfloat(L, 6, 0.0f);
1863 
1864 	return 0;
1865 }
1866 
1867 
1868 /******************************************************************************/
1869 
UpdateInfoTexture(lua_State * L)1870 int LuaUnsyncedCtrl::UpdateInfoTexture(lua_State* L)
1871 {
1872 	const int rawMode = CBaseGroundDrawer::BaseGroundDrawMode(luaL_checkint(L, 1));
1873 	const int texMode = Clamp(rawMode, int(CBaseGroundDrawer::drawLos), int(CBaseGroundDrawer::drawPathCost));
1874 
1875 	CBaseGroundDrawer* gd = readMap->GetGroundDrawer();
1876 
1877 	if (gd->GetDrawMode() == CBaseGroundDrawer::drawNormal) {
1878 		// allow background updates only when no infotex is active
1879 		lua_pushboolean(L, gd->UpdateExtraTexture(texMode));
1880 	} else {
1881 		lua_pushboolean(L, false);
1882 	}
1883 
1884 	return 1;
1885 }
1886 
SetLosViewColors(lua_State * L)1887 int LuaUnsyncedCtrl::SetLosViewColors(lua_State* L)
1888 {
1889 	float red[4];
1890 	float green[4];
1891 	float blue[4];
1892 	if ((LuaUtils::ParseFloatArray(L, 1, red,   4) != 4) ||
1893 	    (LuaUtils::ParseFloatArray(L, 2, green, 4) != 4) ||
1894 	    (LuaUtils::ParseFloatArray(L, 3, blue,  4) != 4)) {
1895 		luaL_error(L, "Incorrect arguments to SetLosViewColors()");
1896 	}
1897 	const int scale = CBaseGroundDrawer::losColorScale;
1898 
1899 	CBaseGroundDrawer* gd = readMap->GetGroundDrawer();
1900 	gd->alwaysColor[0] = (int)(scale *   red[0]);
1901 	gd->alwaysColor[1] = (int)(scale * green[0]);
1902 	gd->alwaysColor[2] = (int)(scale *  blue[0]);
1903 	gd->losColor[0]    = (int)(scale *   red[1]);
1904 	gd->losColor[1]    = (int)(scale * green[1]);
1905 	gd->losColor[2]    = (int)(scale *  blue[1]);
1906 	gd->radarColor[0]  = (int)(scale *   red[2]);
1907 	gd->radarColor[1]  = (int)(scale * green[2]);
1908 	gd->radarColor[2]  = (int)(scale *  blue[2]);
1909 	gd->jamColor[0]    = (int)(scale *   red[3]);
1910 	gd->jamColor[1]    = (int)(scale * green[3]);
1911 	gd->jamColor[2]    = (int)(scale *  blue[3]);
1912 	return 0;
1913 }
1914 
1915 
1916 /******************************************************************************/
1917 /******************************************************************************/
1918 
1919 #define SET_IN_OVERLAY_WARNING \
1920 	if (lua_isboolean(L, 3)) { \
1921 		static bool shown = false; \
1922 		if (!shown) { \
1923 			LOG_L(L_WARNING, "%s: third parameter \"setInOverlay\" is deprecated", __FUNCTION__); \
1924 			shown = true; \
1925 		} \
1926 	}
1927 
GetConfigInt(lua_State * L)1928 int LuaUnsyncedCtrl::GetConfigInt(lua_State* L)
1929 {
1930 	if (!CLuaHandle::CheckModUICtrl(L)) {
1931 		// FIXME: why not put this in UnsyncedRead without the ModUICtrl restriction?
1932 		lua_pushnumber(L, 0);
1933 		return 1;
1934 	}
1935 	const string name = luaL_checkstring(L, 1);
1936 	const int def     = luaL_optint(L, 2, 0);
1937 	SET_IN_OVERLAY_WARNING;
1938 	const int value = configHandler->IsSet(name) ? configHandler->GetInt(name) : def;
1939 	lua_pushnumber(L, value);
1940 	return 1;
1941 }
1942 
SetConfigInt(lua_State * L)1943 int LuaUnsyncedCtrl::SetConfigInt(lua_State* L)
1944 {
1945 	if (!CLuaHandle::CheckModUICtrl(L)) {
1946 		return 0;
1947 	}
1948 	const string name = luaL_checkstring(L, 1);
1949 	const int value   = luaL_checkint(L, 2);
1950 	const bool useOverlay = luaL_optboolean(L, 3, false);
1951 	// don't allow to change a read-only variable
1952 	if (configHandler->IsReadOnly(name)) {
1953 		LOG_L(L_ERROR, "tried to set readonly (int) %s = %d", name.c_str(), value);
1954 		return 0;
1955 	}
1956 	configHandler->EnableWriting(globalConfig->luaWritableConfigFile);
1957 	configHandler->Set(name, value, useOverlay);
1958 	configHandler->EnableWriting(true);
1959 	return 0;
1960 }
1961 
GetConfigString(lua_State * L)1962 int LuaUnsyncedCtrl::GetConfigString(lua_State* L)
1963 {
1964 	if (!CLuaHandle::CheckModUICtrl(L)) {
1965 		// FIXME: why not put this in UnsyncedRead without the ModUICtrl restriction?
1966 		lua_pushstring(L, "");
1967 		return 1;
1968 	}
1969 	const string name = luaL_checkstring(L, 1);
1970 	const string def  = luaL_optstring(L, 2, "");
1971 	SET_IN_OVERLAY_WARNING;
1972 	const string value = configHandler->IsSet(name) ? configHandler->GetString(name) : def;
1973 	lua_pushsstring(L, value);
1974 	return 1;
1975 }
1976 
1977 
SetConfigString(lua_State * L)1978 int LuaUnsyncedCtrl::SetConfigString(lua_State* L)
1979 {
1980 	if (!CLuaHandle::CheckModUICtrl(L)) {
1981 		return 0;
1982 	}
1983 	const string name  = luaL_checkstring(L, 1);
1984 	const string value = luaL_checkstring(L, 2);
1985 	const bool useOverlay = luaL_optboolean(L, 3, false);
1986 	// don't allow to change a read-only variable
1987 	if (configHandler->IsReadOnly(name)) {
1988 		LOG_L(L_ERROR, "tried to set readonly (string) %s = %s", name.c_str(), value.c_str());
1989 		return 0;
1990 	}
1991 	configHandler->EnableWriting(globalConfig->luaWritableConfigFile);
1992 	configHandler->SetString(name, value, useOverlay);
1993 	configHandler->EnableWriting(true);
1994 	return 0;
1995 }
1996 
1997 
1998 /******************************************************************************/
1999 
CreateDir(lua_State * L)2000 int LuaUnsyncedCtrl::CreateDir(lua_State* L)
2001 {
2002 	if (!CLuaHandle::CheckModUICtrl(L)) {
2003 		return 0;
2004 	}
2005 	const string dir = luaL_checkstring(L, 1);
2006 
2007 	// keep directories within the Spring directory
2008 	if ((dir[0] == '/') || (dir[0] == '\\') ||
2009 	    (strstr(dir.c_str(), "..") != NULL) ||
2010 	    ((dir.size() > 0) && (dir[1] == ':'))) {
2011 		luaL_error(L, "Invalid CreateDir() access: %s", dir.c_str());
2012 	}
2013 	const bool success = FileSystem::CreateDirectory(dir);
2014 	lua_pushboolean(L, success);
2015 	return 1;
2016 }
2017 
2018 
2019 /******************************************************************************/
2020 
Restart(lua_State * L)2021 int LuaUnsyncedCtrl::Restart(lua_State* L)
2022 {
2023 	const std::string springArguments = luaL_checkstring(L, 1);
2024 	const std::string scriptContents = luaL_checkstring(L, 2);
2025 
2026 	const std::string springFullName = Platform::GetProcessExecutableFile();
2027 	const std::string scriptFullName = dataDirLocater.GetWriteDirPath() + "script.txt";
2028 
2029 	std::vector<std::string> processArgs;
2030 
2031 	// arguments to Spring binary given by Lua code, if any
2032 	if (!springArguments.empty()) {
2033 		processArgs.push_back(springArguments);
2034 	}
2035 
2036 	if (!scriptContents.empty()) {
2037 		// create file 'script.txt' with contents given by Lua code
2038 		std::ofstream scriptFile(scriptFullName.c_str());
2039 
2040 		scriptFile.write(scriptContents.c_str(), scriptContents.size());
2041 		scriptFile.close();
2042 
2043 		processArgs.push_back(scriptFullName);
2044 	}
2045 
2046 	LOG("[Spring.%s] Spring \"%s\" should be restarting", __FUNCTION__, springFullName.c_str());
2047 
2048 #ifdef _WIN32
2049 	// else OpenAL crashes when using execvp
2050 	ISound::Shutdown();
2051 #endif
2052 
2053 	Platform::ExecuteProcess(springFullName, processArgs);
2054 
2055 	// not reached on success
2056 	lua_pushboolean(L, false);
2057 	return 1;
2058 }
2059 
SetWMIcon(lua_State * L)2060 int LuaUnsyncedCtrl::SetWMIcon(lua_State* L)
2061 {
2062 	const std::string iconFileName = luaL_checksstring(L, 1);
2063 
2064 	CBitmap iconTexture;
2065 	const bool loaded = iconTexture.Load(iconFileName);
2066 	if (loaded) {
2067 		WindowManagerHelper::SetIcon(&iconTexture);
2068 	} else {
2069 		luaL_error(L, "Failed to load image from file \"%s\"", iconFileName.c_str());
2070 	}
2071 
2072 	return 0;
2073 }
2074 
SetWMCaption(lua_State * L)2075 int LuaUnsyncedCtrl::SetWMCaption(lua_State* L)
2076 {
2077 	const std::string title = luaL_checksstring(L, 1);
2078 	WindowManagerHelper::SetCaption(title);
2079 	return 0;
2080 }
2081 
2082 /******************************************************************************/
2083 
SetUnitDefIcon(lua_State * L)2084 int LuaUnsyncedCtrl::SetUnitDefIcon(lua_State* L)
2085 {
2086 	if (!CLuaHandle::CheckModUICtrl(L)) {
2087 		return 0;
2088 	}
2089 
2090 	const int unitDefID = luaL_checkint(L, 1);
2091 	const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
2092 	if (ud == NULL) {
2093 		return 0;
2094 	}
2095 
2096 	ud->iconType = icon::iconHandler->GetIcon(luaL_checksstring(L, 2));
2097 
2098 	// set decoys to the same icon
2099 	std::map<int, std::set<int> >::const_iterator fit;
2100 
2101 	if (ud->decoyDef) {
2102 		ud->decoyDef->iconType = ud->iconType;
2103 		fit = unitDefHandler->decoyMap.find(ud->decoyDef->id);
2104 	} else {
2105 		fit = unitDefHandler->decoyMap.find(ud->id);
2106 	}
2107 	if (fit != unitDefHandler->decoyMap.end()) {
2108 		const std::set<int>& decoySet = fit->second;
2109 		std::set<int>::const_iterator dit;
2110 		for (dit = decoySet.begin(); dit != decoySet.end(); ++dit) {
2111   		const UnitDef* decoyDef = unitDefHandler->GetUnitDefByID(*dit);
2112 			decoyDef->iconType = ud->iconType;
2113 		}
2114 	}
2115 
2116 	return 0;
2117 }
2118 
2119 
SetUnitDefImage(lua_State * L)2120 int LuaUnsyncedCtrl::SetUnitDefImage(lua_State* L)
2121 {
2122 	if (!CLuaHandle::CheckModUICtrl(L)) {
2123 		return 0;
2124 	}
2125 
2126 	const int unitDefID = luaL_checkint(L, 1);
2127 	const UnitDef* ud = unitDefHandler->GetUnitDefByID(unitDefID);
2128 	if (ud == NULL) {
2129 		return 0;
2130 	}
2131 
2132 	if (lua_isnoneornil(L, 2)) {
2133 		// reset to default texture
2134 		unitDefHandler->SetUnitDefImage(ud, ud->buildPicName);
2135 		return 0;
2136 	}
2137 
2138 	if (!lua_israwstring(L, 2)) {
2139 		return 0;
2140 	}
2141 	const string texName = lua_tostring(L, 2);
2142 
2143 	if (texName[0] == LuaTextures::prefix) { // '!'
2144 		LuaTextures& textures = CLuaHandle::GetActiveTextures(L);
2145 		const LuaTextures::Texture* tex = textures.GetInfo(texName);
2146 		if (tex == NULL) {
2147 			return 0;
2148 		}
2149 		unitDefHandler->SetUnitDefImage(ud, tex->id, tex->xsize, tex->ysize);
2150 	} else {
2151 		unitDefHandler->SetUnitDefImage(ud, texName);
2152 	}
2153 	return 0;
2154 }
2155 
2156 
2157 /******************************************************************************/
2158 
SetUnitGroup(lua_State * L)2159 int LuaUnsyncedCtrl::SetUnitGroup(lua_State* L)
2160 {
2161 	if (!CLuaHandle::CheckModUICtrl(L)) {
2162 		return 0;
2163 	}
2164 	if (gs->noHelperAIs) {
2165 		return 0;
2166 	}
2167 
2168 	CUnit* unit = ParseRawUnit(L, __FUNCTION__, 1);
2169 	if (unit == NULL) {
2170 		return 0;
2171 	}
2172 	const int groupID = luaL_checkint(L, 2);
2173 
2174 	if (groupID == -1) {
2175 		unit->SetGroup(NULL);
2176 		return 0;
2177 	}
2178 
2179 	const vector<CGroup*>& groups = grouphandlers[gu->myTeam]->groups;
2180 	if ((groupID < 0) || (groupID >= (int)groups.size())) {
2181 		return 0;
2182 	}
2183 
2184 	CGroup* group = groups[groupID];
2185 	if (group != NULL) {
2186 		unit->SetGroup(group);
2187 	}
2188 	return 0;
2189 }
2190 
2191 
2192 /******************************************************************************/
2193 
ParseUnitMap(lua_State * L,const char * caller,int table,vector<int> & unitIDs)2194 static void ParseUnitMap(lua_State* L, const char* caller,
2195                          int table, vector<int>& unitIDs)
2196 {
2197 	if (!lua_istable(L, table)) {
2198 		luaL_error(L, "%s(): error parsing unit map", caller);
2199 	}
2200 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
2201 		if (lua_israwnumber(L, -2)) {
2202 			CUnit* unit = ParseCtrlUnit(L, __FUNCTION__, -2); // the key
2203 			if (unit != NULL && !unit->noSelect) {
2204 				unitIDs.push_back(unit->id);
2205 			}
2206 		}
2207 	}
2208 }
2209 
2210 
ParseUnitArray(lua_State * L,const char * caller,int table,vector<int> & unitIDs)2211 static void ParseUnitArray(lua_State* L, const char* caller,
2212                            int table, vector<int>& unitIDs)
2213 {
2214 	if (!lua_istable(L, table)) {
2215 		luaL_error(L, "%s(): error parsing unit array", caller);
2216 	}
2217 	for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
2218 		if (lua_israwnumber(L, -2) && lua_isnumber(L, -1)) {   // avoid 'n'
2219 			CUnit* unit = ParseCtrlUnit(L, __FUNCTION__, -1); // the value
2220 			if (unit != NULL && !unit->noSelect) {
2221 				unitIDs.push_back(unit->id);
2222 			}
2223 		}
2224 	}
2225 	return;
2226 }
2227 
2228 
2229 /******************************************************************************/
2230 
CanGiveOrders(const lua_State * L)2231 static bool CanGiveOrders(const lua_State *L)
2232 {
2233 	if (gs->frameNum <= 0) {
2234 		return false;
2235 	}
2236 	if (gs->noHelperAIs) {
2237 		return false;
2238 	}
2239 	if (gs->godMode) {
2240 		return true;
2241 	}
2242 	if (gu->spectating) {
2243 		return false;
2244 	}
2245 	const int ctrlTeam = CLuaHandle::GetHandleCtrlTeam(L);
2246 	// FIXME ? (correct? warning / error?)
2247 	if ((ctrlTeam != gu->myTeam) || (ctrlTeam < 0)) {
2248 		return false;
2249 	}
2250 	return true;
2251 }
2252 
2253 
GiveOrder(lua_State * L)2254 int LuaUnsyncedCtrl::GiveOrder(lua_State* L)
2255 {
2256 	if (!CLuaHandle::CheckModUICtrl(L)) {
2257 		return 0;
2258 	}
2259 	if (!CanGiveOrders(L)) {
2260 		return 1;
2261 	}
2262 
2263 	Command cmd = LuaUtils::ParseCommand(L, __FUNCTION__, 1);
2264 
2265 	selectedUnitsHandler.GiveCommand(cmd);
2266 
2267 	lua_pushboolean(L, true);
2268 
2269 	return 1;
2270 }
2271 
2272 
GiveOrderToUnit(lua_State * L)2273 int LuaUnsyncedCtrl::GiveOrderToUnit(lua_State* L)
2274 {
2275 	if (!CLuaHandle::CheckModUICtrl(L)) {
2276 		return 0;
2277 	}
2278 	if (!CanGiveOrders(L)) {
2279 		lua_pushboolean(L, false);
2280 		return 1;
2281 	}
2282 
2283 	CUnit* unit = ParseCtrlUnit(L, __FUNCTION__, 1);
2284 	if (unit == NULL || unit->noSelect) {
2285 		lua_pushboolean(L, false);
2286 		return 1;
2287 	}
2288 
2289 	Command cmd = LuaUtils::ParseCommand(L, __FUNCTION__, 2);
2290 
2291 	net->Send(CBaseNetProtocol::Get().SendAICommand(gu->myPlayerNum, skirmishAIHandler.GetCurrentAIID(), unit->id, cmd.GetID(), cmd.aiCommandId, cmd.options, cmd.params));
2292 
2293 	lua_pushboolean(L, true);
2294 	return 1;
2295 }
2296 
2297 
GiveOrderToUnitMap(lua_State * L)2298 int LuaUnsyncedCtrl::GiveOrderToUnitMap(lua_State* L)
2299 {
2300 	if (!CLuaHandle::CheckModUICtrl(L)) {
2301 		return 0;
2302 	}
2303 	if (!CanGiveOrders(L)) {
2304 		lua_pushboolean(L, false);
2305 		return 1;
2306 	}
2307 
2308 	// unitIDs
2309 	vector<int> unitIDs;
2310 	ParseUnitMap(L, __FUNCTION__, 1, unitIDs);
2311 	const int count = (int)unitIDs.size();
2312 
2313 	if (count <= 0) {
2314 		lua_pushboolean(L, false);
2315 		return 1;
2316 	}
2317 
2318 	Command cmd = LuaUtils::ParseCommand(L, __FUNCTION__, 2);
2319 
2320 	vector<Command> commands;
2321 	commands.push_back(cmd);
2322 	selectedUnitsHandler.SendCommandsToUnits(unitIDs, commands);
2323 
2324 	lua_pushboolean(L, true);
2325 	return 1;
2326 }
2327 
2328 
GiveOrderToUnitArray(lua_State * L)2329 int LuaUnsyncedCtrl::GiveOrderToUnitArray(lua_State* L)
2330 {
2331 	if (!CLuaHandle::CheckModUICtrl(L)) {
2332 		return 0;
2333 	}
2334 	if (!CanGiveOrders(L)) {
2335 		lua_pushboolean(L, false);
2336 		return 1;
2337 	}
2338 
2339 	// unitIDs
2340 	vector<int> unitIDs;
2341 	ParseUnitArray(L, __FUNCTION__, 1, unitIDs);
2342 	const int count = (int)unitIDs.size();
2343 
2344 	if (count <= 0) {
2345 		lua_pushboolean(L, false);
2346 		return 1;
2347 	}
2348 
2349 	Command cmd = LuaUtils::ParseCommand(L, __FUNCTION__, 2);
2350 
2351 	vector<Command> commands;
2352 	commands.push_back(cmd);
2353 	selectedUnitsHandler.SendCommandsToUnits(unitIDs, commands);
2354 
2355 	lua_pushboolean(L, true);
2356 	return 1;
2357 }
2358 
2359 
GiveOrderArrayToUnitMap(lua_State * L)2360 int LuaUnsyncedCtrl::GiveOrderArrayToUnitMap(lua_State* L)
2361 {
2362 	if (!CLuaHandle::CheckModUICtrl(L)) {
2363 		return 0;
2364 	}
2365 	if (!CanGiveOrders(L)) {
2366 		lua_pushboolean(L, false);
2367 		return 1;
2368 	}
2369 
2370 	// unitIDs
2371 	vector<int> unitIDs;
2372 	ParseUnitMap(L, __FUNCTION__, 1, unitIDs);
2373 
2374 	// commands
2375 	vector<Command> commands;
2376 	LuaUtils::ParseCommandArray(L, __FUNCTION__, 2, commands);
2377 
2378 	if (unitIDs.empty() || commands.empty()) {
2379 		lua_pushboolean(L, false);
2380 		return 1;
2381 	}
2382 
2383 	selectedUnitsHandler.SendCommandsToUnits(unitIDs, commands);
2384 
2385 	lua_pushboolean(L, true);
2386 	return 1;
2387 }
2388 
2389 
GiveOrderArrayToUnitArray(lua_State * L)2390 int LuaUnsyncedCtrl::GiveOrderArrayToUnitArray(lua_State* L)
2391 {
2392 	if (!CLuaHandle::CheckModUICtrl(L)) {
2393 		return 0;
2394 	}
2395 	if (!CanGiveOrders(L)) {
2396 		lua_pushboolean(L, false);
2397 		return 1;
2398 	}
2399 
2400 	const int args = lua_gettop(L); // number of arguments
2401 
2402 	// unitIDs
2403 	vector<int> unitIDs;
2404 	ParseUnitArray(L, __FUNCTION__, 1, unitIDs);
2405 
2406 	// commands
2407 	vector<Command> commands;
2408 	LuaUtils::ParseCommandArray(L, __FUNCTION__, 2, commands);
2409 
2410 	bool pairwise = false;
2411 	if (args >= 3)
2412 		pairwise = lua_toboolean(L, 3);
2413 
2414 	if (unitIDs.empty() || commands.empty()) {
2415 		lua_pushboolean(L, false);
2416 		return 1;
2417 	}
2418 
2419 	selectedUnitsHandler.SendCommandsToUnits(unitIDs, commands, pairwise);
2420 
2421 	lua_pushboolean(L, true);
2422 	return 1;
2423 }
2424 
2425 
2426 /******************************************************************************/
2427 
GetRawMsg(lua_State * L,const char * caller,int index)2428 static string GetRawMsg(lua_State* L, const char* caller, int index)
2429 {
2430 	return luaL_checksstring(L, index);
2431 }
2432 
2433 
SendLuaUIMsg(lua_State * L)2434 int LuaUnsyncedCtrl::SendLuaUIMsg(lua_State* L)
2435 {
2436 	if (!CLuaHandle::CheckModUICtrl(L)) {
2437 		return 0;
2438 	}
2439 	const string msg = GetRawMsg(L, __FUNCTION__, 1);
2440 	std::vector<boost::uint8_t> data(msg.size());
2441 	std::copy(msg.begin(), msg.end(), data.begin());
2442 	const string mode = luaL_optstring(L, 2, "");
2443 	unsigned char modeNum = 0;
2444 	if ((mode == "s") || (mode == "specs")) {
2445 		modeNum = 's';
2446 	}
2447 	else if ((mode == "a") || (mode == "allies")) {
2448 		modeNum = 'a';
2449 	}
2450 	else if (!mode.empty()) {
2451 		luaL_error(L, "Unknown SendLuaUIMsg() mode");
2452 	}
2453 	try {
2454 		net->Send(CBaseNetProtocol::Get().SendLuaMsg(gu->myPlayerNum, LUA_HANDLE_ORDER_UI, modeNum, data));
2455 	} catch (const netcode::PackPacketException& ex) {
2456 		luaL_error(L, "SendLuaUIMsg() packet error: %s", ex.what());
2457 	}
2458 	return 0;
2459 }
2460 
2461 
SendLuaGaiaMsg(lua_State * L)2462 int LuaUnsyncedCtrl::SendLuaGaiaMsg(lua_State* L)
2463 {
2464 	if (!CLuaHandle::CheckModUICtrl(L)) {
2465 		return 0;
2466 	}
2467 	const string msg = GetRawMsg(L, __FUNCTION__, 1);
2468 	std::vector<boost::uint8_t> data(msg.size());
2469 	std::copy(msg.begin(), msg.end(), data.begin());
2470 	try {
2471 		net->Send(CBaseNetProtocol::Get().SendLuaMsg(gu->myPlayerNum, LUA_HANDLE_ORDER_GAIA, 0, data));
2472 	} catch (const netcode::PackPacketException& ex) {
2473 		luaL_error(L, "SendLuaGaiaMsg() packet error: %s", ex.what());
2474 	}
2475 	return 0;
2476 }
2477 
2478 
SendLuaRulesMsg(lua_State * L)2479 int LuaUnsyncedCtrl::SendLuaRulesMsg(lua_State* L)
2480 {
2481 	if (!CLuaHandle::CheckModUICtrl(L)) {
2482 		return 0;
2483 	}
2484 	const string msg = GetRawMsg(L, __FUNCTION__, 1);
2485 	std::vector<boost::uint8_t> data(msg.size());
2486 	std::copy(msg.begin(), msg.end(), data.begin());
2487 	try {
2488 		net->Send(CBaseNetProtocol::Get().SendLuaMsg(gu->myPlayerNum, LUA_HANDLE_ORDER_RULES, 0, data));
2489 	} catch (const netcode::PackPacketException& ex) {
2490 		luaL_error(L, "SendLuaRulesMsg() packet error: %s", ex.what());
2491 	}
2492 	return 0;
2493 }
2494 
2495 
2496 /******************************************************************************/
2497 
SetShareLevel(lua_State * L)2498 int LuaUnsyncedCtrl::SetShareLevel(lua_State* L)
2499 {
2500 	if (!CLuaHandle::CheckModUICtrl(L)) {
2501 		return 0;
2502 	}
2503 	if (gu->spectating || gs->noHelperAIs || (gs->frameNum <= 0)) {
2504 		return 0;
2505 	}
2506 
2507 	const string shareType = luaL_checksstring(L, 1);
2508 	const float shareLevel = max(0.0f, min(1.0f, luaL_checkfloat(L, 2)));
2509 
2510 	if (shareType == "metal") {
2511 		net->Send(CBaseNetProtocol::Get().SendSetShare(gu->myPlayerNum, gu->myTeam, shareLevel, teamHandler->Team(gu->myTeam)->energyShare));
2512 	}
2513 	else if (shareType == "energy") {
2514 		net->Send(CBaseNetProtocol::Get().SendSetShare(gu->myPlayerNum, gu->myTeam,	teamHandler->Team(gu->myTeam)->metalShare, shareLevel));
2515 	}
2516 	else {
2517 		LOG_L(L_WARNING, "SetShareLevel() unknown resource: %s", shareType.c_str());
2518 	}
2519 	return 0;
2520 }
2521 
2522 
ShareResources(lua_State * L)2523 int LuaUnsyncedCtrl::ShareResources(lua_State* L)
2524 {
2525 	if (!CLuaHandle::CheckModUICtrl(L)) {
2526 		return 0;
2527 	}
2528 	if (gu->spectating || gs->noHelperAIs || (gs->frameNum <= 0)) {
2529 		return 0;
2530 	}
2531 
2532 	const int args = lua_gettop(L); // number of arguments
2533 	if ((args < 2) || !lua_isnumber(L, 1) || !lua_isstring(L, 2) ||
2534 	    ((args >= 3) && !lua_isnumber(L, 3))) {
2535 		luaL_error(L, "Incorrect arguments to ShareResources()");
2536 	}
2537 	const int teamID = lua_toint(L, 1);
2538 	if (!teamHandler->IsValidTeam(teamID)) {
2539 		return 0;
2540 	}
2541 	const CTeam* team = teamHandler->Team(teamID);
2542 	if ((team == NULL) || team->isDead) {
2543 		return 0;
2544 	}
2545 	const string& type = lua_tostring(L, 2);
2546 	if (type == "units") {
2547 		// update the selection, and clear the unit command queues
2548 		Command c(CMD_STOP);
2549 		selectedUnitsHandler.GiveCommand(c, false);
2550 		net->Send(CBaseNetProtocol::Get().SendShare(gu->myPlayerNum, teamID, 1, 0.0f, 0.0f));
2551 		selectedUnitsHandler.ClearSelected();
2552 	}
2553 	else if (args >= 3) {
2554 		const float amount = lua_tofloat(L, 3);
2555 		if (type == "metal") {
2556 			net->Send(CBaseNetProtocol::Get().SendShare(gu->myPlayerNum, teamID, 0, amount, 0.0f));
2557 		}
2558 		else if (type == "energy") {
2559 			net->Send(CBaseNetProtocol::Get().SendShare(gu->myPlayerNum, teamID, 0, 0.0f, amount));
2560 		}
2561 	}
2562 	return 0;
2563 }
2564 
2565 
2566 /******************************************************************************/
2567 /******************************************************************************/
2568 
2569 
SetLastMessagePosition(lua_State * L)2570 int LuaUnsyncedCtrl::SetLastMessagePosition(lua_State* L)
2571 {
2572 	const float3 pos(luaL_checkfloat(L, 1),
2573 	                 luaL_checkfloat(L, 2),
2574 	                 luaL_checkfloat(L, 3));
2575 
2576 	eventHandler.LastMessagePosition(pos);
2577 
2578 	return 0;
2579 }
2580 
2581 /******************************************************************************/
2582 /******************************************************************************/
2583 
MarkerAddPoint(lua_State * L)2584 int LuaUnsyncedCtrl::MarkerAddPoint(lua_State* L)
2585 {
2586 	if (!CLuaHandle::CheckModUICtrl(L)) {
2587 		return 0;
2588 	}
2589 	if (inMapDrawer == NULL) {
2590 		return 0;
2591 	}
2592 	const float3 pos(luaL_checkfloat(L, 1),
2593 	                 luaL_checkfloat(L, 2),
2594 	                 luaL_checkfloat(L, 3));
2595 	const string text = luaL_optstring(L, 4, "");
2596 	const bool onlyLocal = (lua_isnumber(L, 5)) ? bool(luaL_optnumber(L, 5, 1)) : luaL_optboolean(L, 5, true);
2597 
2598 	if (onlyLocal) {
2599 		inMapDrawerModel->AddPoint(pos, text, gu->myPlayerNum);
2600 	} else {
2601 		inMapDrawer->SendPoint(pos, text, true);
2602 	}
2603 
2604 	return 0;
2605 }
2606 
2607 
MarkerAddLine(lua_State * L)2608 int LuaUnsyncedCtrl::MarkerAddLine(lua_State* L)
2609 {
2610 	if (!CLuaHandle::CheckModUICtrl(L)) {
2611 		return 0;
2612 	}
2613 	if (inMapDrawer == NULL) {
2614 		return 0;
2615 	}
2616 	const float3 pos1(luaL_checkfloat(L, 1),
2617 	                  luaL_checkfloat(L, 2),
2618 	                  luaL_checkfloat(L, 3));
2619 	const float3 pos2(luaL_checkfloat(L, 4),
2620 	                  luaL_checkfloat(L, 5),
2621 	                  luaL_checkfloat(L, 6));
2622 	const bool onlyLocal = (lua_isnumber(L, 7)) ? bool(luaL_optnumber(L, 7, 0)) : luaL_optboolean(L, 7, false);
2623 
2624 	if (onlyLocal) {
2625 		inMapDrawerModel->AddLine(pos1, pos2, gu->myPlayerNum);
2626 	} else {
2627 		inMapDrawer->SendLine(pos1, pos2, true);
2628 	}
2629 
2630 	return 0;
2631 }
2632 
2633 
MarkerErasePosition(lua_State * L)2634 int LuaUnsyncedCtrl::MarkerErasePosition(lua_State* L)
2635 {
2636 	if (!CLuaHandle::CheckModUICtrl(L)) {
2637 		return 0;
2638 	}
2639 	if (inMapDrawer == NULL) {
2640 		return 0;
2641 	}
2642 	const float3 pos(luaL_checkfloat(L, 1),
2643 	                 luaL_checkfloat(L, 2),
2644 	                 luaL_checkfloat(L, 3));
2645 
2646 	inMapDrawer->SendErase(pos);
2647 
2648 	return 0;
2649 }
2650 
2651 
2652 /******************************************************************************/
2653 /******************************************************************************/
2654 
SetDrawSelectionInfo(lua_State * L)2655 int LuaUnsyncedCtrl::SetDrawSelectionInfo(lua_State* L)
2656 {
2657 	if (!CLuaHandle::CheckModUICtrl(L)) {
2658 		return 0;
2659 	}
2660 
2661 	if (guihandler)
2662 		guihandler->SetDrawSelectionInfo(luaL_checkboolean(L, 1));
2663 
2664 	return 0;
2665 }
2666 
2667 
2668 /******************************************************************************/
2669 /******************************************************************************/
2670 
SetBuildSpacing(lua_State * L)2671 int LuaUnsyncedCtrl::SetBuildSpacing(lua_State* L)
2672 {
2673 	if (!CLuaHandle::CheckModUICtrl(L)) {
2674 		return 0;
2675 	}
2676 
2677 	if (guihandler)
2678 		guihandler->SetBuildSpacing(luaL_checkinteger(L, 1));
2679 
2680 	return 0;
2681 }
2682 
SetBuildFacing(lua_State * L)2683 int LuaUnsyncedCtrl::SetBuildFacing(lua_State* L)
2684 {
2685 	if (!CLuaHandle::CheckModUICtrl(L)) {
2686 		return 0;
2687 	}
2688 
2689 	if (guihandler)
2690 		guihandler->SetBuildFacing(luaL_checkint(L, 1));
2691 
2692 	return 0;
2693 }
2694 /******************************************************************************/
2695 /******************************************************************************/
2696 
2697 
2698 
SetSunParameters(lua_State * L)2699 int LuaUnsyncedCtrl::SetSunParameters(lua_State* L)
2700 {
2701 	DynamicSkyLight* dynSkyLight = dynamic_cast<DynamicSkyLight*>(sky->GetLight());
2702 
2703 	if (dynSkyLight == NULL)
2704 		return 0;
2705 
2706 	const float4 sunDir(luaL_checkfloat(L, 1), luaL_checkfloat(L, 2), luaL_checkfloat(L, 3), luaL_checkfloat(L, 4));
2707 	const float startAngle = luaL_checkfloat(L, 5);
2708 	const float orbitTime = luaL_checkfloat(L, 6);
2709 
2710 	dynSkyLight->SetLightParams(sunDir, startAngle, orbitTime);
2711 	return 0;
2712 }
2713 
SetSunManualControl(lua_State * L)2714 int LuaUnsyncedCtrl::SetSunManualControl(lua_State* L)
2715 {
2716 	DynamicSkyLight* dynSkyLight = dynamic_cast<DynamicSkyLight*>(sky->GetLight());
2717 
2718 	if (dynSkyLight == NULL)
2719 		return 0;
2720 
2721 	dynSkyLight->SetLuaControl(luaL_checkboolean(L, 1));
2722 	return 0;
2723 }
2724 
SetSunDirection(lua_State * L)2725 int LuaUnsyncedCtrl::SetSunDirection(lua_State* L)
2726 {
2727 	DynamicSkyLight* dynSkyLight = dynamic_cast<DynamicSkyLight*>(sky->GetLight());
2728 
2729 	if (dynSkyLight == NULL)
2730 		return 0;
2731 	if (!dynSkyLight->GetLuaControl())
2732 		return 0;
2733 
2734 	dynSkyLight->SetLightDir(float3(luaL_checkfloat(L, 1), luaL_checkfloat(L, 2), luaL_checkfloat(L, 3)));
2735 	return 0;
2736 }
2737 
2738 
2739 
2740 /******************************************************************************/
2741 /******************************************************************************/
2742 
SendSkirmishAIMessage(lua_State * L)2743 int LuaUnsyncedCtrl::SendSkirmishAIMessage(lua_State* L) {
2744 	if (CLuaHandle::GetHandleSynced(L)) {
2745 		return 0;
2746 	}
2747 
2748 	const int aiTeam = luaL_checkint(L, 1);
2749 	const char* inData = luaL_checkstring(L, 2);
2750 
2751 	std::vector<const char*> outData;
2752 
2753 	luaL_checkstack(L, 2, __FUNCTION__);
2754 	lua_pushboolean(L, eoh->SendLuaMessages(aiTeam, inData, outData));
2755 
2756 	// push the AI response(s)
2757 	lua_createtable(L, outData.size(), 0);
2758 	for (unsigned int n = 0; n < outData.size(); n++) {
2759 		lua_pushstring(L, outData[n]);
2760 		lua_rawseti(L, -2, n + 1);
2761 	}
2762 
2763 	return 2;
2764 }
2765 
2766 /******************************************************************************/
2767 /******************************************************************************/
2768 
SetLogSectionFilterLevel(lua_State * L)2769 int LuaUnsyncedCtrl::SetLogSectionFilterLevel(lua_State* L) {
2770 	int logLevel = LOG_LEVEL_INFO;
2771 
2772 	if (lua_israwnumber(L, 2)) {
2773 		logLevel = lua_tonumber(L, 2);
2774 	}
2775 	if (lua_isstring(L, 2)) {
2776 		const std::string& loglvlstr = StringToLower(lua_tostring(L, 2));
2777 
2778 		if (loglvlstr == "debug") {
2779 			logLevel = LOG_LEVEL_DEBUG;
2780 		}
2781 		else if (loglvlstr == "info") {
2782 			logLevel = LOG_LEVEL_INFO;
2783 		}
2784 		else if (loglvlstr == "warning") {
2785 			logLevel = LOG_LEVEL_WARNING;
2786 		}
2787 		else if (loglvlstr == "error") {
2788 			logLevel = LOG_LEVEL_ERROR;
2789 		}
2790 		else if (loglvlstr == "fatal") {
2791 			logLevel = LOG_LEVEL_FATAL;
2792 		}
2793 		else {
2794 			return luaL_error(L, "Incorrect arguments to Spring.SetLogSectionFilterLevel(logsection, loglevel)");
2795 		}
2796 	}
2797 
2798 	log_frontend_register_runtime_section(luaL_checkstring(L, 1), logLevel);
2799 	return 0;
2800 }
2801 
2802 /******************************************************************************/
2803 /******************************************************************************/
2804 
ClearWatchDogTimer(lua_State * L)2805 int LuaUnsyncedCtrl::ClearWatchDogTimer(lua_State* L) {
2806 	const int args = lua_gettop(L); // number of arguments
2807 
2808 	if (args == 0) {
2809 		Watchdog::ClearTimer();
2810 	} else {
2811 		std::string threadname = "main";
2812 		if (lua_isstring(L, 1))
2813 			threadname = lua_tostring(L, 1);
2814 		Watchdog::ClearTimer(threadname);
2815 	}
2816 
2817 	return 0;
2818 }
2819