1 #include <i18n.h>
2 #include "gameGlobalInfo.h"
3 #include "preferenceManager.h"
4 #include "scienceDatabase.h"
5 
6 P<GameGlobalInfo> gameGlobalInfo;
7 
8 REGISTER_MULTIPLAYER_CLASS(GameGlobalInfo, "GameGlobalInfo")
GameGlobalInfo()9 GameGlobalInfo::GameGlobalInfo()
10 : MultiplayerObject("GameGlobalInfo")
11 {
12     assert(!gameGlobalInfo);
13 
14     callsign_counter = 0;
15     victory_faction = -1;
16     gameGlobalInfo = this;
17 
18     for(int n=0; n<max_player_ships; n++)
19     {
20         playerShipId[n] = -1;
21         registerMemberReplication(&playerShipId[n]);
22     }
23 
24     global_message_timeout = 0.0;
25     player_warp_jump_drive_setting = PWJ_ShipDefault;
26     scanning_complexity = SC_Normal;
27     hacking_difficulty = 2;
28     hacking_games = HG_All;
29     use_beam_shield_frequencies = true;
30     use_system_damage = true;
31     allow_main_screen_tactical_radar = true;
32     allow_main_screen_long_range_radar = true;
33     gm_control_code = "";
34     elapsed_time = 0.0f;
35 
36     intercept_all_comms_to_gm = false;
37 
38     registerMemberReplication(&scanning_complexity);
39     registerMemberReplication(&hacking_difficulty);
40     registerMemberReplication(&hacking_games);
41     registerMemberReplication(&global_message);
42     registerMemberReplication(&global_message_timeout, 1.0);
43     registerMemberReplication(&banner_string);
44     registerMemberReplication(&victory_faction);
45     registerMemberReplication(&use_beam_shield_frequencies);
46     registerMemberReplication(&use_system_damage);
47     registerMemberReplication(&allow_main_screen_tactical_radar);
48     registerMemberReplication(&allow_main_screen_long_range_radar);
49     registerMemberReplication(&gm_control_code);
50     registerMemberReplication(&elapsed_time, 0.1);
51 
52     for(unsigned int n=0; n<factionInfo.size(); n++)
53         reputation_points.push_back(0);
54     registerMemberReplication(&reputation_points, 1.0);
55 }
56 
57 //due to a suspected compiler bug this deconstructor needs to be explicitly defined
~GameGlobalInfo()58 GameGlobalInfo::~GameGlobalInfo()
59 {
60 }
61 
getPlayerShip(int index)62 P<PlayerSpaceship> GameGlobalInfo::getPlayerShip(int index)
63 {
64     assert(index >= 0 && index < max_player_ships);
65     if (game_server)
66         return game_server->getObjectById(playerShipId[index]);
67     return game_client->getObjectById(playerShipId[index]);
68 }
69 
setPlayerShip(int index,P<PlayerSpaceship> ship)70 void GameGlobalInfo::setPlayerShip(int index, P<PlayerSpaceship> ship)
71 {
72     assert(index >= 0 && index < max_player_ships);
73     assert(game_server);
74 
75     if (ship)
76         playerShipId[index] = ship->getMultiplayerId();
77     else
78         playerShipId[index] = -1;
79 }
80 
findPlayerShip(P<PlayerSpaceship> ship)81 int GameGlobalInfo::findPlayerShip(P<PlayerSpaceship> ship)
82 {
83     for(int n=0; n<max_player_ships; n++)
84         if (getPlayerShip(n) == ship)
85             return n;
86     return -1;
87 }
88 
insertPlayerShip(P<PlayerSpaceship> ship)89 int GameGlobalInfo::insertPlayerShip(P<PlayerSpaceship> ship)
90 {
91     for(int n=0; n<max_player_ships; n++)
92     {
93         if (!getPlayerShip(n))
94         {
95             setPlayerShip(n, ship);
96             return n;
97         }
98     }
99     return -1;
100 }
101 
update(float delta)102 void GameGlobalInfo::update(float delta)
103 {
104     if (global_message_timeout > 0.0)
105     {
106         global_message_timeout -= delta;
107     }
108     if (my_player_info)
109     {
110         //Set the my_spaceship variable based on the my_player_info->ship_id
111         if ((my_spaceship && my_spaceship->getMultiplayerId() != my_player_info->ship_id) || (my_spaceship && my_player_info->ship_id == -1) || (!my_spaceship && my_player_info->ship_id != -1))
112         {
113             if (game_server)
114                 my_spaceship = game_server->getObjectById(my_player_info->ship_id);
115             else
116                 my_spaceship = game_client->getObjectById(my_player_info->ship_id);
117         }
118     }
119     elapsed_time += delta;
120 }
121 
getNextShipCallsign()122 string GameGlobalInfo::getNextShipCallsign()
123 {
124     callsign_counter += 1;
125     switch(irandom(0, 9))
126     {
127     case 0: return "S" + string(callsign_counter);
128     case 1: return "NC" + string(callsign_counter);
129     case 2: return "CV" + string(callsign_counter);
130     case 3: return "SS" + string(callsign_counter);
131     case 4: return "VS" + string(callsign_counter);
132     case 5: return "BR" + string(callsign_counter);
133     case 6: return "CSS" + string(callsign_counter);
134     case 7: return "UTI" + string(callsign_counter);
135     case 8: return "VK" + string(callsign_counter);
136     case 9: return "CCN" + string(callsign_counter);
137     }
138     return "SS" + string(callsign_counter);
139 }
140 
addScript(P<Script> script)141 void GameGlobalInfo::addScript(P<Script> script)
142 {
143     script_list.update();
144     script_list.push_back(script);
145 }
146 
reset()147 void GameGlobalInfo::reset()
148 {
149     if (state_logger)
150         state_logger->destroy();
151 
152     gm_callback_functions.clear();
153     gm_messages.clear();
154     on_gm_click = nullptr;
155 
156     flushDatabaseData();
157 
158     foreach(GameEntity, e, entityList)
159         e->destroy();
160     foreach(SpaceObject, o, space_object_list)
161         o->destroy();
162     if (engine->getObject("scenario"))
163         engine->getObject("scenario")->destroy();
164 
165     foreach(Script, s, script_list)
166     {
167         s->destroy();
168     }
169     for(unsigned int n=0; n<reputation_points.size(); n++)
170         reputation_points[n] = 0;
171     elapsed_time = 0.0f;
172     callsign_counter = 0;
173     victory_faction = -1;
174     allow_new_player_ships = true;
175 }
176 
startScenario(string filename)177 void GameGlobalInfo::startScenario(string filename)
178 {
179     reset();
180 
181     i18n::reset();
182     i18n::load("locale/main." + PreferencesManager::get("language", "en") + ".po");
183     i18n::load("locale/" + filename.replace(".lua", "." + PreferencesManager::get("language", "en") + ".po"));
184 
185     fillDefaultDatabaseData();
186 
187     P<ScriptObject> scienceInfoScript = new ScriptObject("science_db.lua");
188     if (scienceInfoScript->getError() != "") exit(1);
189     scienceInfoScript->destroy();
190 
191     P<ScriptObject> script = new ScriptObject();
192     script->run(filename);
193     engine->registerObject("scenario", script);
194 
195     if (PreferencesManager::get("game_logs", "1").toInt())
196     {
197         state_logger = new GameStateLogger();
198         state_logger->start();
199     }
200 }
201 
destroy()202 void GameGlobalInfo::destroy()
203 {
204     reset();
205     MultiplayerObject::destroy();
206 }
207 
playerWarpJumpDriveToString(EPlayerWarpJumpDrive player_warp_jump_drive)208 string playerWarpJumpDriveToString(EPlayerWarpJumpDrive player_warp_jump_drive)
209 {
210     switch(player_warp_jump_drive)
211     {
212     case PWJ_ShipDefault:
213         return "Ship default";
214     case PWJ_WarpDrive:
215         return "Warp-drive";
216     case PWJ_JumpDrive:
217         return "Jump-drive";
218     case PWJ_WarpAndJumpDrive:
219         return "Both";
220     default:
221         return "?";
222     }
223 }
224 
getSectorName(sf::Vector2f position)225 string getSectorName(sf::Vector2f position)
226 {
227     constexpr float sector_size = 20000;
228     int sector_x = floorf(position.x / sector_size) + 5;
229     int sector_y = floorf(position.y / sector_size) + 5;
230     string y;
231     string x;
232     if (sector_y >= 0)
233         y = string(char('A' + (sector_y)));
234     else
235         y = string(char('z' + sector_y / 26)) + string(char('z' + 1 + (sector_y % 26)));
236     if (sector_x >= 0)
237         x = string(sector_x);
238     else
239         x = string(100 + sector_x);
240     return y + x;
241 }
242 
getSectorName(lua_State * L)243 int getSectorName(lua_State* L)
244 {
245     float x = luaL_checknumber(L, 1);
246     float y = luaL_checknumber(L, 2);
247     lua_pushstring(L, getSectorName(sf::Vector2f(x, y)).c_str());
248     return 1;
249 }
250 /// getSectorName(x, y)
251 /// Return the sector name for the point with coordinates (x, y). Compare SpaceObject:getSectorName().
252 REGISTER_SCRIPT_FUNCTION(getSectorName);
253 
victory(lua_State * L)254 static int victory(lua_State* L)
255 {
256     gameGlobalInfo->setVictory(luaL_checkstring(L, 1));
257     if (engine->getObject("scenario"))
258         engine->getObject("scenario")->destroy();
259     engine->setGameSpeed(0.0);
260     return 0;
261 }
262 /// victory(string)
263 /// Called with a faction name as parameter, sets a certain faction as victor and ends the game.
264 /// (The GM can unpause the game, but the scenario with its update function is destroyed.)
265 REGISTER_SCRIPT_FUNCTION(victory);
266 
globalMessage(lua_State * L)267 static int globalMessage(lua_State* L)
268 {
269     gameGlobalInfo->global_message = luaL_checkstring(L, 1);
270     gameGlobalInfo->global_message_timeout = 5.0;
271     return 0;
272 }
273 /// globalMessage(string)
274 /// Show a global message on the main screens of all active player ships.
275 /// The message is shown for 5 sec; new messages replace the old immediately.
276 REGISTER_SCRIPT_FUNCTION(globalMessage);
277 
setBanner(lua_State * L)278 static int setBanner(lua_State* L)
279 {
280     gameGlobalInfo->banner_string = luaL_checkstring(L, 1);
281     return 0;
282 }
283 /// setBanner(string)
284 /// Show a scrolling banner containing this text on the cinematic and top down views.
285 REGISTER_SCRIPT_FUNCTION(setBanner);
286 
getScenarioTime(lua_State * L)287 static int getScenarioTime(lua_State* L)
288 {
289     lua_pushnumber(L, gameGlobalInfo->elapsed_time);
290     return 1;
291 }
292 /// getScenarioTime()
293 /// Return the elapsed time of the scenario.
294 REGISTER_SCRIPT_FUNCTION(getScenarioTime);
295 
getPlayerShip(lua_State * L)296 static int getPlayerShip(lua_State* L)
297 {
298     int index = luaL_checkinteger(L, 1);
299     if (index == -1)
300     {
301         for(index = 0; index<GameGlobalInfo::max_player_ships; index++)
302         {
303             P<PlayerSpaceship> ship = gameGlobalInfo->getPlayerShip(index);
304             if (ship)
305                 return convert<P<PlayerSpaceship> >::returnType(L, ship);
306         }
307         return 0;
308     }
309     if (index < 1 || index > GameGlobalInfo::max_player_ships)
310         return 0;
311     P<PlayerSpaceship> ship = gameGlobalInfo->getPlayerShip(index - 1);
312     if (!ship)
313         return 0;
314     return convert<P<PlayerSpaceship> >::returnType(L, ship);
315 }
316 /// getPlayerShip(index)
317 /// Return the player's ship, use -1 to get the first active player ship.
318 REGISTER_SCRIPT_FUNCTION(getPlayerShip);
319 
getActivePlayerShips(lua_State * L)320 static int getActivePlayerShips(lua_State* L)
321 {
322     PVector<PlayerSpaceship> ships;
323     ships.reserve(GameGlobalInfo::max_player_ships);
324     for (auto index = 0; index < GameGlobalInfo::max_player_ships; ++index)
325     {
326         auto ship = gameGlobalInfo->getPlayerShip(index);
327 
328         if (ship)
329         {
330             ships.emplace_back(std::move(ship));
331         }
332     }
333 
334     return convert<PVector<PlayerSpaceship>>::returnType(L, ships);
335 }
336 /// getActivePlayerShips()
337 /// Return a list of active player ships.
338 REGISTER_SCRIPT_FUNCTION(getActivePlayerShips);
339 
getObjectsInRadius(lua_State * L)340 static int getObjectsInRadius(lua_State* L)
341 {
342     float x = luaL_checknumber(L, 1);
343     float y = luaL_checknumber(L, 2);
344     float r = luaL_checknumber(L, 3);
345 
346     sf::Vector2f position(x, y);
347 
348     PVector<SpaceObject> objects;
349     PVector<Collisionable> objectList = CollisionManager::queryArea(position - sf::Vector2f(r, r), position + sf::Vector2f(r, r));
350     foreach(Collisionable, obj, objectList)
351     {
352         P<SpaceObject> sobj = obj;
353         if (sobj && (sobj->getPosition() - position) < r)
354             objects.push_back(sobj);
355     }
356 
357     return convert<PVector<SpaceObject> >::returnType(L, objects);
358 }
359 /// getObjectsInRadius(x, y, radius)
360 /// Return a list of all space objects at the x,y location within a certain radius.
361 REGISTER_SCRIPT_FUNCTION(getObjectsInRadius);
362 
getAllObjects(lua_State * L)363 static int getAllObjects(lua_State* L)
364 {
365     return convert<PVector<SpaceObject> >::returnType(L, space_object_list);
366 }
367 /// getAllObjects()
368 /// Return a list of all space objects. (Use with care, this could return a very long list which could slow down the game when called every update)
369 REGISTER_SCRIPT_FUNCTION(getAllObjects);
370 
getScenarioVariation(lua_State * L)371 static int getScenarioVariation(lua_State* L)
372 {
373     lua_pushstring(L, gameGlobalInfo->variation.c_str());
374     return 1;
375 }
376 /// getScenarioVariation()
377 /// Returns the currently used scenario variation.
378 REGISTER_SCRIPT_FUNCTION(getScenarioVariation);
379 
getGameLanguage(lua_State * L)380 static int getGameLanguage(lua_State* L)
381 {
382     lua_pushstring(L, PreferencesManager::get("language", "en").c_str());
383     return 1;
384 }
385 /// getGameLanguage()
386 /// Returns the language as the string set in game preferences under language key
387 REGISTER_SCRIPT_FUNCTION(getGameLanguage);
388 
389 /** Short lived object to do a scenario change on the update loop. See "setScenario" for details */
390 class ScenarioChanger : public Updatable
391 {
392 public:
ScenarioChanger(string script_name,string variation)393     ScenarioChanger(string script_name, string variation)
394     : script_name(script_name), variation(variation)
395     {
396     }
397 
update(float delta)398     virtual void update(float delta)
399     {
400         gameGlobalInfo->variation = variation;
401         gameGlobalInfo->startScenario(script_name);
402         destroy();
403     }
404 private:
405     string script_name;
406     string variation;
407 };
408 
setScenario(lua_State * L)409 static int setScenario(lua_State* L)
410 {
411     string script_name = luaL_checkstring(L, 1);
412     string variation = luaL_optstring(L, 2, "");
413     //This could be called from a currently active scenario script.
414     // Calling GameGlobalInfo::startScenario is unsafe at this point,
415     // as this will destroy the lua state that this function is running in.
416     //So use the ScenarioChanger object which will do the change in the update loop. Which is safe.
417     new ScenarioChanger(script_name, variation);
418     return 0;
419 }
420 /// setScenario(script_name, variation_name)
421 /// Change the current scenario to a different one.
422 REGISTER_SCRIPT_FUNCTION(setScenario);
423 
shutdownGame(lua_State * L)424 static int shutdownGame(lua_State* L)
425 {
426     engine->shutdown();
427     return 0;
428 }
429 /// Shutdown the game.
430 /// Calling this function will close the game. Mainly usefull for a headless server setup.
431 REGISTER_SCRIPT_FUNCTION(shutdownGame);
432 
pauseGame(lua_State * L)433 static int pauseGame(lua_State* L)
434 {
435     engine->setGameSpeed(0.0);
436     return 0;
437 }
438 /// Pause the game
439 /// Calling this function will pause the game. Mainly usefull for a headless server setup.
440 REGISTER_SCRIPT_FUNCTION(pauseGame);
441 
unpauseGame(lua_State * L)442 static int unpauseGame(lua_State* L)
443 {
444     engine->setGameSpeed(1.0);
445     return 0;
446 }
447 /// Pause the game
448 /// Calling this function will pause the game. Mainly usefull for a headless server setup. As the scenario functions are not called when paused.
449 REGISTER_SCRIPT_FUNCTION(unpauseGame);
450 
playSoundFile(lua_State * L)451 static int playSoundFile(lua_State* L)
452 {
453     soundManager->playSound(luaL_checkstring(L, 1));
454     return 0;
455 }
456 /// Play a sound file on the server. Will work with any file supported by SFML (.wav, .ogg, .flac)
457 /// Note that the sound is only played on the server. Not on any of the clients.
458 REGISTER_SCRIPT_FUNCTION(playSoundFile);
459 
returnType(lua_State * L,EScanningComplexity complexity)460 template<> int convert<EScanningComplexity>::returnType(lua_State* L, EScanningComplexity complexity)
461 {
462     switch(complexity)
463     {
464     case SC_None:
465         lua_pushstring(L, "none");
466         return 1;
467     case SC_Simple:
468         lua_pushstring(L, "simple");
469         return 1;
470     case SC_Normal:
471         lua_pushstring(L, "normal");
472         return 1;
473     case SC_Advanced:
474         lua_pushstring(L, "advanced");
475         return 1;
476     default:
477         return 0;
478     }
479 }
480 
getScanningComplexity(lua_State * L)481 static int getScanningComplexity(lua_State* L)
482 {
483     return convert<EScanningComplexity>::returnType(L, gameGlobalInfo->scanning_complexity);
484 }
485 /// Get the scanning complexity setting (returns an EScanningComplexity representation)
486 REGISTER_SCRIPT_FUNCTION(getScanningComplexity);
487 
getHackingDifficulty(lua_State * L)488 static int getHackingDifficulty(lua_State* L)
489 {
490     lua_pushinteger(L, gameGlobalInfo->hacking_difficulty);
491     return 1;
492 }
493 /// Get the hacking difficulty setting (returns an integer between 0 and 3)
494 REGISTER_SCRIPT_FUNCTION(getHackingDifficulty);
495 
returnType(lua_State * L,EHackingGames game)496 template<> int convert<EHackingGames>::returnType(lua_State* L, EHackingGames game)
497 {
498     switch(game)
499     {
500     case HG_Mine:
501         lua_pushstring(L, "mines");
502         return 1;
503     case HG_Lights:
504         lua_pushstring(L, "lights");
505         return 1;
506     case HG_All:
507         lua_pushstring(L, "all");
508         return 1;
509     default:
510         return 0;
511     }
512 }
513 
getHackingGames(lua_State * L)514 static int getHackingGames(lua_State* L)
515 {
516     return convert<EHackingGames>::returnType(L, gameGlobalInfo->hacking_games);
517 }
518 /// Get the hacking games setting (returns an EHackingGames representation)
519 REGISTER_SCRIPT_FUNCTION(getHackingGames);
520 
areBeamShieldFrequenciesUsed(lua_State * L)521 static int areBeamShieldFrequenciesUsed(lua_State* L)
522 {
523     lua_pushboolean(L, gameGlobalInfo->use_beam_shield_frequencies);
524     return 1;
525 }
526 /// returns if the "Beam/Shield Frequencies" setting is enabled
527 REGISTER_SCRIPT_FUNCTION(areBeamShieldFrequenciesUsed);
528 
isPerSystemDamageUsed(lua_State * L)529 static int isPerSystemDamageUsed(lua_State* L)
530 {
531     lua_pushboolean(L, gameGlobalInfo->use_system_damage);
532     return 1;
533 }
534 /// returns if the "Per-System Damage" setting is enabled
535 REGISTER_SCRIPT_FUNCTION(isPerSystemDamageUsed);
536 
isTacticalRadarAllowed(lua_State * L)537 static int isTacticalRadarAllowed(lua_State* L)
538 {
539     lua_pushboolean(L, gameGlobalInfo->allow_main_screen_tactical_radar);
540     return 1;
541 }
542 /// returns if the "Tactical Radar" setting is enabled
543 REGISTER_SCRIPT_FUNCTION(isTacticalRadarAllowed);
544 
isLongRangeRadarAllowed(lua_State * L)545 static int isLongRangeRadarAllowed(lua_State* L)
546 {
547     lua_pushboolean(L, gameGlobalInfo->allow_main_screen_long_range_radar);
548     return 1;
549 }
550 /// returns if the "Long Range Radar" setting is enabled
551 REGISTER_SCRIPT_FUNCTION(isLongRangeRadarAllowed);
552 
onNewPlayerShip(lua_State * L)553 static int onNewPlayerShip(lua_State* L)
554 {
555     int idx = 1;
556     convert<ScriptSimpleCallback>::param(L, idx, gameGlobalInfo->on_new_player_ship);
557     return 0;
558 }
559 /// Register a callback function that is called when a new ship is created on the ship selection screen.
560 REGISTER_SCRIPT_FUNCTION(onNewPlayerShip);
561 
allowNewPlayerShips(lua_State * L)562 static int allowNewPlayerShips(lua_State* L)
563 {
564     gameGlobalInfo->allow_new_player_ships = lua_toboolean(L, 1);
565     return 0;
566 }
567 /// Set if the server is allowed to create new player ships from the ship creation screen.
568 /// allowNewPlayerShip(false) -- disallow new player ships to be created
569 REGISTER_SCRIPT_FUNCTION(allowNewPlayerShips);
570 
getEEVersion(lua_State * L)571 static int getEEVersion(lua_State* L)
572 {
573     lua_pushinteger(L, VERSION_NUMBER);
574     return 1;
575 }
576 /// Get a string with the current version number, like "20191231"
577 REGISTER_SCRIPT_FUNCTION(getEEVersion);