g_main.cpp 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. #include "g_local.h"
  4. #include "bots/bot_includes.h"
  5. CHECK_GCLIENT_INTEGRITY;
  6. CHECK_EDICT_INTEGRITY;
  7. std::mt19937 mt_rand;
  8. game_locals_t game;
  9. level_locals_t level;
  10. local_game_import_t gi;
  11. /*static*/ char local_game_import_t::print_buffer[0x10000];
  12. /*static*/ std::array<char[MAX_INFO_STRING], MAX_LOCALIZATION_ARGS> local_game_import_t::buffers;
  13. /*static*/ std::array<const char*, MAX_LOCALIZATION_ARGS> local_game_import_t::buffer_ptrs;
  14. game_export_t globals;
  15. spawn_temp_t st;
  16. cached_modelindex sm_meat_index;
  17. cached_soundindex snd_fry;
  18. edict_t *g_edicts;
  19. cvar_t *deathmatch;
  20. cvar_t *coop;
  21. cvar_t *skill;
  22. cvar_t *fraglimit;
  23. cvar_t *timelimit;
  24. // ZOID
  25. cvar_t *capturelimit;
  26. cvar_t *g_quick_weapon_switch;
  27. cvar_t *g_instant_weapon_switch;
  28. // ZOID
  29. cvar_t *password;
  30. cvar_t *spectator_password;
  31. cvar_t *needpass;
  32. static cvar_t *maxclients;
  33. cvar_t *maxspectators;
  34. static cvar_t *maxentities;
  35. cvar_t *g_select_empty;
  36. cvar_t *sv_dedicated;
  37. cvar_t *filterban;
  38. cvar_t *sv_maxvelocity;
  39. cvar_t *sv_gravity;
  40. cvar_t *g_skipViewModifiers;
  41. cvar_t *sv_rollspeed;
  42. cvar_t *sv_rollangle;
  43. cvar_t *gun_x;
  44. cvar_t *gun_y;
  45. cvar_t *gun_z;
  46. cvar_t *run_pitch;
  47. cvar_t *run_roll;
  48. cvar_t *bob_up;
  49. cvar_t *bob_pitch;
  50. cvar_t *bob_roll;
  51. cvar_t *sv_cheats;
  52. cvar_t *g_debug_monster_paths;
  53. cvar_t *g_debug_monster_kills;
  54. cvar_t *bot_debug_follow_actor;
  55. cvar_t *bot_debug_move_to_point;
  56. cvar_t *flood_msgs;
  57. cvar_t *flood_persecond;
  58. cvar_t *flood_waitdelay;
  59. cvar_t *sv_stopspeed; // PGM (this was a define in g_phys.c)
  60. cvar_t *g_strict_saves;
  61. // ROGUE cvars
  62. cvar_t *gamerules;
  63. cvar_t *huntercam;
  64. cvar_t *g_dm_strong_mines;
  65. cvar_t *g_dm_random_items;
  66. // ROGUE
  67. // [Kex]
  68. cvar_t* g_instagib;
  69. cvar_t* g_coop_player_collision;
  70. cvar_t* g_coop_squad_respawn;
  71. cvar_t* g_coop_enable_lives;
  72. cvar_t* g_coop_num_lives;
  73. cvar_t* g_coop_instanced_items;
  74. cvar_t* g_allow_grapple;
  75. cvar_t* g_grapple_fly_speed;
  76. cvar_t* g_grapple_pull_speed;
  77. cvar_t* g_grapple_damage;
  78. cvar_t* g_coop_health_scaling;
  79. cvar_t* g_weapon_respawn_time;
  80. // dm"flags"
  81. cvar_t* g_no_health;
  82. cvar_t* g_no_items;
  83. cvar_t* g_dm_weapons_stay;
  84. cvar_t* g_dm_no_fall_damage;
  85. cvar_t* g_dm_instant_items;
  86. cvar_t* g_dm_same_level;
  87. cvar_t* g_friendly_fire;
  88. cvar_t* g_dm_force_respawn;
  89. cvar_t* g_dm_force_respawn_time;
  90. cvar_t* g_dm_spawn_farthest;
  91. cvar_t* g_no_armor;
  92. cvar_t* g_dm_allow_exit;
  93. cvar_t* g_infinite_ammo;
  94. cvar_t* g_dm_no_quad_drop;
  95. cvar_t* g_dm_no_quadfire_drop;
  96. cvar_t* g_no_mines;
  97. cvar_t* g_dm_no_stack_double;
  98. cvar_t* g_no_nukes;
  99. cvar_t* g_no_spheres;
  100. cvar_t* g_teamplay_armor_protect;
  101. cvar_t* g_allow_techs;
  102. cvar_t* g_start_items;
  103. cvar_t* g_map_list;
  104. cvar_t* g_map_list_shuffle;
  105. cvar_t *g_lag_compensation;
  106. cvar_t *sv_airaccelerate;
  107. cvar_t *g_damage_scale;
  108. cvar_t *g_disable_player_collision;
  109. cvar_t *ai_damage_scale;
  110. cvar_t *ai_model_scale;
  111. cvar_t *ai_allow_dm_spawn;
  112. cvar_t *ai_movement_disabled;
  113. static cvar_t *g_frames_per_frame;
  114. void SpawnEntities(const char *mapname, const char *entities, const char *spawnpoint);
  115. void ClientThink(edict_t *ent, usercmd_t *cmd);
  116. edict_t *ClientChooseSlot(const char *userinfo, const char *social_id, bool isBot, edict_t **ignore, size_t num_ignore, bool cinematic);
  117. bool ClientConnect(edict_t *ent, char *userinfo, const char *social_id, bool isBot);
  118. char *WriteGameJson(bool autosave, size_t *out_size);
  119. void ReadGameJson(const char *jsonString);
  120. char *WriteLevelJson(bool transition, size_t *out_size);
  121. void ReadLevelJson(const char *jsonString);
  122. bool G_CanSave();
  123. void ClientDisconnect(edict_t *ent);
  124. void ClientBegin(edict_t *ent);
  125. void ClientCommand(edict_t *ent);
  126. void G_RunFrame(bool main_loop);
  127. void G_PrepFrame();
  128. void InitSave();
  129. #include <chrono>
  130. /*
  131. ============
  132. PreInitGame
  133. This will be called when the dll is first loaded, which
  134. only happens when a new game is started or a save game
  135. is loaded.
  136. ============
  137. */
  138. void PreInitGame()
  139. {
  140. maxclients = gi.cvar("maxclients", G_Fmt("{}", MAX_SPLIT_PLAYERS).data(), CVAR_SERVERINFO | CVAR_LATCH);
  141. deathmatch = gi.cvar("deathmatch", "0", CVAR_LATCH);
  142. coop = gi.cvar("coop", "0", CVAR_LATCH);
  143. teamplay = gi.cvar("teamplay", "0", CVAR_LATCH);
  144. // ZOID
  145. CTFInit();
  146. // ZOID
  147. // ZOID
  148. // This gamemode only supports deathmatch
  149. if (ctf->integer)
  150. {
  151. if (!deathmatch->integer)
  152. {
  153. gi.Com_Print("Forcing deathmatch.\n");
  154. gi.cvar_set("deathmatch", "1");
  155. }
  156. // force coop off
  157. if (coop->integer)
  158. gi.cvar_set("coop", "0");
  159. // force tdm off
  160. if (teamplay->integer)
  161. gi.cvar_set("teamplay", "0");
  162. }
  163. if (teamplay->integer)
  164. {
  165. if (!deathmatch->integer)
  166. {
  167. gi.Com_Print("Forcing deathmatch.\n");
  168. gi.cvar_set("deathmatch", "1");
  169. }
  170. // force coop off
  171. if (coop->integer)
  172. gi.cvar_set("coop", "0");
  173. }
  174. // ZOID
  175. }
  176. /*
  177. ============
  178. InitGame
  179. Called after PreInitGame when the game has set up cvars.
  180. ============
  181. */
  182. void InitGame()
  183. {
  184. gi.Com_Print("==== InitGame ====\n");
  185. InitSave();
  186. // seed RNG
  187. mt_rand.seed((uint32_t) std::chrono::system_clock::now().time_since_epoch().count());
  188. gun_x = gi.cvar("gun_x", "0", CVAR_NOFLAGS);
  189. gun_y = gi.cvar("gun_y", "0", CVAR_NOFLAGS);
  190. gun_z = gi.cvar("gun_z", "0", CVAR_NOFLAGS);
  191. // FIXME: sv_ prefix is wrong for these
  192. sv_rollspeed = gi.cvar("sv_rollspeed", "200", CVAR_NOFLAGS);
  193. sv_rollangle = gi.cvar("sv_rollangle", "2", CVAR_NOFLAGS);
  194. sv_maxvelocity = gi.cvar("sv_maxvelocity", "2000", CVAR_NOFLAGS);
  195. sv_gravity = gi.cvar("sv_gravity", "800", CVAR_NOFLAGS);
  196. g_skipViewModifiers = gi.cvar("g_skipViewModifiers", "0", CVAR_NOSET);
  197. sv_stopspeed = gi.cvar("sv_stopspeed", "100", CVAR_NOFLAGS); // PGM - was #define in g_phys.c
  198. // ROGUE
  199. huntercam = gi.cvar("huntercam", "1", CVAR_SERVERINFO | CVAR_LATCH);
  200. g_dm_strong_mines = gi.cvar("g_dm_strong_mines", "0", CVAR_NOFLAGS);
  201. g_dm_random_items = gi.cvar("g_dm_random_items", "0", CVAR_NOFLAGS);
  202. // ROGUE
  203. // [Kex] Instagib
  204. g_instagib = gi.cvar("g_instagib", "0", CVAR_NOFLAGS);
  205. // [Paril-KEX]
  206. g_coop_player_collision = gi.cvar("g_coop_player_collision", "0", CVAR_LATCH);
  207. g_coop_squad_respawn = gi.cvar("g_coop_squad_respawn", "1", CVAR_LATCH);
  208. g_coop_enable_lives = gi.cvar("g_coop_enable_lives", "0", CVAR_LATCH);
  209. g_coop_num_lives = gi.cvar("g_coop_num_lives", "2", CVAR_LATCH);
  210. g_coop_instanced_items = gi.cvar("g_coop_instanced_items", "1", CVAR_LATCH);
  211. g_allow_grapple = gi.cvar("g_allow_grapple", "auto", CVAR_NOFLAGS);
  212. g_grapple_fly_speed = gi.cvar("g_grapple_fly_speed", G_Fmt("{}", CTF_DEFAULT_GRAPPLE_SPEED).data(), CVAR_NOFLAGS);
  213. g_grapple_pull_speed = gi.cvar("g_grapple_pull_speed", G_Fmt("{}", CTF_DEFAULT_GRAPPLE_PULL_SPEED).data(), CVAR_NOFLAGS);
  214. g_grapple_damage = gi.cvar("g_grapple_damage", "10", CVAR_NOFLAGS);
  215. g_debug_monster_paths = gi.cvar("g_debug_monster_paths", "0", CVAR_NOFLAGS);
  216. g_debug_monster_kills = gi.cvar("g_debug_monster_kills", "0", CVAR_LATCH);
  217. bot_debug_follow_actor = gi.cvar("bot_debug_follow_actor", "0", CVAR_NOFLAGS);
  218. bot_debug_move_to_point = gi.cvar("bot_debug_move_to_point", "0", CVAR_NOFLAGS);
  219. // noset vars
  220. sv_dedicated = gi.cvar("dedicated", "0", CVAR_NOSET);
  221. // latched vars
  222. sv_cheats = gi.cvar("cheats",
  223. #if defined(_DEBUG)
  224. "1"
  225. #else
  226. "0"
  227. #endif
  228. , CVAR_SERVERINFO | CVAR_LATCH);
  229. gi.cvar("gamename", GAMEVERSION, CVAR_SERVERINFO | CVAR_LATCH);
  230. maxspectators = gi.cvar("maxspectators", "4", CVAR_SERVERINFO);
  231. skill = gi.cvar("skill", "1", CVAR_LATCH);
  232. maxentities = gi.cvar("maxentities", G_Fmt("{}", MAX_EDICTS).data(), CVAR_LATCH);
  233. gamerules = gi.cvar("gamerules", "0", CVAR_LATCH); // PGM
  234. // change anytime vars
  235. fraglimit = gi.cvar("fraglimit", "0", CVAR_SERVERINFO);
  236. timelimit = gi.cvar("timelimit", "0", CVAR_SERVERINFO);
  237. // ZOID
  238. capturelimit = gi.cvar("capturelimit", "0", CVAR_SERVERINFO);
  239. g_quick_weapon_switch = gi.cvar("g_quick_weapon_switch", "1", CVAR_LATCH);
  240. g_instant_weapon_switch = gi.cvar("g_instant_weapon_switch", "0", CVAR_LATCH);
  241. // ZOID
  242. password = gi.cvar("password", "", CVAR_USERINFO);
  243. spectator_password = gi.cvar("spectator_password", "", CVAR_USERINFO);
  244. needpass = gi.cvar("needpass", "0", CVAR_SERVERINFO);
  245. filterban = gi.cvar("filterban", "1", CVAR_NOFLAGS);
  246. g_select_empty = gi.cvar("g_select_empty", "0", CVAR_ARCHIVE);
  247. run_pitch = gi.cvar("run_pitch", "0.002", CVAR_NOFLAGS);
  248. run_roll = gi.cvar("run_roll", "0.005", CVAR_NOFLAGS);
  249. bob_up = gi.cvar("bob_up", "0.005", CVAR_NOFLAGS);
  250. bob_pitch = gi.cvar("bob_pitch", "0.002", CVAR_NOFLAGS);
  251. bob_roll = gi.cvar("bob_roll", "0.002", CVAR_NOFLAGS);
  252. // flood control
  253. flood_msgs = gi.cvar("flood_msgs", "4", CVAR_NOFLAGS);
  254. flood_persecond = gi.cvar("flood_persecond", "4", CVAR_NOFLAGS);
  255. flood_waitdelay = gi.cvar("flood_waitdelay", "10", CVAR_NOFLAGS);
  256. g_strict_saves = gi.cvar("g_strict_saves", "1", CVAR_NOFLAGS);
  257. sv_airaccelerate = gi.cvar("sv_airaccelerate", "0", CVAR_NOFLAGS);
  258. g_damage_scale = gi.cvar("g_damage_scale", "1", CVAR_NOFLAGS);
  259. g_disable_player_collision = gi.cvar("g_disable_player_collision", "0", CVAR_NOFLAGS);
  260. ai_damage_scale = gi.cvar("ai_damage_scale", "1", CVAR_NOFLAGS);
  261. ai_model_scale = gi.cvar("ai_model_scale", "0", CVAR_NOFLAGS);
  262. ai_allow_dm_spawn = gi.cvar("ai_allow_dm_spawn", "0", CVAR_NOFLAGS);
  263. ai_movement_disabled = gi.cvar("ai_movement_disabled", "0", CVAR_NOFLAGS);
  264. g_frames_per_frame = gi.cvar("g_frames_per_frame", "1", CVAR_NOFLAGS);
  265. g_coop_health_scaling = gi.cvar("g_coop_health_scaling", "0", CVAR_LATCH);
  266. g_weapon_respawn_time = gi.cvar("g_weapon_respawn_time", "30", CVAR_NOFLAGS);
  267. // dm "flags"
  268. g_no_health = gi.cvar("g_no_health", "0", CVAR_NOFLAGS);
  269. g_no_items = gi.cvar("g_no_items", "0", CVAR_NOFLAGS);
  270. g_dm_weapons_stay = gi.cvar("g_dm_weapons_stay", "0", CVAR_NOFLAGS);
  271. g_dm_no_fall_damage = gi.cvar("g_dm_no_fall_damage", "0", CVAR_NOFLAGS);
  272. g_dm_instant_items = gi.cvar("g_dm_instant_items", "1", CVAR_NOFLAGS);
  273. g_dm_same_level = gi.cvar("g_dm_same_level", "0", CVAR_NOFLAGS);
  274. g_friendly_fire = gi.cvar("g_friendly_fire", "0", CVAR_NOFLAGS);
  275. g_dm_force_respawn = gi.cvar("g_dm_force_respawn", "0", CVAR_NOFLAGS);
  276. g_dm_force_respawn_time = gi.cvar("g_dm_force_respawn_time", "0", CVAR_NOFLAGS);
  277. g_dm_spawn_farthest = gi.cvar("g_dm_spawn_farthest", "1", CVAR_NOFLAGS);
  278. g_no_armor = gi.cvar("g_no_armor", "0", CVAR_NOFLAGS);
  279. g_dm_allow_exit = gi.cvar("g_dm_allow_exit", "0", CVAR_NOFLAGS);
  280. g_infinite_ammo = gi.cvar("g_infinite_ammo", "0", CVAR_LATCH);
  281. g_dm_no_quad_drop = gi.cvar("g_dm_no_quad_drop", "0", CVAR_NOFLAGS);
  282. g_dm_no_quadfire_drop = gi.cvar("g_dm_no_quadfire_drop", "0", CVAR_NOFLAGS);
  283. g_no_mines = gi.cvar("g_no_mines", "0", CVAR_NOFLAGS);
  284. g_dm_no_stack_double = gi.cvar("g_dm_no_stack_double", "0", CVAR_NOFLAGS);
  285. g_no_nukes = gi.cvar("g_no_nukes", "0", CVAR_NOFLAGS);
  286. g_no_spheres = gi.cvar("g_no_spheres", "0", CVAR_NOFLAGS);
  287. g_teamplay_force_join = gi.cvar("g_teamplay_force_join", "0", CVAR_NOFLAGS);
  288. g_teamplay_armor_protect = gi.cvar("g_teamplay_armor_protect", "0", CVAR_NOFLAGS);
  289. g_allow_techs = gi.cvar("g_allow_techs", "auto", CVAR_NOFLAGS);
  290. g_start_items = gi.cvar("g_start_items", "", CVAR_LATCH);
  291. g_map_list = gi.cvar("g_map_list", "", CVAR_NOFLAGS);
  292. g_map_list_shuffle = gi.cvar("g_map_list_shuffle", "0", CVAR_NOFLAGS);
  293. g_lag_compensation = gi.cvar("g_lag_compensation", "1", CVAR_NOFLAGS);
  294. // items
  295. InitItems();
  296. game = {};
  297. // initialize all entities for this game
  298. game.maxentities = maxentities->integer;
  299. g_edicts = (edict_t *) gi.TagMalloc(game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
  300. globals.edicts = g_edicts;
  301. globals.max_edicts = game.maxentities;
  302. // initialize all clients for this game
  303. game.maxclients = maxclients->integer;
  304. game.clients = (gclient_t *) gi.TagMalloc(game.maxclients * sizeof(game.clients[0]), TAG_GAME);
  305. globals.num_edicts = game.maxclients + 1;
  306. //======
  307. // ROGUE
  308. if (gamerules->integer)
  309. InitGameRules(); // if there are game rules to set up, do so now.
  310. // ROGUE
  311. //======
  312. // how far back we should support lag origins for
  313. game.max_lag_origins = 20 * (0.1f / gi.frame_time_s);
  314. game.lag_origins = (vec3_t *) gi.TagMalloc(game.maxclients * sizeof(vec3_t) * game.max_lag_origins, TAG_GAME);
  315. }
  316. //===================================================================
  317. void ShutdownGame()
  318. {
  319. gi.Com_Print("==== ShutdownGame ====\n");
  320. gi.FreeTags(TAG_LEVEL);
  321. gi.FreeTags(TAG_GAME);
  322. }
  323. static void *G_GetExtension(const char *name)
  324. {
  325. return nullptr;
  326. }
  327. const shadow_light_data_t *GetShadowLightData(int32_t entity_number);
  328. gtime_t FRAME_TIME_S;
  329. gtime_t FRAME_TIME_MS;
  330. /*
  331. =================
  332. GetGameAPI
  333. Returns a pointer to the structure with all entry points
  334. and global variables
  335. =================
  336. */
  337. Q2GAME_API game_export_t *GetGameAPI(game_import_t *import)
  338. {
  339. gi = *import;
  340. FRAME_TIME_S = FRAME_TIME_MS = gtime_t::from_ms(gi.frame_time_ms);
  341. globals.apiversion = GAME_API_VERSION;
  342. globals.PreInit = PreInitGame;
  343. globals.Init = InitGame;
  344. globals.Shutdown = ShutdownGame;
  345. globals.SpawnEntities = SpawnEntities;
  346. globals.WriteGameJson = WriteGameJson;
  347. globals.ReadGameJson = ReadGameJson;
  348. globals.WriteLevelJson = WriteLevelJson;
  349. globals.ReadLevelJson = ReadLevelJson;
  350. globals.CanSave = G_CanSave;
  351. globals.Pmove = Pmove;
  352. globals.GetExtension = G_GetExtension;
  353. globals.ClientChooseSlot = ClientChooseSlot;
  354. globals.ClientThink = ClientThink;
  355. globals.ClientConnect = ClientConnect;
  356. globals.ClientUserinfoChanged = ClientUserinfoChanged;
  357. globals.ClientDisconnect = ClientDisconnect;
  358. globals.ClientBegin = ClientBegin;
  359. globals.ClientCommand = ClientCommand;
  360. globals.RunFrame = G_RunFrame;
  361. globals.PrepFrame = G_PrepFrame;
  362. globals.ServerCommand = ServerCommand;
  363. globals.Bot_SetWeapon = Bot_SetWeapon;
  364. globals.Bot_TriggerEdict = Bot_TriggerEdict;
  365. globals.Bot_GetItemID = Bot_GetItemID;
  366. globals.Bot_UseItem = Bot_UseItem;
  367. globals.Edict_ForceLookAtPoint = Edict_ForceLookAtPoint;
  368. globals.Bot_PickedUpItem = Bot_PickedUpItem;
  369. globals.Entity_IsVisibleToPlayer = Entity_IsVisibleToPlayer;
  370. globals.GetShadowLightData = GetShadowLightData;
  371. globals.edict_size = sizeof(edict_t);
  372. return &globals;
  373. }
  374. //======================================================================
  375. /*
  376. =================
  377. ClientEndServerFrames
  378. =================
  379. */
  380. void ClientEndServerFrames()
  381. {
  382. edict_t *ent;
  383. // calc the player views now that all pushing
  384. // and damage has been added
  385. for (uint32_t i = 0; i < game.maxclients; i++)
  386. {
  387. ent = g_edicts + 1 + i;
  388. if (!ent->inuse || !ent->client)
  389. continue;
  390. ClientEndServerFrame(ent);
  391. }
  392. }
  393. /*
  394. =================
  395. CreateTargetChangeLevel
  396. Returns the created target changelevel
  397. =================
  398. */
  399. edict_t *CreateTargetChangeLevel(const char *map)
  400. {
  401. edict_t *ent;
  402. ent = G_Spawn();
  403. ent->classname = "target_changelevel";
  404. Q_strlcpy(level.nextmap, map, sizeof(level.nextmap));
  405. ent->map = level.nextmap;
  406. return ent;
  407. }
  408. inline std::vector<std::string> str_split(const std::string_view &str, char by)
  409. {
  410. std::vector<std::string> out;
  411. size_t start, end = 0;
  412. while ((start = str.find_first_not_of(by, end)) != std::string_view::npos)
  413. {
  414. end = str.find(by, start);
  415. out.push_back(std::string{str.substr(start, end - start)});
  416. }
  417. return out;
  418. }
  419. /*
  420. =================
  421. EndDMLevel
  422. The timelimit or fraglimit has been exceeded
  423. =================
  424. */
  425. void EndDMLevel()
  426. {
  427. edict_t *ent;
  428. // stay on same level flag
  429. if (g_dm_same_level->integer)
  430. {
  431. BeginIntermission(CreateTargetChangeLevel(level.mapname));
  432. return;
  433. }
  434. if (*level.forcemap)
  435. {
  436. BeginIntermission(CreateTargetChangeLevel(level.forcemap));
  437. return;
  438. }
  439. // see if it's in the map list
  440. if (*g_map_list->string)
  441. {
  442. const char *str = g_map_list->string;
  443. char first_map[MAX_QPATH] { 0 };
  444. char *map;
  445. while (1)
  446. {
  447. map = COM_ParseEx(&str, " ");
  448. if (!*map)
  449. break;
  450. if (Q_strcasecmp(map, level.mapname) == 0)
  451. {
  452. // it's in the list, go to the next one
  453. map = COM_ParseEx(&str, " ");
  454. if (!*map)
  455. {
  456. // end of list, go to first one
  457. if (!first_map[0]) // there isn't a first one, same level
  458. {
  459. BeginIntermission(CreateTargetChangeLevel(level.mapname));
  460. return;
  461. }
  462. else
  463. {
  464. // [Paril-KEX] re-shuffle if necessary
  465. if (g_map_list_shuffle->integer)
  466. {
  467. auto values = str_split(g_map_list->string, ' ');
  468. if (values.size() == 1)
  469. {
  470. // meh
  471. BeginIntermission(CreateTargetChangeLevel(level.mapname));
  472. return;
  473. }
  474. std::shuffle(values.begin(), values.end(), mt_rand);
  475. // if the current map is the map at the front, push it to the end
  476. if (values[0] == level.mapname)
  477. std::swap(values[0], values[values.size() - 1]);
  478. gi.cvar_forceset("g_map_list", fmt::format("{}", join_strings(values, " ")).data());
  479. BeginIntermission(CreateTargetChangeLevel(values[0].c_str()));
  480. return;
  481. }
  482. BeginIntermission(CreateTargetChangeLevel(first_map));
  483. return;
  484. }
  485. }
  486. else
  487. {
  488. BeginIntermission(CreateTargetChangeLevel(map));
  489. return;
  490. }
  491. }
  492. if (!first_map[0])
  493. Q_strlcpy(first_map, map, sizeof(first_map));
  494. }
  495. }
  496. if (level.nextmap[0]) // go to a specific map
  497. {
  498. BeginIntermission(CreateTargetChangeLevel(level.nextmap));
  499. return;
  500. }
  501. // search for a changelevel
  502. ent = G_FindByString<&edict_t::classname>(nullptr, "target_changelevel");
  503. if (!ent)
  504. { // the map designer didn't include a changelevel,
  505. // so create a fake ent that goes back to the same level
  506. BeginIntermission(CreateTargetChangeLevel(level.mapname));
  507. return;
  508. }
  509. BeginIntermission(ent);
  510. }
  511. /*
  512. =================
  513. CheckNeedPass
  514. =================
  515. */
  516. void CheckNeedPass()
  517. {
  518. int need;
  519. static int32_t password_modified, spectator_password_modified;
  520. // if password or spectator_password has changed, update needpass
  521. // as needed
  522. if (Cvar_WasModified(password, password_modified) || Cvar_WasModified(spectator_password, spectator_password_modified))
  523. {
  524. need = 0;
  525. if (*password->string && Q_strcasecmp(password->string, "none"))
  526. need |= 1;
  527. if (*spectator_password->string && Q_strcasecmp(spectator_password->string, "none"))
  528. need |= 2;
  529. gi.cvar_set("needpass", G_Fmt("{}", need).data());
  530. }
  531. }
  532. /*
  533. =================
  534. CheckDMRules
  535. =================
  536. */
  537. void CheckDMRules()
  538. {
  539. gclient_t *cl;
  540. if (level.intermissiontime)
  541. return;
  542. if (!deathmatch->integer)
  543. return;
  544. // ZOID
  545. if (ctf->integer && CTFCheckRules())
  546. {
  547. EndDMLevel();
  548. return;
  549. }
  550. if (CTFInMatch())
  551. return; // no checking in match mode
  552. // ZOID
  553. //=======
  554. // ROGUE
  555. if (gamerules->integer && DMGame.CheckDMRules)
  556. {
  557. if (DMGame.CheckDMRules())
  558. return;
  559. }
  560. // ROGUE
  561. //=======
  562. if (timelimit->value)
  563. {
  564. if (level.time >= gtime_t::from_min(timelimit->value))
  565. {
  566. gi.LocBroadcast_Print(PRINT_HIGH, "$g_timelimit_hit");
  567. EndDMLevel();
  568. return;
  569. }
  570. }
  571. if (fraglimit->integer)
  572. {
  573. // [Paril-KEX]
  574. if (teamplay->integer)
  575. {
  576. CheckEndTDMLevel();
  577. return;
  578. }
  579. for (uint32_t i = 0; i < game.maxclients; i++)
  580. {
  581. cl = game.clients + i;
  582. if (!g_edicts[i + 1].inuse)
  583. continue;
  584. if (cl->resp.score >= fraglimit->integer)
  585. {
  586. gi.LocBroadcast_Print(PRINT_HIGH, "$g_fraglimit_hit");
  587. EndDMLevel();
  588. return;
  589. }
  590. }
  591. }
  592. }
  593. /*
  594. =============
  595. ExitLevel
  596. =============
  597. */
  598. void ExitLevel()
  599. {
  600. // [Paril-KEX] N64 fade
  601. if (level.intermission_fade)
  602. {
  603. level.intermission_fade_time = level.time + 1.3_sec;
  604. level.intermission_fading = true;
  605. return;
  606. }
  607. ClientEndServerFrames();
  608. level.exitintermission = 0;
  609. level.intermissiontime = 0_ms;
  610. // [Paril-KEX] support for intermission completely wiping players
  611. // back to default stuff
  612. if (level.intermission_clear)
  613. {
  614. level.intermission_clear = false;
  615. for (uint32_t i = 0; i < game.maxclients; i++)
  616. {
  617. // [Kex] Maintain user info to keep the player skin.
  618. char userinfo[MAX_INFO_STRING];
  619. memcpy(userinfo, game.clients[i].pers.userinfo, sizeof(userinfo));
  620. game.clients[i].pers = game.clients[i].resp.coop_respawn = {};
  621. g_edicts[i + 1].health = 0; // this should trip the power armor, etc to reset as well
  622. memcpy(game.clients[i].pers.userinfo, userinfo, sizeof(userinfo));
  623. memcpy(game.clients[i].resp.coop_respawn.userinfo, userinfo, sizeof(userinfo));
  624. }
  625. }
  626. // [Paril-KEX] end of unit, so clear level trackers
  627. if (level.intermission_eou)
  628. {
  629. game.level_entries = {};
  630. // give all players their lives back
  631. if (g_coop_enable_lives->integer)
  632. for (auto player : active_players())
  633. player->client->pers.lives = g_coop_num_lives->integer + 1;
  634. }
  635. if (CTFNextMap())
  636. return;
  637. if (level.changemap == nullptr)
  638. {
  639. gi.Com_Error("Got null changemap when trying to exit level. Was a trigger_changelevel configured correctly?");
  640. return;
  641. }
  642. // for N64 mainly, but if we're directly changing to "victorXXX.pcx" then
  643. // end game
  644. size_t start_offset = (level.changemap[0] == '*' ? 1 : 0);
  645. if (strlen(level.changemap) > (6 + start_offset) &&
  646. !Q_strncasecmp(level.changemap + start_offset, "victor", 6) &&
  647. !Q_strncasecmp(level.changemap + strlen(level.changemap) - 4, ".pcx", 4))
  648. gi.AddCommandString(G_Fmt("endgame \"{}\"\n", level.changemap + start_offset).data());
  649. else
  650. gi.AddCommandString(G_Fmt("gamemap \"{}\"\n", level.changemap).data());
  651. level.changemap = nullptr;
  652. }
  653. static void G_CheckCvars()
  654. {
  655. if (Cvar_WasModified(sv_airaccelerate, game.airacceleration_modified))
  656. {
  657. // [Paril-KEX] air accel handled by game DLL now, and allow
  658. // it to be changed in sp/coop
  659. gi.configstring(CS_AIRACCEL, G_Fmt("{}", sv_airaccelerate->integer).data());
  660. pm_config.airaccel = sv_airaccelerate->integer;
  661. }
  662. if (Cvar_WasModified(sv_gravity, game.gravity_modified))
  663. level.gravity = sv_gravity->value;
  664. }
  665. static bool G_AnyDeadPlayersWithoutLives()
  666. {
  667. for (auto player : active_players())
  668. if (player->health <= 0 && !player->client->pers.lives)
  669. return true;
  670. return false;
  671. }
  672. /*
  673. ================
  674. G_RunFrame
  675. Advances the world by 0.1 seconds
  676. ================
  677. */
  678. inline void G_RunFrame_(bool main_loop)
  679. {
  680. level.in_frame = true;
  681. G_CheckCvars();
  682. Bot_UpdateDebug();
  683. level.time += FRAME_TIME_MS;
  684. if (level.intermission_fading)
  685. {
  686. if (level.intermission_fade_time > level.time)
  687. {
  688. float alpha = clamp(1.0f - (level.intermission_fade_time - level.time - 300_ms).seconds(), 0.f, 1.f);
  689. for (auto player : active_players())
  690. player->client->ps.screen_blend = { 0, 0, 0, alpha };
  691. }
  692. else
  693. {
  694. level.intermission_fade = level.intermission_fading = false;
  695. ExitLevel();
  696. }
  697. level.in_frame = false;
  698. return;
  699. }
  700. edict_t *ent;
  701. // exit intermissions
  702. if (level.exitintermission)
  703. {
  704. ExitLevel();
  705. level.in_frame = false;
  706. return;
  707. }
  708. // reload the map start save if restart time is set (all players are dead)
  709. if (level.coop_level_restart_time > 0_ms && level.time > level.coop_level_restart_time)
  710. {
  711. ClientEndServerFrames();
  712. gi.AddCommandString("restart_level\n");
  713. }
  714. // clear client coop respawn states; this is done
  715. // early since it may be set multiple times for different
  716. // players
  717. if (coop->integer && (g_coop_enable_lives->integer || g_coop_squad_respawn->integer))
  718. {
  719. for (auto player : active_players())
  720. {
  721. if (player->client->respawn_time >= level.time)
  722. player->client->coop_respawn_state = COOP_RESPAWN_WAITING;
  723. else if (g_coop_enable_lives->integer && player->health <= 0 && player->client->pers.lives == 0)
  724. player->client->coop_respawn_state = COOP_RESPAWN_NO_LIVES;
  725. else if (g_coop_enable_lives->integer && G_AnyDeadPlayersWithoutLives())
  726. player->client->coop_respawn_state = COOP_RESPAWN_NO_LIVES;
  727. else
  728. player->client->coop_respawn_state = COOP_RESPAWN_NONE;
  729. }
  730. }
  731. //
  732. // treat each object in turn
  733. // even the world gets a chance to think
  734. //
  735. ent = &g_edicts[0];
  736. for (uint32_t i = 0; i < globals.num_edicts; i++, ent++)
  737. {
  738. if (!ent->inuse)
  739. {
  740. // defer removing client info so that disconnected, etc works
  741. if (i > 0 && i <= game.maxclients)
  742. {
  743. if (ent->timestamp && level.time < ent->timestamp)
  744. {
  745. int32_t playernum = ent - g_edicts - 1;
  746. gi.configstring(CS_PLAYERSKINS + playernum, "");
  747. ent->timestamp = 0_sec;
  748. }
  749. }
  750. continue;
  751. }
  752. level.current_entity = ent;
  753. // Paril: RF_BEAM entities update their old_origin by hand.
  754. if (!(ent->s.renderfx & RF_BEAM))
  755. ent->s.old_origin = ent->s.origin;
  756. // if the ground entity moved, make sure we are still on it
  757. if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount))
  758. {
  759. contents_t mask = G_GetClipMask(ent);
  760. if (!(ent->flags & (FL_SWIM | FL_FLY)) && (ent->svflags & SVF_MONSTER))
  761. {
  762. ent->groundentity = nullptr;
  763. M_CheckGround(ent, mask);
  764. }
  765. else
  766. {
  767. // if it's still 1 point below us, we're good
  768. trace_t tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin + ent->gravityVector, ent,
  769. mask);
  770. if (tr.startsolid || tr.allsolid || tr.ent != ent->groundentity)
  771. ent->groundentity = nullptr;
  772. else
  773. ent->groundentity_linkcount = ent->groundentity->linkcount;
  774. }
  775. }
  776. Entity_UpdateState( ent );
  777. if (i > 0 && i <= game.maxclients)
  778. {
  779. ClientBeginServerFrame(ent);
  780. continue;
  781. }
  782. G_RunEntity(ent);
  783. }
  784. // see if it is time to end a deathmatch
  785. CheckDMRules();
  786. // see if needpass needs updated
  787. CheckNeedPass();
  788. if (coop->integer && (g_coop_enable_lives->integer || g_coop_squad_respawn->integer))
  789. {
  790. // rarely, we can see a flash of text if all players respawned
  791. // on some other player, so if everybody is now alive we'll reset
  792. // back to empty
  793. bool reset_coop_respawn = true;
  794. for (auto player : active_players())
  795. {
  796. if (player->health >= 0)
  797. {
  798. reset_coop_respawn = false;
  799. break;
  800. }
  801. }
  802. if (reset_coop_respawn)
  803. {
  804. for (auto player : active_players())
  805. player->client->coop_respawn_state = COOP_RESPAWN_NONE;
  806. }
  807. }
  808. // build the playerstate_t structures for all players
  809. ClientEndServerFrames();
  810. // [Paril-KEX] if not in intermission and player 1 is loaded in
  811. // the game as an entity, increase timer on current entry
  812. if (level.entry && !level.intermissiontime && g_edicts[1].inuse && g_edicts[1].client->pers.connected)
  813. level.entry->time += FRAME_TIME_S;
  814. // [Paril-KEX] run monster pains now
  815. for (uint32_t i = 0; i < globals.num_edicts + 1 + game.maxclients + BODY_QUEUE_SIZE; i++)
  816. {
  817. edict_t *e = &g_edicts[i];
  818. if (!e->inuse || !(e->svflags & SVF_MONSTER))
  819. continue;
  820. M_ProcessPain(e);
  821. }
  822. level.in_frame = false;
  823. }
  824. inline bool G_AnyPlayerSpawned()
  825. {
  826. for (auto player : active_players())
  827. if (player->client && player->client->pers.spawned)
  828. return true;
  829. return false;
  830. }
  831. void G_RunFrame(bool main_loop)
  832. {
  833. if (main_loop && !G_AnyPlayerSpawned())
  834. return;
  835. for (int32_t i = 0; i < g_frames_per_frame->integer; i++)
  836. G_RunFrame_(main_loop);
  837. // match details.. only bother if there's at least 1 player in-game
  838. // and not already end of game
  839. if (G_AnyPlayerSpawned() && !level.intermissiontime)
  840. {
  841. constexpr gtime_t MATCH_REPORT_TIME = 45_sec;
  842. if (level.time - level.next_match_report > MATCH_REPORT_TIME)
  843. {
  844. level.next_match_report = level.time + MATCH_REPORT_TIME;
  845. G_ReportMatchDetails(false);
  846. }
  847. }
  848. }
  849. /*
  850. ================
  851. G_PrepFrame
  852. This has to be done before the world logic, because
  853. player processing happens outside RunFrame
  854. ================
  855. */
  856. void G_PrepFrame()
  857. {
  858. for (uint32_t i = 0; i < globals.num_edicts; i++)
  859. g_edicts[i].s.event = EV_NONE;
  860. for (auto player : active_players())
  861. player->client->ps.stats[STAT_HIT_MARKER] = 0;
  862. globals.server_flags &= ~SERVER_FLAG_INTERMISSION;
  863. if ( level.intermissiontime ) {
  864. globals.server_flags |= SERVER_FLAG_INTERMISSION;
  865. }
  866. }