UFO: Alien Invasion
g_client.cpp
Go to the documentation of this file.
1
20/*
21Copyright (C) 2002-2022 UFO: Alien Invasion.
22
23This program is free software; you can redistribute it and/or
24modify it under the terms of the GNU General Public License
25as published by the Free Software Foundation; either version 2
26of the License, or (at your option) any later version.
27
28This program is distributed in the hope that it will be useful,
29but WITHOUT ANY WARRANTY; without even the implied warranty of
30MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
31
32See the GNU General Public License for more details.
33
34You should have received a copy of the GNU General Public License
35along with this program; if not, write to the Free Software
36Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37*/
38
39#include "g_client.h"
40#include "g_actor.h"
41#include "g_ai.h"
42#include "g_combat.h"
43#include "g_edicts.h"
44#include "g_inventory.h"
45#include "g_match.h"
46#include "g_move.h"
47#include "g_reaction.h"
48#include "g_utils.h"
49#include "g_vis.h"
50
52static int scoreMissionNum = 0;
53
58Player* G_PlayerGetNextHuman (Player* lastPlayer)
59{
61 return nullptr;
62
63 if (!lastPlayer)
64 return game.players;
65
66 const Player* endOfPlayers = &game.players[game.sv_maxplayersperteam];
67
68 assert(lastPlayer >= game.players);
69 assert(lastPlayer < endOfPlayers);
70
71 Player* player = lastPlayer;
72
73 player++;
74 if (player >= endOfPlayers)
75 return nullptr;
76 else
77 return player;
78}
79
84Player* G_PlayerGetNextAI (Player* lastPlayer)
85{
87 return nullptr;
88
89 if (!lastPlayer)
91
92 const Player* endOfPlayers = &game.players[game.sv_maxplayersperteam * 2]; /* two teams */
93
94 assert(lastPlayer >= &game.players[game.sv_maxplayersperteam]); /* AI players are in the upper half of the players array */
95 assert(lastPlayer < endOfPlayers);
96
97 Player* player = lastPlayer;
98
99 player++;
100 if (player >= endOfPlayers)
101 return nullptr;
102 else
103 return player;
104}
105
110Player* G_PlayerGetNextActiveHuman (Player* lastPlayer)
111{
112 Player* player = lastPlayer;
113
114 while ((player = G_PlayerGetNextHuman(player))) {
115 if (player->isInUse())
116 return player;
117 }
118
119 return nullptr;
120}
121
126Player* G_PlayerGetNextActiveAI (Player* lastPlayer)
127{
128 Player* player = lastPlayer;
129
130 while ((player = G_PlayerGetNextAI(player))) {
131 if (player->isInUse())
132 return player;
133 }
134
135 return nullptr;
136}
137
145{
146 playermask_t playerMask = 0;
147 Player* p = nullptr;
148
149 /* don't handle the ai players, here */
150 while ((p = G_PlayerGetNextHuman(p))) {
151 if (p->isInUse() && team == p->getTeam())
152 playerMask |= G_PlayerToPM(*p);
153 }
154
155 return playerMask;
156}
157
166{
167 teammask_t teamMask = 0;
168 Player* p = nullptr;
169
170 /* don't handle the ai players, here */
171 while ((p = G_PlayerGetNextActiveHuman(p))) {
172 if (playerMask & G_PlayerToPM(*p))
173 teamMask |= G_TeamToVisMask(p->getTeam());
174 }
175
176 return teamMask;
177}
178
187{
188 playermask_t playerMask = 0;
189 Player* p = nullptr;
190
191 /* don't handle the ai players, here */
192 while ((p = G_PlayerGetNextActiveHuman(p))) {
193 if (teamMask & G_TeamToVisMask(p->getTeam()))
194 playerMask |= G_PlayerToPM(*p);
195 }
196
197 return playerMask;
198}
199
206void G_ClientPrintf (const Player& player, int printLevel, const char* fmt, ...)
207{
208 /* there is no client for an AI controlled player on the server where we
209 * could send the message to */
210 if (G_IsAIPlayer(&player))
211 return;
212
213 va_list ap;
214 va_start(ap, fmt);
215 gi.PlayerPrintf(&player, printLevel, fmt, ap);
216 va_end(ap);
217}
218
224void G_GiveTimeUnits (int team)
225{
226 Actor* actor = nullptr;
227
228 while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, team))) {
230 G_SendStats(*actor);
231 }
232}
233
245void G_AppearPerishEvent (playermask_t playerMask, bool appear, Edict& check, const Edict* ent)
246{
247 /* test for pointless player mask */
248 if (!playerMask)
249 return;
250
251 const teammask_t teamMaskDiff = G_PMToVis(playerMask);
252 G_VisFlagsSwap(check, teamMaskDiff);
253
254 if (appear) {
255 /* appear */
256 switch (check.type) {
257 case ET_ACTOR:
258 case ET_ACTOR2x2: {
259 Actor* actor = makeActor(&check);
260 G_EventActorAppear(playerMask, *actor, ent);
261 break;
262 }
263
264 case ET_CAMERA:
265 G_EventCameraAppear(playerMask, check);
266 break;
267
268 case ET_ITEM:
269 G_EventEdictAppear(playerMask, check);
270 G_SendInventory(playerMask, check);
271 break;
272
273 case ET_PARTICLE:
274 G_EventEdictAppear(playerMask, check);
275 G_EventSendParticle(playerMask, check);
276 break;
277
279 G_EventAddBrushModel(playerMask, check);
280 break;
281
282 default:
283 if (G_IsVisibleOnBattlefield(&check))
284 gi.Error("Missing edict type %i in G_AppearPerishEvent", check.type);
285 break;
286 }
287 } else if (G_IsVisibleOnBattlefield(&check)) {
288 G_EventEdictPerish(playerMask, check);
289 }
290}
291
302void G_SendInvisible (const Player& player)
303{
304 const int team = player.getTeam();
305
306 assert(team != TEAM_NO_ACTIVE);
307 if (!level.num_alive[team])
308 return;
309
310 Actor* actor = nullptr;
311 /* check visibility */
312 while ((actor = G_EdictsGetNextActor(actor))) {
313 if (actor->getTeam() == team)
314 continue;
315 /* not visible for this team - so add the le only */
316 if (!G_IsVisibleForTeam(actor, team)) {
317 G_EventActorAdd(G_PlayerToPM(player), *actor);
318 }
319 }
320}
321
328{
329 return level.activeTeam;
330}
331
337static bool G_ActionCheck (const Player& player, Edict* ent)
338{
339 if (!ent || !ent->inuse) {
340 G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - object not present!"));
341 return false;
342 }
343
344 if (!G_IsActor(ent)) {
345 G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not an actor!"));
346 return false;
347 }
348
349 if (G_IsStunned(ent)) {
350 G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is stunned!"));
351 return false;
352 }
353
354 if (G_IsDead(ent)) {
355 G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is dead!"));
356 return false;
357 }
358
359 if (ent->getTeam() != player.getTeam()) {
360 G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not on same team!"));
361 return false;
362 }
363
364 if (ent->getPlayerNum() != player.getNum()) {
365 G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - no control over allied actors!"));
366 return false;
367 }
368
369 /* could be possible */
370 return true;
371}
372
380bool G_ActionCheckForCurrentTeam (const Player& player, Actor* ent, int TU)
381{
382 /* a generic tester if an action could be possible */
383 if (level.activeTeam != player.getTeam()) {
384 G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - it is not your turn!"));
385 return false;
386 }
387
388 if (TU > G_ActorUsableTUs(ent)) {
389 return false;
390 }
391
392 return G_ActionCheck(player, ent);
393}
394
403bool G_ActionCheckForReaction (const Player& player, Actor* actor, int TU)
404{
405 if (TU > actor->getTus()) {
406 return false;
407 }
408
409 return G_ActionCheck(player, actor);
410}
411
415static void G_ClientTurn (Player& player, Actor* actor, dvec_t dvec)
416{
417 /* check if action is possible */
418 if (!G_ActionCheckForCurrentTeam(player, actor, TU_TURN))
419 return;
420
421 /* check if we're already facing that direction */
422 const int dir = getDVdir(dvec);
423 if (actor->dir == dir)
424 return;
425
426 /* do the turn */
427 G_ActorDoTurn(actor, dir);
428 G_ActorUseTU(actor, TU_TURN);
429
430 /* send the turn */
431 G_EventActorTurn(*actor);
432
433 /* send the new TUs */
434 G_SendStats(*actor);
435
436 /* end the event */
437 G_EventEnd();
438}
439
447{
448 /* Send the state change. */
450
451 /* Check if the player appears/perishes, seen from other teams. */
452 G_CheckVis(&ent);
453
454 /* Calc new vis for this player. */
455 G_CheckVisTeamAll(ent.getTeam(), 0, &ent);
456
457 /* Send the new TUs. */
458 G_SendStats(ent);
459
460 /* End the event. */
461 G_EventEnd();
462}
463
473void G_ClientStateChange (const Player& player, Actor* actor, int reqState, bool checkaction)
474{
475 /* Check if any action is possible. */
476 if (checkaction && !G_ActionCheckForCurrentTeam(player, actor, 0))
477 return;
478
479 if (!reqState)
480 return;
481
482 switch (reqState) {
483 case STATE_CROUCHED: /* Toggle between crouch/stand. */
484 /* Check if player has enough TUs (TU_CROUCH TUs for crouch/uncrouch). */
485 if (!checkaction || G_ActionCheckForCurrentTeam(player, actor, TU_CROUCH)) {
486 if (actor->isCrouched()) {
487 if (!gi.CanActorStandHere(actor->fieldSize, actor->pos))
488 break;
489 }
490 G_ToggleCrouched(actor);
491 G_ActorUseTU(actor, TU_CROUCH);
492 G_ActorSetMaxs(actor);
493 }
494 break;
495 case ~STATE_REACTION: /* Request to turn off reaction fire. */
496 if (actor->isReaction()) {
497 if (actor->isShaken() && G_ReactionFireSettingsReserveTUs(actor)) {
498 G_ClientPrintf(player, PRINT_HUD, _("Currently shaken, won't let their guard down."));
499 } else {
500 /* Turn off reaction fire. */
501 actor->removeReaction();
502 G_ActorReserveTUs(actor, 0, actor->chr.reservedTus.shot, actor->chr.reservedTus.crouch);
503 if (!G_IsAI(actor))
505 }
506 }
507 break;
508 /* Request to turn on multi- or single-reaction fire mode. */
509 case STATE_REACTION:
510 /* Disable reaction fire. */
511 actor->removeReaction();
512
514 /* Enable requested reaction fire. */
515 G_SetState(actor, reqState);
516 }
517 if (!G_IsAI(actor))
519 break;
520 default:
521 gi.DPrintf("G_ClientStateChange: unknown request %i, ignoring\n", reqState);
522 return;
523 }
524
525 /* Only activate the events - network stuff is handled in the calling function */
526 if (!checkaction)
527 return;
528
530}
531
536bool G_ClientCanReload (Actor* actor, containerIndex_t containerID)
537{
538 const objDef_t* weapon;
539
540 if (actor->getContainer(containerID)) {
541 weapon = actor->getContainer(containerID)->def();
542 } else if (containerID == CID_LEFT && actor->getRightHandItem()->isHeldTwoHanded()) {
543 /* Check for two-handed weapon */
544 weapon = actor->getRightHandItem()->def();
545 } else
546 return false;
547
548 assert(weapon);
549
550 /* also try the temp containers */
551 const Container* cont = nullptr;
552 while ((cont = actor->chr.inv.getNextCont(cont, true))) {
553 Item* item = nullptr;
554 while ((item = cont->getNextItem(item))) {
555 if (item->def()->isLoadableInWeapon(weapon))
556 return true;
557 }
558 }
559 return false;
560}
561
569{
570 /* e.g. bloodspiders are not allowed to carry or collect weapons */
571 if (!actor->chr.teamDef->weapons)
572 return false;
573
574 /* search for weapons and select the one that is available easily */
575 const invDef_t* bestContainer = nullptr;
576 Item* theWeapon = nullptr;
577 int tu = 100;
578 const Container* cont = nullptr;
579 while ((cont = actor->chr.inv.getNextCont(cont, true))) {
580 if (cont->def()->out >= tu)
581 continue;
582 Item* item = nullptr;
583 while ((item = cont->getNextItem(item))) {
584 /* We are looking for the *fastest* way to get a weapon,
585 * no matter what kind of weapon it is. */
586 assert(item->def());
587 if (item->isWeapon() && !item->mustReload()) {
588 theWeapon = item;
589 bestContainer = cont->def();
590 tu = bestContainer->out;
591 break;
592 }
593 }
594 }
595
596 /* send request */
597 const invDef_t* invDef = INVDEF(CID_RIGHT);
598 if (bestContainer) {
599 return G_ActorInvMove(actor, bestContainer, theWeapon, invDef, 0, 0, true);
600 }
601 return false; /* no weapon found */
602}
603
614bool G_ClientUseEdict (const Player& player, Actor* actor, Edict* edict)
615{
616 /* check whether the actor has sufficient TUs to 'use' this edicts */
617 if (!G_ActionCheckForCurrentTeam(player, actor, edict->TU))
618 return false;
619
620 if (!G_UseEdict(edict, actor))
621 return false;
622
623 /* using a group of edicts only costs TUs once (for the master) */
624 G_ActorUseTU(actor, edict->TU);
625 /* send the new TUs */
626 G_SendStats(*actor);
627
628 G_EventEnd();
629
630 return true;
631}
632
638int G_ClientAction (Player& player)
639{
640 pos3_t pos;
641 int i;
642 int from;
643
644 /* read the header */
645 player_action_t action = (player_action_t)gi.ReadByte();
646 int num = gi.ReadShort();
647
648 Edict* ent = G_EdictsGetByNum(num);
649 if (ent == nullptr)
650 return action;
651
652 /* we expext this ent to be an Actor */
653 Actor* actor = makeActor(ent);
654
655 const char* format = pa_format[action];
656
657 switch (action) {
658 case PA_NULL:
659 /* do nothing on a null action */
660 break;
661
662 case PA_TURN:
663 gi.ReadFormat(format, &i);
664 G_ClientTurn(player, actor, (dvec_t) i);
665 break;
666
667 case PA_MOVE:
668 gi.ReadFormat(format, &pos);
669 G_ClientMove(player, player.getTeam(), actor, pos);
670 break;
671
672 case PA_STATE:
673 gi.ReadFormat(format, &i);
674 G_ClientStateChange(player, actor, i, true);
675 break;
676
677 case PA_SHOOT:
678 fireDefIndex_t firemode;
679 gi.ReadFormat(format, &pos, &i, &firemode, &from);
680 G_ClientShoot(player, actor, pos, i, firemode, nullptr, true, from);
681 break;
682
683 case PA_INVMOVE:
684 int fx, fy, to, tx, ty;
685 gi.ReadFormat(format, &from, &fx, &fy, &to, &tx, &ty);
686
687 if (!isValidContId(from) || !isValidContId(to)) {
688 gi.DPrintf("G_ClientAction: PA_INVMOVE Container index out of range. (from: %i, to: %i)\n", from, to);
689 } else {
690 const invDef_t* fromPtr = INVDEF(from);
691 const invDef_t* toPtr = INVDEF(to);
692 Item* fromItem = ent->chr.inv.getItemAtPos(fromPtr, fx, fy);
693 if (fromItem)
694 G_ActorInvMove(actor, fromPtr, fromItem, toPtr, tx, ty, true);
695 }
696 break;
697
698 case PA_USE:
699 if (actor->clientAction) {
700 /* read the door the client wants to open */
701 gi.ReadFormat(format, &i);
702
703 /* get the door edict */
704 Edict* actionEnt = G_EdictsGetByNum(i);
705
706 /* maybe the door is no longer 'alive' because it was destroyed */
707 if (actionEnt && actor->clientAction == actionEnt) {
708 if (G_IsDoor(actionEnt)) {
709 G_ActorUseDoor(actor, actionEnt);
710 }
711 }
712 }
713 break;
714
715 case PA_REACT_SELECT:
716 actorHands_t hand;
717 int fmIdx, objIdx;
718 gi.ReadFormat(format, &hand, &fmIdx, &objIdx);
719 G_ReactionFireSettingsUpdate(actor, fmIdx, hand, INVSH_GetItemByIDX(objIdx));
720 break;
721
722 case PA_RESERVE_STATE:
723 int resCrouch, resShot;
724 gi.ReadFormat(format, &resShot, &resCrouch);
725
726 G_ActorReserveTUs(ent, ent->chr.reservedTus.reaction, resShot, resCrouch);
727 break;
728
729 default:
730 gi.Error("G_ClientAction: Unknown action!\n");
731 }
732 return action;
733}
734
740static void G_GetTeam (Player& player)
741{
742 /* player has already a team */
743 if (player.getTeam() > 0) {
744 Com_DPrintf(DEBUG_GAME, "Player %s is already on team %i\n", player.pers.netname, player.getTeam());
745 return;
746 }
747
748 /* number of currently connected players (no ai players) */
749 int playersInGame = 0;
750 Player* p = nullptr;
751 while ((p = G_PlayerGetNextActiveHuman(p)))
752 playersInGame++;
753
754 const int maxTeam = std::min(sv_maxteams->integer, TEAM_MAX_HUMAN);
755 /* randomly assign a teamnumber in deathmatch games */
756 if (playersInGame <= 1 && G_IsMultiPlayer() && !sv_teamplay->integer) {
757 int spawnCheck[MAX_TEAMS];
758 int spawnSpots = 0;
759 /* skip civilian teams */
760 for (int i = TEAM_PHALANX; i <= maxTeam; i++) {
761 spawnCheck[i] = 0;
762 /* check whether there are spawnpoints for this team */
764 spawnCheck[spawnSpots++] = i;
765 }
766 /* we need at least 2 different team spawnpoints for multiplayer in a death match game */
767 if (spawnSpots < 2)
768 gi.Error("G_GetTeam: Not enough spawn spots in map!");
769
770 /* assign random valid team number */
771 int i = spawnSpots;
772 int randomSpot = rand() % spawnSpots;
773 for (;;) {
774 const int team = spawnCheck[randomSpot];
775 if (i == 0)
776 gi.Error("G_GetTeam: Could not assign a team!");
777 if (G_SetTeamForPlayer(player, team)) {
778 gi.DPrintf("%s has been randomly assigned to team %i\n",
779 player.pers.netname, G_ClientGetTeamNum(player));
780 break;
781 }
782 i--;
783 randomSpot = (randomSpot + 1) % spawnSpots;
784 }
785 return;
786 }
787
788 /* find a team */
789 if (G_IsSinglePlayer()) {
791 } else if (sv_teamplay->integer) {
792 /* set the team specified in the userinfo */
793 const int i = G_ClientGetTeamNumPref(player);
794 gi.DPrintf("Get a team for teamplay for %s\n", player.pers.netname);
795 /* civilians are at team zero */
796 if (i > TEAM_CIVILIAN && sv_maxteams->integer >= i) {
797 G_SetTeamForPlayer(player, i);
798 gi.BroadcastPrintf(PRINT_CONSOLE, "serverconsole: %s has chosen team %i\n", player.pers.netname, i);
799 } else {
800 gi.DPrintf("Team %i is not valid - choose a team between 1 and %i\n", i, sv_maxteams->integer);
802 }
803 } else {
804 int i;
805 /* search team */
806 gi.DPrintf("Getting a multiplayer team for %s\n", player.pers.netname);
807 for (i = TEAM_CIVILIAN + 1; i <= maxTeam; i++) {
808 if (level.num_spawnpoints[i]) {
809 bool teamAvailable = true;
810
811 p = nullptr;
812 /* check if team is in use (only human controlled players) */
813 while ((p = G_PlayerGetNextActiveHuman(p))) {
814 if (p->getTeam() == i) {
815 Com_DPrintf(DEBUG_GAME, "Team %i is already in use\n", i);
816 /* team already in use */
817 teamAvailable = false;
818 break;
819 }
820 }
821 if (teamAvailable)
822 break;
823 }
824 }
825
826 /* set the team */
827 if (i <= maxTeam) {
828 /* remove ai player */
829 p = nullptr;
830 while ((p = G_PlayerGetNextActiveAI(p))) {
831 if (p->getTeam() == i) {
832 gi.BroadcastPrintf(PRINT_CONSOLE, "Removing ai player...");
833 p->setInUse(false);
834 break;
835 }
836 }
837 Com_DPrintf(DEBUG_GAME, "Assigning %s to team %i\n", player.pers.netname, i);
838 G_SetTeamForPlayer(player, i);
839 } else {
840 gi.DPrintf("No free team - disconnecting '%s'\n", player.pers.netname);
841 G_ClientDisconnect(player);
842 }
843 }
844}
845
852bool G_SetTeamForPlayer (Player& player, const int team)
853{
854 assert(team >= TEAM_NO_ACTIVE && team < MAX_TEAMS);
855
856 if (G_IsAIPlayer(&player)) {
857 if (team != TEAM_ALIEN && team != TEAM_CIVILIAN)
858 return false;
859 } else {
860 if (!sv_teamplay->integer) {
861 Player* p = nullptr;
862 while ((p = G_PlayerGetNextHuman(p)) != nullptr) {
863 if (p->getTeam() == team)
864 return false;
865 }
866 }
867 }
868
869 player.setTeam(team);
870
871 /* if we started in dev mode, we maybe don't have a
872 * starting position in this map */
873 if (!g_nospawn->integer) {
874 if (team >= 0 && team < MAX_TEAMS) {
875 if (!level.num_spawnpoints[team])
876 gi.Error("No spawnpoints for team %i", team);
877 }
878 }
879
880 if (!G_IsAIPlayer(&player))
881 Info_SetValueForKeyAsInteger(player.pers.userinfo, sizeof(player.pers.userinfo), "cl_team", team);
882
883 return true;
884}
885
889int G_ClientGetTeamNum (const Player& player)
890{
891 return player.getTeam();
892}
893
897int G_ClientGetTeamNumPref (const Player& player)
898{
899 return Info_IntegerForKey(player.pers.userinfo, "cl_teamnum");
900}
901
905bool G_ClientIsReady (const Player* player)
906{
907 assert(player);
908 return player->isReady();
909}
910
916static void G_GetStartingTeam (const Player& player)
917{
918 /* return with no action if activeTeam already assigned or if are in single-player mode */
919 if (G_MatchIsRunning())
920 return;
921
922 if (G_IsSinglePlayer()) {
923 level.activeTeam = player.getTeam();
925 return;
926 }
927
928 /* count number of currently connected unique teams and players (human controlled players only) */
929 int knownTeams[MAX_TEAMS];
930 Player* p = nullptr;
931 int teamCount = 0;
932 int playerCount = 0;
933 while ((p = G_PlayerGetNextActiveHuman(p))) {
934 int j;
935 playerCount++;
936 for (j = 0; j < teamCount; j++) {
937 if (p->getTeam() == knownTeams[j])
938 break;
939 }
940 if (j == teamCount)
941 knownTeams[teamCount++] = p->getTeam();
942 }
943
944 if (teamCount) {
945 const int teamIndex = (int) (frand() * (teamCount - 1) + 0.5);
946 G_PrintStats("Starting new game: %s with %i teams", level.mapname, teamCount);
947 level.activeTeam = knownTeams[teamIndex];
949 p = nullptr;
950 while ((p = G_PlayerGetNextActiveHuman(p)))
951 if (p->getTeam() != level.activeTeam)
952 p->roundDone = true;
953 }
954}
955
963static Edict* G_ClientGetFreeSpawnPoint (const Player& player, int spawnType)
964{
965 /* Abort for non-spawnpoints */
966 assert(spawnType == ET_ACTORSPAWN || spawnType == ET_ACTOR2x2SPAWN);
967
968 Edict* ent = nullptr;
969 if (level.noRandomSpawn) {
970 while ((ent = G_EdictsGetNextInUse(ent)))
971 if (ent->type == spawnType && player.getTeam() == ent->getTeam()) {
973 continue;
974 return ent;
975 }
976 } else {
977 Edict* list[MAX_EDICTS];
978 int count = 0;
979 while ((ent = G_EdictsGetNextInUse(ent)))
980 if (ent->type == spawnType && player.getTeam() == ent->getTeam()) {
982 continue;
983 list[count++] = ent;
984 }
985
986 if (count)
987 return list[rand() % count];
988 }
989
990 return nullptr;
991}
992
1002static inline bool G_ActorSpawnIsAllowed (const int num, const int team)
1003{
1004 if (G_IsSinglePlayer())
1005 return true;
1006
1008}
1009
1015{
1016 /* We can safely assume that this is an Actor */
1017 Actor* actor = makeActor(ent);
1018 G_ActorDieOrStun(actor, nullptr);
1019 actor->think = nullptr;
1020}
1021
1026static void G_ThinkActorGoCrouch (Edict* ent)
1027{
1028 Actor* actor = makeActor(ent);
1029 G_ClientStateChange(actor->getPlayer(), actor, STATE_CROUCHED, true);
1030 actor->think = nullptr;
1031}
1032
1039Actor* G_ClientGetFreeSpawnPointForActorSize (const Player& player, const actorSizeEnum_t actorSize)
1040{
1041 Edict* ent;
1042
1043 if (actorSize == ACTOR_SIZE_NORMAL) {
1044 /* Find valid actor spawn fields for this player. */
1046 if (ent) {
1047 /* preserve the spawpoint so we can later spawn more (alien rush) */
1048 Edict* copy = G_EdictDuplicate(ent);
1049 if (copy != nullptr)
1050 copy->type = ET_ACTOR;
1051 ent = copy;
1052 }
1053 } else if (actorSize == ACTOR_SIZE_2x2) {
1054 /* Find valid actor spawn fields for this player. */
1056 if (ent) {
1057 Edict* copy = G_EdictDuplicate(ent);
1058 if (copy != nullptr) {
1059 copy->type = ET_ACTOR2x2;
1060 copy->setMorale(100);
1061 }
1062 ent = copy;
1063 }
1064 } else {
1065 gi.Error("G_ClientGetFreeSpawnPointForActorSize: unknown fieldSize for actor edict (actorSize: %i)\n", actorSize);
1066 }
1067
1068 if (!ent)
1069 return nullptr;
1070
1071 level.num_spawned[ent->getTeam()]++;
1072 ent->setPlayerNum(player.getNum());
1073 ent->chr.fieldSize = actorSize;
1074 ent->fieldSize = ent->chr.fieldSize;
1075 ent->flags |= FL_DESTROYABLE;
1076 G_VisFlagsReset(*ent);
1077 gi.LinkEdict(ent);
1078
1079 if (ent->spawnflags & STATE_CROUCHED) {
1081 ent->nextthink = 1;
1082 }
1083
1084 if (ent->spawnflags & STATE_STUN) {
1085 if (ent->spawnflags & STATE_DEAD)
1086 ent->HP = 0;
1088 ent->nextthink = 1;
1089 }
1090
1091 G_ActorModifyCounters(nullptr, ent, 1, 0, 0);
1092
1094
1095 return makeActor(ent);
1096}
1097
1103{
1104 /* inventory */
1105 for (int nr = gi.ReadShort(); nr > 0; nr--) {
1106 const invDef_t* container;
1107 Item item;
1108 int x, y;
1109 G_ReadItem(&item, &container, &x, &y);
1110 if (container->temp)
1111 gi.Error("G_ClientReadInventory failed, tried to add '%s' to a temp container %i", item.def()->id, container->id);
1112 /* ignore the overload for now */
1113 if (!ent->chr.inv.canHoldItemWeight(CID_EQUIP, container->id, item, ent->chr.score.skills[ABILITY_POWER]))
1114 Com_Printf("G_ClientReadInventory: Item %s exceeds ent %i weight capacity\n", item.def()->id, ent->getIdNum());
1115 if (!level.noEquipment && game.invi.addToInventory(&ent->chr.inv, &item, container, x, y, 1) == nullptr)
1116 gi.Error("G_ClientReadInventory failed, could not add item '%s' to container %i (x:%i,y:%i)",
1117 item.def()->id, container->id, x, y);
1118 }
1119}
1120
1126{
1127 ent->chr.init();
1128 /* model */
1129 ent->chr.ucn = gi.ReadShort();
1130 gi.ReadString(ent->chr.name, sizeof(ent->chr.name));
1131 gi.ReadString(ent->chr.path, sizeof(ent->chr.path));
1132 gi.ReadString(ent->chr.body, sizeof(ent->chr.body));
1133 gi.ReadString(ent->chr.head, sizeof(ent->chr.head));
1134 ent->chr.bodySkin = gi.ReadByte();
1135 ent->chr.headSkin = gi.ReadByte();
1136
1137 ent->chr.HP = gi.ReadShort();
1138 ent->chr.minHP = ent->chr.HP;
1139 ent->chr.maxHP = gi.ReadShort();
1140 const int teamDefIdx = gi.ReadByte();
1141 if (teamDefIdx < 0 || teamDefIdx >= MAX_TEAMDEFS)
1142 gi.Error("Invalid team definition index given: %i", teamDefIdx);
1143 ent->chr.teamDef = &gi.csi->teamDef[teamDefIdx];
1144
1145 ent->chr.gender = gi.ReadByte();
1146 ent->chr.STUN = gi.ReadByte();
1147 ent->chr.morale = gi.ReadByte();
1148
1149 for (int k = 0; k < ent->chr.teamDef->bodyTemplate->numBodyParts(); ++k)
1150 ent->chr.wounds.treatmentLevel[k] = gi.ReadByte();
1151
1152 for (int k = 0; k < SKILL_NUM_TYPES + 1; k++) /* new attributes */
1153 ent->chr.score.experience[k] = gi.ReadLong();
1154 for (int k = 0; k < SKILL_NUM_TYPES; k++) /* new attributes */
1155 ent->chr.score.skills[k] = gi.ReadByte();
1156 for (int k = 0; k < KILLED_NUM_TYPES; k++)
1157 ent->chr.score.kills[k] = gi.ReadShort();
1158 for (int k = 0; k < KILLED_NUM_TYPES; k++)
1159 ent->chr.score.stuns[k] = gi.ReadShort();
1160 ent->chr.score.assignedMissions = gi.ReadShort();
1161}
1162
1168static void G_ClientSkipActorInfo (void)
1169{
1170 Edict ent;
1171 invDef_t container;
1172 Item item;
1173 int x, y;
1174 const invDef_t* c = &container;
1175
1177
1178 /* skip inventory */
1179 const int n = gi.ReadShort();
1180 for (int i = 0; i < n; i++) {
1181 G_ReadItem(&item, &c, &x, &y);
1182 }
1183}
1184
1191{
1192 /* Mission Scores */
1196
1197 /* set initial vital statistics */
1198 actor->HP = actor->chr.HP;
1199 actor->setMorale(actor->chr.morale);
1200
1203
1204 /* set models */
1205 actor->setBody(gi.ModelIndex(CHRSH_CharGetBody(&actor->chr)));
1206 actor->setHead(gi.ModelIndex(CHRSH_CharGetHead(&actor->chr)));
1207
1208 actor->chr.scoreMission->carriedWeight = actor->chr.inv.getWeight();
1209}
1210
1215void G_ClientInitActorStates (const Player& player)
1216{
1217 const int length = gi.ReadByte(); /* Get the actor amount that the client sent. */
1218
1219 for (int i = 0; i < length; i++) {
1220 const int ucn = gi.ReadShort();
1221 Actor* actor = G_EdictsGetActorByUCN(ucn, player.getTeam());
1222 if (!actor) {
1223 gi.DPrintf("Could not find character on team %i with unique character number %i\n", player.getTeam(), ucn);
1224 /* Skip actor info */
1225 gi.ReadShort();
1226 gi.ReadShort();
1227 gi.ReadShort();
1228 gi.ReadShort();
1229 continue;
1230 }
1231
1232 /* these state changes are not consuming any TUs */
1233 const int saveTU = actor->getTus();
1234 G_ClientStateChange(player, actor, gi.ReadShort(), false);
1235 const actorHands_t hand = (actorHands_t)gi.ReadShort();
1236 const fireDefIndex_t fmIdx = gi.ReadShort();
1237 const int objIdx = gi.ReadShort();
1238 G_ActorSetTU(actor, saveTU);
1239 if (objIdx != NONE) {
1240 if (fmIdx == NONE)
1242 else
1243 G_ReactionFireSettingsUpdate(actor, fmIdx, hand, INVSH_GetItemByIDX(objIdx));
1244 }
1246 }
1247}
1248
1255void G_ClientTeamInfo (const Player& player)
1256{
1257 const int length = gi.ReadByte(); /* Get the actor amount that the client sent. */
1258
1259 for (int i = 0; i < length; i++) {
1260 const actorSizeEnum_t actorFieldSize = gi.ReadByte();
1261 /* Search for a spawn point for each entry the client sent */
1262 if (player.getTeam() == TEAM_NO_ACTIVE || !G_ActorSpawnIsAllowed(i, player.getTeam()))
1264 else {
1265 Actor* actor = G_ClientGetFreeSpawnPointForActorSize(player, actorFieldSize);
1266 if (actor) {
1267 Com_DPrintf(DEBUG_GAME, "Player: %i - team %i - size: %i\n", player.getNum(), actor->getTeam(), actor->fieldSize);
1268
1269 G_ClientReadCharacter(actor);
1270 G_ClientReadInventory(actor);
1272 G_ActorGiveTimeUnits(actor);
1273 G_TouchTriggers(actor);
1275 AIL_InitActor(actor);
1276 } else {
1277 gi.DPrintf("Not enough spawn points for team %i (actorsize: %i)\n", player.getTeam(), actorFieldSize);
1278
1280 }
1281 }
1282 }
1283
1284 Com_Printf("Used inventory slots client %s spawn: %i\n", player.pers.netname, game.invi.GetUsedSlots());
1285}
1286
1296static void G_ClientSendEdictsAndBrushModels (const Player& player)
1297{
1298 const int mask = G_PlayerToPM(player);
1299 /* skip the world */
1300 Edict* ent = G_EdictsGetFirst();
1301
1302 /* make SOLID_BSP edicts visible to the client */
1303 while ((ent = G_EdictsGetNextInUse(ent))) {
1304 /* brush models that have a type - not the world - keep in
1305 * mind that there are several world edicts in the list in case of
1306 * a map assembly */
1307 if (ent->solid != SOLID_BSP)
1308 continue;
1309
1310 /* skip the world(s) in case of map assembly */
1311 if (ent->type > ET_NULL) {
1312 G_EventAddBrushModel(mask, *ent);
1313 G_VisFlagsAdd(*ent, ~ent->visflags);
1314 }
1315 }
1316}
1317
1323bool G_ClientBegin (Player& player)
1324{
1325 player.began = true;
1326 level.numplayers++;
1327
1328 /* find a team */
1329 G_GetTeam(player);
1330 if (!player.began)
1331 return false;
1332
1333 gi.ConfigString(CS_PLAYERCOUNT, "%i", level.numplayers);
1334
1335 /* spawn camera (starts client rendering) */
1337
1338 /* send things like doors and breakables */
1340
1341 /* ensure that the start event is send */
1342 G_EventEnd();
1343
1344 /* set the net name */
1345 gi.ConfigString(CS_PLAYERNAMES + player.getNum(), "%s", player.pers.netname);
1346
1347 /* inform all clients */
1348 gi.BroadcastPrintf(PRINT_CONSOLE, "%s has joined team %i\n", player.pers.netname, player.getTeam());
1349
1350 return true;
1351}
1352
1361void G_ClientStartMatch (Player& player)
1362{
1363 G_GetStartingTeam(player);
1364
1365 /* do all the init events here... */
1366 /* reset the data */
1367 G_EventReset(player, level.activeTeam);
1368
1369 /* show visible actors and add invisible actor */
1370 G_VisFlagsClear(player.getTeam());
1371 G_CheckVisPlayer(player, false);
1372 G_SendInvisible(player);
1373
1374 /* submit stats */
1375 G_SendPlayerStats(player);
1376
1377 /* ensure that the last event is send, too */
1378 G_EventEnd();
1379
1380 if (G_IsMultiPlayer()) {
1381 /* ensure that we restart the round time limit */
1383 }
1384
1385 /* inform all clients */
1386 gi.BroadcastPrintf(PRINT_CONSOLE, "%s has taken control over team %i.\n", player.pers.netname, player.getTeam());
1387}
1388
1393void G_ClientUserinfoChanged (Player& player, const char* userinfo)
1394{
1395 const bool alreadyReady = player.isReady();
1396 const int oldTeamnum = Info_IntegerForKey(player.pers.userinfo, "cl_teamnum");
1397
1398 /* check for malformed or illegal info strings */
1399 if (!Info_Validate(userinfo))
1400 userinfo = "\\cl_name\\badinfo";
1401
1402 /* set name */
1403 Q_strncpyz(player.pers.netname, Info_ValueForKey(userinfo, "cl_name"), sizeof(player.pers.netname));
1404 Q_strncpyz(player.pers.userinfo, userinfo, sizeof(player.pers.userinfo));
1405 player.autostand = Info_IntegerForKey(userinfo, "cl_autostand");
1406 player.setReady(Info_IntegerForKey(userinfo, "cl_ready"));
1407
1408 /* send the updated config string */
1409 gi.ConfigString(CS_PLAYERNAMES + player.getNum(), "%s", player.pers.netname);
1410
1411 /* try to update to the preferred team */
1412 if (!G_MatchIsRunning() && oldTeamnum != Info_IntegerForKey(userinfo, "cl_teamnum")) {
1413 /* if the player is marked as ready he can't change his team */
1414 if (!alreadyReady || !player.isReady()) {
1415 player.setTeam(TEAM_NO_ACTIVE);
1416 G_GetTeam(player);
1417 } else {
1418 Com_DPrintf(DEBUG_GAME, "G_ClientUserinfoChanged: player %s is already marked as being ready\n",
1419 player.pers.netname);
1420 }
1421 }
1422}
1423
1436bool G_ClientConnect (Player* player, char* userinfo, size_t userinfoSize)
1437{
1438 const char* value = Info_ValueForKey(userinfo, "ip");
1439
1440 Com_Printf("connection attempt from %s\n", value);
1441
1442 /* check to see if they are on the banned IP list */
1443 if (SV_FilterPacket(value)) {
1444 Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", REJ_BANNED);
1445 return false;
1446 }
1447
1448 if (!G_PlayerToPM(*player)) {
1449 Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", REJ_SERVER_FULL);
1450 return false;
1451 }
1452
1453 value = Info_ValueForKey(userinfo, "password");
1454 if (password->string[0] != '\0' && !Q_streq(password->string, "none") && !Q_streq(password->string, value)) {
1455 Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", REJ_PASSWORD_REQUIRED_OR_INCORRECT);
1456 return false;
1457 }
1458
1459 /* fix for fast reconnects after a disconnect */
1460 if (player->isInUse()) {
1461 gi.BroadcastPrintf(PRINT_CONSOLE, "%s already in use.\n", player->pers.netname);
1462 G_ClientDisconnect(*player);
1463 }
1464
1465 /* reset persistent data */
1466 OBJZERO(player->pers);
1467 G_ClientUserinfoChanged(*player, userinfo);
1468
1469 gi.BroadcastPrintf(PRINT_CONSOLE, "%s is connecting...\n", player->pers.netname);
1470 return true;
1471}
1472
1476void G_ClientDisconnect (Player& player)
1477{
1478 /* only if the player already sent his began */
1479 if (player.began) {
1480 level.numplayers--;
1481 gi.ConfigString(CS_PLAYERCOUNT, "%i", level.numplayers);
1482
1483 if (level.activeTeam == player.getTeam())
1484 G_ClientEndRound(player);
1485
1486 /* if no more players are connected - stop the server */
1488 }
1489
1490#if 0
1491 /* now let's remove all the edicts that belongs to this player */
1492 Actor* actor = nullptr;
1493 while ((actor = G_EdictsGetNextLivingActor(actor))) {
1494 if (actor->pnum == player.num)
1495 G_ActorDie(actor, STATE_DEAD, nullptr);
1496 }
1498#endif
1499
1500 player.began = false;
1501 player.roundDone = false;
1502 player.setReady(false);
1503
1504 gi.BroadcastPrintf(PRINT_CONSOLE, "%s disconnected.\n", player.pers.netname);
1505}
1506
1511{
1512 scoreMissionNum = 0;
1514}
const char * CHRSH_CharGetBody(const character_t *const chr)
Returns the body model for the soldiers for armoured and non armoured soldiers.
Definition: chr_shared.cpp:297
const char * CHRSH_CharGetHead(const character_t *const chr)
Returns the head model for the soldiers for armoured and non armoured soldiers.
Definition: chr_shared.cpp:319
@ ABILITY_POWER
Definition: chr_shared.h:37
@ SKILL_NUM_TYPES
Definition: chr_shared.h:51
@ ABILITY_MIND
Definition: chr_shared.h:40
#define MAX_TEAMDEFS
Definition: chr_shared.h:228
@ KILLED_NUM_TYPES
Definition: chr_shared.h:32
#define _(String)
Definition: cl_shared.h:44
#define INVDEF(containerID)
Definition: cl_shared.h:48
An Edict of type Actor.
Definition: g_edict.h:348
bool isShaken() const
Definition: g_edict.h:354
void setBody(unsigned int body_)
Definition: g_edict.h:387
void removeReaction()
Definition: g_edict.h:378
bool isReaction() const
Definition: g_edict.h:357
bool isCrouched() const
Definition: g_edict.h:361
void setHead(unsigned int head_)
Definition: g_edict.h:393
short numBodyParts(void) const
Definition: chr_shared.cpp:389
Item * getNextItem(const Item *prev) const
Definition: inv_shared.cpp:671
const invDef_t * def() const
Definition: inv_shared.cpp:667
Definition: g_edict.h:45
int getPlayerNum() const
Definition: g_edict.h:234
teammask_t visflags
Definition: g_edict.h:82
int TU
Definition: g_edict.h:88
character_t chr
Definition: g_edict.h:116
void setPlayerNum(int num)
Definition: g_edict.h:186
int flags
Definition: g_edict.h:169
int getIdNum() const
Definition: g_edict.h:231
void setMorale(int mor)
Definition: g_edict.h:311
Item * getRightHandItem() const
Definition: g_edict.h:249
vec3_t origin
Definition: g_edict.h:53
pos3_t pos
Definition: g_edict.h:55
actorSizeEnum_t fieldSize
Definition: g_edict.h:141
void(* think)(Edict *self)
Definition: g_edict.h:150
int HP
Definition: g_edict.h:89
byte dir
Definition: g_edict.h:86
int contentFlags
Definition: g_edict.h:84
Edict * clientAction
Definition: g_edict.h:113
bool inuse
Definition: g_edict.h:47
int getTeam() const
Definition: g_edict.h:269
float nextthink
Definition: g_edict.h:149
Player & getPlayer() const
Definition: g_edict.h:265
int spawnflags
Definition: g_edict.h:118
solid_t solid
Definition: g_edict.h:58
entity_type_t type
Definition: g_edict.h:81
Item * getContainer(const containerIndex_t idx) const
Definition: g_edict.h:243
int pnum
Definition: g_edict.h:97
int getTus() const
Definition: g_edict.h:318
bool canHoldItemWeight(containerIndex_t from, containerIndex_t to, const Item &item, int maxWeight) const
Check that adding an item to the inventory won't exceed the max permitted weight.
Definition: inv_shared.cpp:919
int getWeight() const
Get the weight of the items in the given inventory (excluding those in temp containers).
Definition: inv_shared.cpp:937
Item * getItemAtPos(const invDef_t *container, const int x, const int y) const
Searches if there is an item at location (x,y) in a container.
Definition: inv_shared.cpp:844
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
Definition: inv_shared.cpp:722
int GetUsedSlots()
Calculate the number of used inventory slots.
Definition: inventory.cpp:966
Item * addToInventory(Inventory *const inv, const Item *const item, const invDef_t *container, int x, int y, int amount) __attribute__((warn_unused_result))
Add an item to a specified container in a given inventory.
Definition: inventory.cpp:91
item instance data, with linked list capability
Definition: inv_shared.h:402
bool mustReload() const
Definition: inv_shared.h:483
const objDef_t * def(void) const
Definition: inv_shared.h:469
bool isWeapon() const
Definition: inv_shared.h:486
bool isHeldTwoHanded() const
Definition: inv_shared.h:476
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition: common.cpp:398
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
#define PRINT_HUD
Definition: defines.h:107
#define MAX_EDICTS
Definition: defines.h:99
#define MAX_TEAMS
Definition: defines.h:98
#define PRINT_CONSOLE
Definition: defines.h:108
#define NONE
Definition: defines.h:68
#define TEAM_DEFAULT
Definition: defines.h:51
#define TU_TURN
Definition: defines.h:73
#define DEBUG_GAME
Definition: defines.h:61
#define ACTOR_SIZE_2x2
Definition: defines.h:303
#define ACTOR_SIZE_NORMAL
Definition: defines.h:302
#define TU_CROUCH
Definition: defines.h:72
void G_ActorReserveTUs(Edict *ent, int resReaction, int resShot, int resCrouch)
Reserves TUs for different actor actions.
Definition: g_actor.cpp:136
bool G_ActorDieOrStun(Actor *actor, Edict *attacker)
Reports and handles death or stun of an actor. If the HP of an actor is zero the actor will die,...
Definition: g_actor.cpp:435
void G_ActorGiveTimeUnits(Actor *actor)
Set time units for the given edict. Based on speed skills.
Definition: g_actor.cpp:260
int G_ActorUsableTUs(const Edict *ent)
Calculates the amount of usable TUs. This is without the reserved TUs.
Definition: g_actor.cpp:102
void G_ActorModifyCounters(const Edict *attacker, const Edict *victim, int deltaAlive, int deltaKills, int deltaStuns)
Definition: g_actor.cpp:299
void G_ActorUseDoor(Actor *actor, Edict *door)
Make the actor use (as in open/close) a door edict.
Definition: g_actor.cpp:55
void G_ActorSetTU(Edict *ent, int tus)
Definition: g_actor.cpp:267
static bool G_ActorDie(Actor *actor, const Edict *attacker)
Definition: g_actor.cpp:405
void G_ActorUseTU(Edict *ent, int tus)
Definition: g_actor.cpp:278
int G_ActorDoTurn(Edict *ent, byte dir)
Turns an actor around.
Definition: g_actor.cpp:154
void G_ActorSetMaxs(Actor *actor)
Sets correct bounding box for actor (state dependent).
Definition: g_actor.cpp:226
bool G_ActorInvMove(Actor *actor, const invDef_t *fromContType, Item *fItem, const invDef_t *toContType, int tx, int ty, bool checkaction)
Moves an item inside an inventory. Floors are handled special.
Definition: g_actor.cpp:506
int G_ActorGetContentFlags(const vec3_t origin)
Get the content flags from where the actor is currently standing.
Definition: g_actor.cpp:484
#define G_IsStunned(ent)
Definition: g_actor.h:30
#define G_SetState(ent, s)
Definition: g_actor.h:36
#define G_IsDead(ent)
Definition: g_actor.h:34
#define G_ToggleCrouched(ent)
Definition: g_actor.h:42
Artificial Intelligence functions.
int AIL_InitActor(Actor *actor)
Initializes the lua AI for an actor.
Definition: g_ai_lua.cpp:2203
playermask_t G_TeamToPM(int team)
Generates the player bit mask for a given team.
Definition: g_client.cpp:144
bool G_SetTeamForPlayer(Player &player, const int team)
Set the used team for the given player.
Definition: g_client.cpp:852
void G_ResetClientData(void)
Called after every player has joined.
Definition: g_client.cpp:1510
static void G_ClientReadCharacter(Edict *ent)
Reads the character data from the netchannel that is needed to spawn an actor.
Definition: g_client.cpp:1125
static void G_ClientStateChangeUpdate(Edict &ent)
After an actor changed his state, he might get visible for other players. Check the vis here and send...
Definition: g_client.cpp:446
Player * G_PlayerGetNextActiveHuman(Player *lastPlayer)
Iterate through the list of players.
Definition: g_client.cpp:110
bool G_ActionCheckForCurrentTeam(const Player &player, Actor *ent, int TU)
Checks whether the requested action is possible for the current active team.
Definition: g_client.cpp:380
Player * G_PlayerGetNextHuman(Player *lastPlayer)
Iterate through the list of players.
Definition: g_client.cpp:58
Player * G_PlayerGetNextActiveAI(Player *lastPlayer)
Iterate through the list of players.
Definition: g_client.cpp:126
void G_GiveTimeUnits(int team)
Network function to update the time units (TUs) for each team-member.
Definition: g_client.cpp:224
static void G_ClientSendEdictsAndBrushModels(const Player &player)
Send brush models for entities like func_breakable and func_door and triggers with their bounding box...
Definition: g_client.cpp:1296
static void G_ThinkActorGoCrouch(Edict *ent)
Think function for actors that spawn crouched.
Definition: g_client.cpp:1026
static bool G_ActorSpawnIsAllowed(const int num, const int team)
Checks whether the spawn of an actor is allowed for the current running match.
Definition: g_client.cpp:1002
void G_ClientUserinfoChanged(Player &player, const char *userinfo)
called whenever the player updates a userinfo variable.
Definition: g_client.cpp:1393
bool G_ClientUseEdict(const Player &player, Actor *actor, Edict *edict)
This function 'uses' the edict. E.g. it opens the door when the player wants it to open.
Definition: g_client.cpp:614
int G_ClientGetTeamNumPref(const Player &player)
Returns the preferred team number for the player.
Definition: g_client.cpp:897
int G_GetActiveTeam(void)
Returns the current active team to the server.
Definition: g_client.cpp:327
static void G_ThinkActorDieAfterSpawn(Edict *ent)
Think function for actors that spawn dead.
Definition: g_client.cpp:1014
static void G_ClientSkipActorInfo(void)
Call this if you want to skip some actor netchannel data.
Definition: g_client.cpp:1168
static bool G_ActionCheck(const Player &player, Edict *ent)
Checks whether the requested action is possible.
Definition: g_client.cpp:337
bool G_ClientIsReady(const Player *player)
Definition: g_client.cpp:905
static Edict * G_ClientGetFreeSpawnPoint(const Player &player, int spawnType)
Find valid actor spawn fields for this player.
Definition: g_client.cpp:963
void G_ClientDisconnect(Player &player)
Definition: g_client.cpp:1476
void G_ClientStateChange(const Player &player, Actor *actor, int reqState, bool checkaction)
Changes the state of a player/soldier.
Definition: g_client.cpp:473
static chrScoreMission_t scoreMission[MAX_EDICTS]
Definition: g_client.cpp:51
void G_AppearPerishEvent(playermask_t playerMask, bool appear, Edict &check, const Edict *ent)
Send the appear or perish event to the affected clients.
Definition: g_client.cpp:245
static void G_GetStartingTeam(const Player &player)
Chose a team that should start the match.
Definition: g_client.cpp:916
static void G_ClientAssignDefaultActorValues(Actor *actor)
Used after spawning an actor to set some default values that are not read from the network event.
Definition: g_client.cpp:1190
void G_SendInvisible(const Player &player)
This function sends all the actors to the client that are not visible initially - this is needed beca...
Definition: g_client.cpp:302
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition: g_client.cpp:206
bool G_ClientBegin(Player &player)
This functions starts the client.
Definition: g_client.cpp:1323
int G_ClientGetTeamNum(const Player &player)
Returns the assigned team number of the player.
Definition: g_client.cpp:889
int G_ClientAction(Player &player)
The client sent us a message that he did something. We now execute the related function(s) and notify...
Definition: g_client.cpp:638
bool G_ClientConnect(Player *player, char *userinfo, size_t userinfoSize)
Checks whether the connection is valid or invalid and set some user info keys.
Definition: g_client.cpp:1436
Player * G_PlayerGetNextAI(Player *lastPlayer)
Iterate through the list of players.
Definition: g_client.cpp:84
Actor * G_ClientGetFreeSpawnPointForActorSize(const Player &player, const actorSizeEnum_t actorSize)
Searches a free spawning point for a given actor size and turns it into an actor.
Definition: g_client.cpp:1039
bool G_ActionCheckForReaction(const Player &player, Actor *actor, int TU)
Checks whether the requested action is possible.
Definition: g_client.cpp:403
bool G_ClientCanReload(Actor *actor, containerIndex_t containerID)
Returns true if actor can reload weapon.
Definition: g_client.cpp:536
void G_ClientStartMatch(Player &player)
Sets the team, init the TU and sends the player stats.
Definition: g_client.cpp:1361
bool G_ClientGetWeaponFromInventory(Actor *actor)
Retrieve or collect a loaded weapon from any linked container for the actor's right hand.
Definition: g_client.cpp:568
static void G_ClientTurn(Player &player, Actor *actor, dvec_t dvec)
Sends the actual actor turn event over the netchannel.
Definition: g_client.cpp:415
static void G_ClientReadInventory(Edict *ent)
Read the inventory from the clients team data.
Definition: g_client.cpp:1102
teammask_t G_PMToVis(playermask_t playerMask)
Converts player mask to vis mask.
Definition: g_client.cpp:165
static int scoreMissionNum
Definition: g_client.cpp:52
void G_ClientInitActorStates(const Player &player)
This is called after the actors are spawned and will set actor states without consuming TUs.
Definition: g_client.cpp:1215
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition: g_client.cpp:186
void G_ClientTeamInfo(const Player &player)
The client lets the server spawn the actors for a given player by sending their information (models,...
Definition: g_client.cpp:1255
static void G_GetTeam(Player &player)
Sets the teamnum var for this match.
Definition: g_client.cpp:740
Interface for g_client.cpp.
bool G_ClientShoot(const Player &player, Actor *actor, const pos3_t at, shoot_types_t shootType, fireDefIndex_t firemode, shot_mock_t *mock, bool allowReaction, int z_align)
Setup for shooting, either real or mock.
Definition: g_combat.cpp:1183
All parts of the main game logic that are combat related.
Actor * makeActor(Edict *ent)
Convert an Edict pointer into an Actor pointer.
Definition: g_edicts.cpp:327
Edict * G_EdictsGetFirst(void)
Returns the first entity.
Definition: g_edicts.cpp:98
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition: g_edicts.cpp:216
Actor * G_EdictsGetActorByUCN(const int ucn, const int team)
Searches an actor by a unique character number.
Definition: g_edicts.cpp:254
Actor * G_EdictsGetNextActor(Actor *lastEnt)
Iterate through the actor entities (even the dead!)
Definition: g_edicts.cpp:231
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition: g_edicts.cpp:196
Edict * G_EdictDuplicate(const Edict *edict)
Definition: g_edicts.cpp:128
Edict * G_EdictsGetNextInUse(Edict *lastEnt)
Iterate through the entities that are in use.
Definition: g_edicts.cpp:166
Actor * G_EdictsGetLivingActorFromPos(const pos3_t pos)
Searches an actor at the given grid location.
Definition: g_edicts.cpp:270
Edict * G_EdictsGetByNum(const int num)
Get an entity by it's number.
Definition: g_edicts.cpp:83
functions to handle the storage and lifecycle of all edicts in the game module.
void G_EventEnd(void)
Definition: g_events.cpp:711
void G_EventSendParticle(playermask_t playerMask, const Edict &ent)
Definition: g_events.cpp:529
void G_EventReactionFireChange(const Edict &ent)
Definition: g_events.cpp:282
void G_EventActorTurn(const Edict &ent)
Send the turn event for the given entity.
Definition: g_events.cpp:77
void G_EventActorAppear(playermask_t playerMask, const Actor &check, const Edict *ent)
Definition: g_events.cpp:571
void G_EventAddBrushModel(playermask_t playerMask, const Edict &ent)
Definition: g_events.cpp:639
void G_EventStart(const Player &player, bool teamplay)
Definition: g_events.cpp:662
void G_EventCameraAppear(playermask_t playerMask, const Edict &ent)
Send an appear event to the client.
Definition: g_events.cpp:543
void G_EventEdictPerish(playermask_t playerMask, const Edict &ent)
Send disappear event.
Definition: g_events.cpp:624
void G_EventReset(const Player &player, int activeTeam)
Definition: g_events.cpp:669
void G_EventSendState(playermask_t playerMask, const Edict &ent)
Definition: g_events.cpp:466
void G_EventActorAdd(playermask_t playerMask, const Edict &ent, const bool instant)
Definition: g_events.cpp:511
void G_EventEdictAppear(playermask_t playerMask, const Edict &ent)
Send an appear event to the client.
Definition: g_events.cpp:563
#define G_PlayerToPM(player)
Definition: g_events.h:37
unsigned int playermask_t
Definition: g_events.h:34
void G_SendInventory(playermask_t playerMask, const Edict &ent)
Sends whole inventory through the network buffer.
void G_ReadItem(Item *item, const invDef_t **container, int *x, int *y)
Read item from the network buffer.
#define G_IsVisibleOnBattlefield(ent)
Definition: g_local.h:140
game_locals_t game
Definition: g_main.cpp:37
level_locals_t level
Definition: g_main.cpp:38
#define G_IsDoor(ent)
Definition: g_local.h:135
cvar_t * sv_teamplay
Definition: g_main.cpp:61
#define G_IsActor(ent)
Definition: g_local.h:127
#define G_IsAI(ent)
Definition: g_local.h:141
#define G_IsVisibleForTeam(ent, team)
Definition: g_local.h:144
#define G_TeamToVisMask(team)
Definition: g_local.h:143
bool SV_FilterPacket(const char *from)
Definition: g_svcmds.cpp:104
void G_SendStats(Edict &ent)
Send stats to network buffer.
Definition: g_stats.cpp:34
game_import_t gi
Definition: g_main.cpp:39
#define G_IsAIPlayer(player)
Definition: g_local.h:142
#define G_IsMultiPlayer()
Definition: g_local.h:145
void G_ClientEndRound(Player &player)
Definition: g_round.cpp:184
#define G_IsSinglePlayer()
Definition: g_local.h:146
cvar_t * sv_maxsoldiersperplayer
Definition: g_main.cpp:54
cvar_t * sv_roundtimelimit
Definition: g_main.cpp:56
cvar_t * g_nospawn
Definition: g_main.cpp:118
cvar_t * sv_maxsoldiersperteam
Definition: g_main.cpp:53
cvar_t * password
Definition: g_main.cpp:67
void G_SendPlayerStats(const Player &player)
Write player stats to network buffer.
Definition: g_stats.cpp:49
#define FL_DESTROYABLE
If an edict is destroyable (like ET_BREAKABLE, ET_DOOR [if health set] or maybe a ET_MISSION [if heal...
Definition: g_local.h:289
cvar_t * sv_maxteams
Definition: g_main.cpp:59
bool G_MatchIsRunning(void)
Checks whether the game is running (active team and no intermission time)
Definition: g_match.cpp:320
void G_MatchEndCheck(void)
Checks whether there are still actors to fight with left. If none are the match end will be triggered...
Definition: g_match.cpp:280
Match related functions.
void G_ClientMove(const Player &player, int visTeam, Actor *actor, const pos3_t to)
Generates the client events that are send over the netchannel to move an actor.
Definition: g_move.cpp:307
void G_ReactionFireSettingsUpdate(Actor *actor, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t *od)
Updates the reaction fire settings in case something was moved into a hand or from a hand that would ...
Definition: g_reaction.cpp:555
void G_ReactionFireTargetsCreate(const Edict *shooter)
free function to create a table of reaction fire targets for the given edict.
Definition: g_reaction.cpp:436
bool G_ReactionFireSettingsReserveTUs(Actor *ent)
Set the reaction fire TU reservation for an actor.
Definition: g_reaction.cpp:665
Reaction fire system.
bool G_UseEdict(Edict *ent, Edict *activator)
Call the 'use' function for the given edict and all its group members.
Definition: g_utils.cpp:117
void G_PrintStats(const char *format,...)
Prints stats to game console and stats log file.
Definition: g_utils.cpp:304
int G_TouchTriggers(Edict *ent, const entity_type_t type)
Check the world against triggers for the current entity.
Definition: g_utils.cpp:547
Misc utility functions for game module.
void G_VisFlagsAdd(Edict &ent, teammask_t teamMask)
Definition: g_vis.cpp:433
void G_VisFlagsClear(int team)
Reset the visflags for all edicts in the global list for the given team - and only for the given team...
Definition: g_vis.cpp:424
int G_CheckVisTeamAll(const int team, const vischeckflags_t visFlags, const Edict *ent)
Do G_CheckVisTeam for all entities ent is the one that is looking at the others.
Definition: g_vis.cpp:376
void G_VisFlagsReset(Edict &ent)
Definition: g_vis.cpp:438
void G_VisFlagsSwap(Edict &ent, teammask_t teamMask)
Definition: g_vis.cpp:443
void G_CheckVis(Edict *check, const vischeckflags_t visFlags)
Check if the edict appears/perishes for the other teams. If they appear for other teams,...
Definition: g_vis.cpp:409
void G_CheckVisPlayer(Player &player, const vischeckflags_t visFlags)
Sets visible edict on player spawn.
Definition: g_vis.cpp:327
unsigned int teammask_t
Definition: g_vis.h:30
@ SOLID_BSP
Definition: game.h:157
const char * Info_ValueForKey(const char *s, const char *key)
Searches the string for the given key and returns the associated value, or an empty string.
Definition: infostring.cpp:39
void Info_SetValueForKey(char *s, const size_t size, const char *key, const char *value)
Adds a new entry into string with given value.
Definition: infostring.cpp:169
void Info_SetValueForKeyAsInteger(char *s, const size_t size, const char *key, const int value)
Definition: infostring.cpp:154
bool Info_Validate(const char *s)
Some characters are illegal in info strings because they can mess up the server's parsing.
Definition: infostring.cpp:142
int Info_IntegerForKey(const char *s, const char *key)
Definition: infostring.cpp:84
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
Definition: inv_shared.cpp:266
int32_t fireDefIndex_t
Definition: inv_shared.h:78
int32_t containerIndex_t
Definition: inv_shared.h:46
#define CID_EQUIP
Definition: inv_shared.h:56
#define CID_LEFT
Definition: inv_shared.h:48
bool isValidContId(const containerIndex_t id)
Definition: inv_shared.h:59
#define CID_RIGHT
Definition: inv_shared.h:47
actorHands_t
Definition: inv_shared.h:626
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
short dvec_t
The direction vector tells us where the actor came from (in his previous step). The pathing table hol...
Definition: mathlib.h:236
#define getDVdir(dv)
Definition: mathlib.h:249
const char * pa_format[]
Player action format strings for netchannel transfer.
Definition: q_shared.cpp:34
#define TEAM_PHALANX
Definition: q_shared.h:62
#define STATE_DEAD
Definition: q_shared.h:262
#define CS_PLAYERNAMES
Definition: q_shared.h:328
#define TEAM_ALIEN
Definition: q_shared.h:63
#define REJ_SERVER_FULL
Definition: q_shared.h:583
#define STATE_STUN
Definition: q_shared.h:268
@ ET_ACTOR
Definition: q_shared.h:148
@ ET_ACTOR2x2
Definition: q_shared.h:160
@ ET_TRIGGER_RESCUE
Definition: q_shared.h:154
@ ET_PARTICLE
Definition: q_shared.h:164
@ ET_NULL
Definition: q_shared.h:146
@ ET_CAMERA
Definition: q_shared.h:171
@ ET_ACTORSPAWN
Definition: q_shared.h:147
@ ET_ACTOR2x2SPAWN
Definition: q_shared.h:159
@ ET_ITEM
Definition: q_shared.h:149
#define STATE_CROUCHED
Definition: q_shared.h:263
#define STATE_REACTION
Definition: q_shared.h:272
#define GET_MORALE(ab)
Definition: q_shared.h:290
void format(__printf__, 1, 2)))
#define REJ_BANNED
Definition: q_shared.h:582
#define TEAM_MAX_HUMAN
Definition: q_shared.h:64
#define CS_PLAYERCOUNT
Definition: q_shared.h:317
player_action_t
Definition: q_shared.h:189
@ PA_SHOOT
Definition: q_shared.h:194
@ PA_INVMOVE
Definition: q_shared.h:196
@ PA_NULL
Definition: q_shared.h:190
@ PA_USE
Definition: q_shared.h:195
@ PA_STATE
Definition: q_shared.h:193
@ PA_REACT_SELECT
Definition: q_shared.h:197
@ PA_RESERVE_STATE
Definition: q_shared.h:198
@ PA_TURN
Definition: q_shared.h:191
@ PA_MOVE
Definition: q_shared.h:192
#define TEAM_CIVILIAN
Definition: q_shared.h:61
#define TEAM_NO_ACTIVE
Definition: q_shared.h:60
#define REJ_PASSWORD_REQUIRED_OR_INCORRECT
Reject messages that are send to the client from the game module.
Definition: q_shared.h:580
QGL_EXTERN GLuint count
Definition: r_gl.h:99
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
QGL_EXTERN GLint i
Definition: r_gl.h:113
#define Q_streq(a, b)
Definition: shared.h:136
#define OBJZERO(obj)
Definition: shared.h:178
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
chrReservations_t reservedTus
Definition: chr_shared.h:415
const teamDef_t * teamDef
Definition: chr_shared.h:413
chrScoreMission_t * scoreMission
Definition: chr_shared.h:407
char head[MAX_VAR]
Definition: chr_shared.h:393
char path[MAX_VAR]
Definition: chr_shared.h:391
chrScoreGlobal_t score
Definition: chr_shared.h:406
actorSizeEnum_t fieldSize
Definition: chr_shared.h:409
woundInfo_t wounds
Definition: chr_shared.h:402
char body[MAX_VAR]
Definition: chr_shared.h:392
char name[MAX_VAR]
Definition: chr_shared.h:390
Inventory inv
Definition: chr_shared.h:411
int stuns[KILLED_NUM_TYPES]
Definition: chr_shared.h:127
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:126
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
int experience[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:120
Structure of all stats collected in a mission.
Definition: chr_shared.h:75
teamDef_t teamDef[MAX_TEAMDEFS]
Definition: q_shared.h:548
bool modified
Definition: cvar.h:79
int integer
Definition: cvar.h:81
char * string
Definition: cvar.h:73
const csi_t * csi
Definition: game.h:176
int sv_maxplayersperteam
Definition: g_local.h:73
InventoryInterface invi
Definition: g_local.h:76
Player * players
Definition: g_local.h:70
inventory definition for our menus
Definition: inv_shared.h:371
containerIndex_t id
Definition: inv_shared.h:373
bool temp
Definition: inv_shared.h:381
int activeTeam
Definition: g_local.h:101
unsigned num_spawned[MAX_TEAMS]
Definition: g_local.h:112
char mapname[MAX_QPATH]
Definition: g_local.h:85
int numplayers
Definition: g_local.h:100
int noRandomSpawn
Definition: g_local.h:108
int noEquipment
Definition: g_local.h:109
byte num_alive[MAX_TEAMS]
Definition: g_local.h:115
byte num_spawnpoints[MAX_TEAMS]
Definition: g_local.h:116
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
bool isLoadableInWeapon(const objDef_s *weapon) const
Checks if an item can be used to reload a weapon.
Definition: inv_shared.cpp:356
const char * id
Definition: inv_shared.h:268
bool weapons
Definition: chr_shared.h:335
const BodyData * bodyTemplate
Definition: chr_shared.h:350
int treatmentLevel[BODYPART_MAXTYPE]
Definition: chr_shared.h:363
pos_t pos3_t[3]
Definition: ufotypes.h:58
int32_t actorSizeEnum_t
Definition: ufotypes.h:77