UFO: Alien Invasion
Loading...
Searching...
No Matches
g_client.cpp
Go to the documentation of this file.
1
19
20/*
21Copyright (C) 2002-2025 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{
60 if (!game.sv_maxplayersperteam)
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{
86 if (!game.sv_maxplayersperteam)
87 return nullptr;
88
89 if (!lastPlayer)
90 return &game.players[game.sv_maxplayersperteam];
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 */
763 if (level.num_spawnpoints[i])
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();
924 level.teamOfs = MAX_TEAMS - level.activeTeam;
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];
948 level.teamOfs = MAX_TEAMS - level.activeTeam;
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
1007 return num < sv_maxsoldiersperplayer->integer && level.num_spawned[team] < sv_maxsoldiersperteam->integer;
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) */
1336 G_EventStart(player, sv_teamplay->integer);
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 */
1382 sv_roundtimelimit->modified = true;
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.
const char * CHRSH_CharGetHead(const character_t *const chr)
Returns the head model for the soldiers for armoured and non armoured soldiers.
@ 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
Item * getNextItem(const Item *prev) const
const invDef_t * def() const
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.
int getWeight() const
Get the weight of the items in the given inventory (excluding those in temp containers).
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.
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
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:440
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#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.
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.
static void G_ClientReadCharacter(Edict *ent)
Reads the character data from the netchannel that is needed to spawn an actor.
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...
static void G_ThinkActorGoCrouch(Edict *ent)
Think function for actors that spawn crouched.
static bool G_ActorSpawnIsAllowed(const int num, const int team)
Checks whether the spawn of an actor is allowed for the current running match.
void G_ClientUserinfoChanged(Player &player, const char *userinfo)
called whenever the player updates a userinfo variable.
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.
static void G_ClientSkipActorInfo(void)
Call this if you want to skip some actor netchannel data.
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)
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.
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.
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.
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.
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.
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.
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.
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,...
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.
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 ...
void G_ReactionFireTargetsCreate(const Edict *shooter)
free function to create a table of reaction fire targets for the given edict.
bool G_ReactionFireSettingsReserveTUs(Actor *ent)
Set the reaction fire TU reservation for an actor.
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.
void Info_SetValueForKey(char *s, const size_t size, const char *key, const char *value)
Adds a new entry into string with given value.
void Info_SetValueForKeyAsInteger(char *s, const size_t size, const char *key, const int value)
bool Info_Validate(const char *s)
Some characters are illegal in info strings because they can mess up the server's parsing.
int Info_IntegerForKey(const char *s, const char *key)
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
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
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
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.
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