g_client.c

Go to the documentation of this file.
00001 
00006 /*
00007  Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009  This program is free software; you can redistribute it and/or
00010  modify it under the terms of the GNU General Public License
00011  as published by the Free Software Foundation; either version 2
00012  of the License, or (at your option) any later version.
00013 
00014  This program is distributed in the hope that it will be useful,
00015  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018  See the GNU General Public License for more details.
00019 
00020  You should have received a copy of the GNU General Public License
00021  along with this program; if not, write to the Free Software
00022  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 
00024  */
00025 
00026 #include "g_local.h"
00027 
00028 static chrScoreMission_t scoreMission[MAX_EDICTS];
00029 static int scoreMissionNum = 0;
00030 
00037 unsigned int G_TeamToPM (int team)
00038 {
00039     const player_t *p;
00040     unsigned int playerMask, i;
00041 
00042     playerMask = 0;
00043 
00044     /* don't handle the ai players, here */
00045     for (i = 0, p = game.players; i < game.sv_maxplayersperteam; i++, p++)
00046         if (p->inuse && team == p->pers.team)
00047             playerMask |= (1 << i);
00048 
00049     return playerMask;
00050 }
00051 
00059 unsigned int G_VisToPM (unsigned int vis_mask)
00060 {
00061     const player_t *p;
00062     unsigned int playerMask, i;
00063 
00064     playerMask = 0;
00065 
00066     /* don't handle the ai players, here */
00067     for (i = 0, p = game.players; i < game.sv_maxplayersperteam; i++, p++)
00068         if (p->inuse && (vis_mask & G_TeamToVisMask(p->pers.team)))
00069             playerMask |= (1 << i);
00070 
00071     return playerMask;
00072 }
00073 
00080 void G_ClientPrintf (const player_t *player, int printLevel, const char *fmt, ...)
00081 {
00082     va_list ap;
00083 
00084     /* there is no client for an AI controlled player on the server where we
00085      * could send the message to */
00086     if (G_IsAIPlayer(player))
00087         return;
00088 
00089     va_start(ap, fmt);
00090     gi.PlayerPrintf(player, printLevel, fmt, ap);
00091     va_end(ap);
00092 }
00093 
00099 void G_GiveTimeUnits (int team)
00100 {
00101     edict_t *ent = NULL;
00102 
00103     while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, team))) {
00104         G_ActorGiveTimeUnits(ent);
00105         G_SendStats(ent);
00106     }
00107 }
00108 
00109 void G_SendState (unsigned int playerMask, const edict_t *ent)
00110 {
00111     gi.AddEvent(playerMask & G_TeamToPM(ent->team), EV_ACTOR_STATECHANGE);
00112     gi.WriteShort(ent->number);
00113     gi.WriteShort(ent->state);
00114 
00115     gi.AddEvent(playerMask & ~G_TeamToPM(ent->team), EV_ACTOR_STATECHANGE);
00116     gi.WriteShort(ent->number);
00117     gi.WriteShort(ent->state & STATE_PUBLIC);
00118 }
00119 
00125 static void G_SendParticle (unsigned int playerMask, const edict_t *ent)
00126 {
00127     gi.AddEvent(playerMask, EV_PARTICLE_APPEAR);
00128     gi.WriteShort(ent->number);
00129     gi.WriteShort(ent->spawnflags);
00130     gi.WriteString(ent->particle);
00131 }
00132 
00140 static void G_EdictAppear (unsigned int playerMask, const edict_t *ent)
00141 {
00142     gi.AddEvent(playerMask, EV_ENT_APPEAR);
00143     gi.WriteShort(ent->number);
00144     gi.WriteByte(ent->type);
00145     gi.WriteGPos(ent->pos);
00146 }
00147 
00159 void G_AppearPerishEvent (unsigned int playerMask, qboolean appear, edict_t *check, const edict_t *ent)
00160 {
00161     /* test for pointless player mask */
00162     if (!playerMask)
00163         return;
00164 
00165     if (appear) {
00166         /* appear */
00167         switch (check->type) {
00168         case ET_ACTOR:
00169         case ET_ACTOR2x2:
00170             /* parsed in CL_ActorAppear */
00171             gi.AddEvent(playerMask, EV_ACTOR_APPEAR);
00172             gi.WriteShort(check->number);
00173             gi.WriteShort(ent && ent->number > 0 ? ent->number : SKIP_LOCAL_ENTITY);
00174             gi.WriteByte(check->team);
00175             gi.WriteByte(check->chr.teamDef ? check->chr.teamDef->idx : NONE);
00176             gi.WriteByte(check->chr.gender);
00177             gi.WriteByte(check->pnum);
00178             gi.WriteGPos(check->pos);
00179             gi.WriteByte(check->dir);
00180             if (RIGHT(check)) {
00181                 gi.WriteShort(RIGHT(check)->item.t->idx);
00182             } else {
00183                 gi.WriteShort(NONE);
00184             }
00185 
00186             if (LEFT(check)) {
00187                 gi.WriteShort(LEFT(check)->item.t->idx);
00188             } else {
00189                 gi.WriteShort(NONE);
00190             }
00191 
00192             gi.WriteShort(check->body);
00193             gi.WriteShort(check->head);
00194             gi.WriteByte(check->chr.skin);
00195             gi.WriteShort(check->state & STATE_PUBLIC);
00196             gi.WriteByte(check->fieldSize);
00197             gi.WriteByte(GET_TU(check->chr.score.skills[ABILITY_SPEED]));
00198             gi.WriteByte(min(MAX_SKILL, GET_MORALE(check->chr.score.skills[ABILITY_MIND])));
00199             gi.WriteShort(check->chr.maxHP);
00200 
00201             {
00202                 const int mask = G_TeamToPM(check->team) & playerMask;
00203                 if (mask) {
00204                     gi.AddEvent(mask, EV_ACTOR_STATECHANGE);
00205                     gi.WriteShort(check->number);
00206                     gi.WriteShort(check->state);
00207                     G_SendInventory(mask, check);
00208                 }
00209             }
00210             break;
00211 
00212         case ET_ITEM:
00213             G_EdictAppear(playerMask, check);
00214             G_SendInventory(playerMask, check);
00215             break;
00216 
00217         case ET_PARTICLE:
00218             G_EdictAppear(playerMask, check);
00219             G_SendParticle(playerMask, check);
00220             break;
00221 
00222         default:
00223             if (G_IsVisibleOnBattlefield(check))
00224                 gi.Error("Missing edict type %i in G_AppearPerishEvent", check->type);
00225             break;
00226         }
00227     } else if (G_IsVisibleOnBattlefield(check)) {
00228         /* disappear */
00229         gi.AddEvent(playerMask, EV_ENT_PERISH);
00230         gi.WriteShort(check->number);
00231         check->visflags = 0;
00232     }
00233 }
00234 
00239 void G_CenterView (const edict_t *ent)
00240 {
00241     gi.AddEvent(G_VisToPM(ent->visflags), EV_CENTERVIEW);
00242     gi.WriteGPos(ent->pos);
00243 }
00244 
00255 void G_SendInvisible (player_t* player)
00256 {
00257     const int team = player->pers.team;
00258 
00259     assert(team != TEAM_NO_ACTIVE);
00260     if (level.num_alive[team]) {
00261         edict_t* ent = NULL;
00262         /* check visibility */
00263         while ((ent = G_EdictsGetNextActor(ent))) {
00264             if (ent->team != team) {
00265                 /* not visible for this team - so add the le only */
00266                 if (!G_IsVisibleForTeam(ent, team)) {
00267                     /* parsed in CL_ActorAdd */
00268                     gi.AddEvent(G_PlayerToPM(player), EV_ACTOR_ADD);
00269                     gi.WriteShort(ent->number);
00270                     gi.WriteByte(ent->team);
00271                     gi.WriteByte(ent->chr.teamDef ? ent->chr.teamDef->idx : NONE);
00272                     gi.WriteByte(ent->chr.gender);
00273                     gi.WriteByte(ent->pnum);
00274                     gi.WriteGPos(ent->pos);
00275                     gi.WriteShort(ent->state & STATE_PUBLIC);
00276                     gi.WriteByte(ent->fieldSize);
00277                 }
00278             }
00279         }
00280     }
00281 }
00282 
00288 int G_GetActiveTeam (void)
00289 {
00290     return level.activeTeam;
00291 }
00292 
00293 
00301 static qboolean G_ActionCheck (const player_t *player, edict_t *ent, int TU)
00302 {
00303     /* don't check for a player - but maybe a server action */
00304     if (!player)
00305         return qtrue;
00306 
00307     if (!ent || !ent->inuse) {
00308         G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - object not present!\n"));
00309         return qfalse;
00310     }
00311 
00312     if (ent->type != ET_ACTOR && ent->type != ET_ACTOR2x2) {
00313         G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not an actor!\n"));
00314         return qfalse;
00315     }
00316 
00317     if (G_IsStunned(ent)) {
00318         G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is stunned!\n"));
00319         return qfalse;
00320     }
00321 
00322     if (G_IsDead(ent)) {
00323         G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is dead!\n"));
00324         return qfalse;
00325     }
00326 
00327     if (ent->team != player->pers.team) {
00328         G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not on same team!\n"));
00329         return qfalse;
00330     }
00331 
00332     if (ent->pnum != player->num) {
00333         G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - no control over allied actors!\n"));
00334         return qfalse;
00335     }
00336 
00337     if (TU > G_ActorUsableTUs(ent)) {
00338         return qfalse;
00339     }
00340 
00341     /* could be possible */
00342     return qtrue;
00343 }
00344 
00352 qboolean G_ActionCheckForCurrentTeam (const player_t *player, edict_t *ent, int TU)
00353 {
00354     /* don't check for a player - but maybe a server action */
00355     if (!player)
00356         return qtrue;
00357 
00358     /* a generic tester if an action could be possible */
00359     if (level.activeTeam != player->pers.team) {
00360         G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - this isn't your round!\n"));
00361         return qfalse;
00362     }
00363 
00364     return G_ActionCheck(player, ent, TU);
00365 }
00366 
00375 qboolean G_ActionCheckWithoutTeam (const player_t *player, edict_t *ent, int TU)
00376 {
00377     return G_ActionCheck(player, ent, TU);
00378 }
00379 
00383 static void G_ClientTurn (player_t * player, edict_t* ent, byte dv)
00384 {
00385     const int dir = getDVdir(dv);
00386 
00387     /* check if action is possible */
00388     if (!G_ActionCheckForCurrentTeam(player, ent, TU_TURN))
00389         return;
00390 
00391     /* check if we're already facing that direction */
00392     if (ent->dir == dir)
00393         return;
00394 
00395     /* do the turn */
00396     G_ActorDoTurn(ent, dir);
00397     G_ActorUseTU(ent, TU_TURN);
00398 
00399     /* send the turn */
00400     G_EventActorTurn(ent);
00401 
00402     /* send the new TUs */
00403     G_SendStats(ent);
00404 
00405     /* end the event */
00406     gi.EndEvents();
00407 }
00408 
00415 static void G_ClientStateChangeUpdate (edict_t *ent)
00416 {
00417     /* Send the state change. */
00418     G_SendState(G_VisToPM(ent->visflags), ent);
00419 
00420     /* Check if the player appears/perishes, seen from other teams. */
00421     G_CheckVis(ent, qtrue);
00422 
00423     /* Calc new vis for this player. */
00424     G_CheckVisTeamAll(ent->team, qfalse, ent);
00425 
00426     /* Send the new TUs. */
00427     G_SendStats(ent);
00428 
00429     /* End the event. */
00430     gi.EndEvents();
00431 }
00432 
00442 void G_ClientStateChange (const player_t* player, edict_t* ent, int reqState, qboolean checkaction)
00443 {
00444     /* Check if any action is possible. */
00445     if (checkaction && !G_ActionCheckForCurrentTeam(player, ent, 0))
00446         return;
00447 
00448     if (!reqState)
00449         return;
00450 
00451     switch (reqState) {
00452     case STATE_CROUCHED: /* Toggle between crouch/stand. */
00453         /* Check if player has enough TUs (TU_CROUCH TUs for crouch/uncrouch). */
00454         if (!checkaction || G_ActionCheckForCurrentTeam(player, ent, TU_CROUCH)) {
00455             G_ToggleCrouched(ent);
00456             G_ActorUseTU(ent, TU_CROUCH);
00457             G_ActorSetMaxs(ent);
00458         }
00459         break;
00460     case ~STATE_REACTION: /* Request to turn off reaction fire. */
00461         if (G_IsReaction(ent)) {
00462             if (G_IsShaken(ent)) {
00463                 G_ClientPrintf(player, PRINT_HUD, _("Currently shaken, won't let their guard down.\n"));
00464             } else {
00465                 /* Turn off reaction fire. */
00466                 G_RemoveReaction(ent);
00467                 G_ActorReserveTUs(ent, 0, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
00468             }
00469         }
00470         break;
00471     /* Request to turn on multi- or single-reaction fire mode. */
00472     case STATE_REACTION_MANY:
00473     case STATE_REACTION_ONCE:
00474         /* Disable reaction fire. */
00475         G_RemoveReaction(ent);
00476 
00477         if (G_ReactionFireSetDefault(ent) && G_ReactionFireCanBeEnabled(ent)) {
00478             const int TUs = G_ActorGetTUForReactionFire(ent);
00479             /* Enable requested reaction fire. */
00480             G_SetState(ent, reqState);
00481             G_ActorReserveTUs(ent, TUs, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
00482         } else {
00483             G_ActorReserveTUs(ent, 0, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
00484         }
00485         break;
00486     default:
00487         gi.DPrintf("G_ClientStateChange: unknown request %i, ignoring\n", reqState);
00488         return;
00489     }
00490 
00491     /* Only activate the events - network stuff is handled in the calling function */
00492     if (!checkaction)
00493         return;
00494 
00495     G_ClientStateChangeUpdate(ent);
00496 }
00497 
00502 qboolean G_ClientCanReload (edict_t *ent, containerIndex_t containerID)
00503 {
00504     invList_t *ic;
00505     containerIndex_t container;
00506     objDef_t *weapon;
00507 
00508     if (CONTAINER(ent, containerID)) {
00509         weapon = CONTAINER(ent, containerID)->item.t;
00510     } else if (containerID == gi.csi->idLeft && RIGHT(ent)->item.t->holdTwoHanded) {
00511         /* Check for two-handed weapon */
00512         containerID = gi.csi->idRight;
00513         weapon = CONTAINER(ent, containerID)->item.t;
00514     } else
00515         return qfalse;
00516 
00517     assert(weapon);
00518 
00519     /* also try the temp containers */
00520     for (container = 0; container < gi.csi->numIDs; container++)
00521         for (ic = CONTAINER(ent, container); ic; ic = ic->next)
00522             if (INVSH_LoadableInWeapon(ic->item.t, weapon))
00523                 return qtrue;
00524     return qfalse;
00525 }
00526 
00533 void G_ClientGetWeaponFromInventory (edict_t *ent)
00534 {
00535     invList_t *ic;
00536     invList_t *icFinal;
00537     invDef_t *invDef;
00538     int tu;
00539     containerIndex_t container;
00540     invDef_t *bestContainer;
00541 
00542     /* e.g. bloodspiders are not allowed to carry or collect weapons */
00543     if (!ent->chr.teamDef->weapons)
00544         return;
00545 
00546     /* search for weapons and select the one that is available easily */
00547     tu = 100;
00548     invDef = INVDEF(gi.csi->idRight);
00549     bestContainer = NULL;
00550     icFinal = NULL;
00551 
00552     /* also try the temp containers */
00553     for (container = 0; container < gi.csi->numIDs; container++) {
00554         if (INVDEF(container)->out < tu) {
00555             /* Once we've found at least one clip, there's no point
00556              * searching other containers if it would take longer
00557              * to retrieve the ammo from them than the one
00558              * we've already found. */
00559             for (ic = CONTAINER(ent, container); ic; ic = ic->next) {
00560                 assert(ic->item.t);
00561                 if (ic->item.t->weapon && (ic->item.a > 0 || !ic->item.t->reload)) {
00562                     icFinal = ic;
00563                     bestContainer = INVDEF(container);
00564                     tu = bestContainer->out;
00565                     break;
00566                 }
00567             }
00568         }
00569     }
00570 
00571     /* send request */
00572     if (bestContainer)
00573         G_ActorInvMove(ent, bestContainer, icFinal, invDef, 0, 0, qtrue);
00574 }
00575 
00586 qboolean G_ClientUseEdict (const player_t *player, edict_t *actor, edict_t *edict)
00587 {
00588     /* check whether the actor has sufficient TUs to 'use' this edicts */
00589     if (!G_ActionCheckForCurrentTeam(player, actor, edict->TU))
00590         return qfalse;
00591 
00592     if (!G_UseEdict(edict, actor))
00593         return qfalse;
00594 
00595     /* using a group of edicts only costs TUs once (for the master) */
00596     G_ActorUseTU(actor, edict->TU);
00597     /* send the new TUs */
00598     G_SendStats(actor);
00599 
00600     gi.EndEvents();
00601 
00602     return qtrue;
00603 }
00604 
00610 int G_ClientAction (player_t * player)
00611 {
00612     player_action_t action;
00613     int num;
00614     pos3_t pos;
00615     int i;
00616     fireDefIndex_t firemode;
00617     int from, fx, fy, to, tx, ty;
00618     actorHands_t hand, fmIdx, objIdx;
00619     int resCrouch, resShot;
00620     edict_t *ent;
00621     const char *format;
00622 
00623     /* read the header */
00624     action = gi.ReadByte();
00625     num = gi.ReadShort();
00626 
00627     ent = G_EdictsGetByNum(num);
00628     if (ent == NULL)
00629         return action;
00630 
00631     format = pa_format[action];
00632 
00633     switch (action) {
00634     case PA_NULL:
00635         /* do nothing on a null action */
00636         break;
00637 
00638     case PA_TURN:
00639         gi.ReadFormat(format, &i);
00640         G_ClientTurn(player, ent, (byte) i);
00641         break;
00642 
00643     case PA_MOVE:
00644         gi.ReadFormat(format, &pos);
00645         G_ClientMove(player, player->pers.team, ent, pos);
00646         break;
00647 
00648     case PA_STATE:
00649         gi.ReadFormat(format, &i);
00650         G_ClientStateChange(player, ent, i, qtrue);
00651         break;
00652 
00653     case PA_SHOOT:
00654         gi.ReadFormat(format, &pos, &i, &firemode, &from);
00655         G_ClientShoot(player, ent, pos, i, firemode, NULL, qtrue, from);
00656         break;
00657 
00658     case PA_INVMOVE:
00659         gi.ReadFormat(format, &from, &fx, &fy, &to, &tx, &ty);
00660 
00661         if (from < 0 || from >= gi.csi->numIDs || to < 0 || to >= gi.csi->numIDs) {
00662             gi.DPrintf("G_ClientAction: PA_INVMOVE Container index out of range. (from: %i, to: %i)\n", from, to);
00663         } else {
00664             invDef_t *fromPtr = INVDEF(from);
00665             invDef_t *toPtr = INVDEF(to);
00666             invList_t *fromItem = INVSH_SearchInInventory(&ent->chr.i, fromPtr, fx, fy);
00667             if (!fromItem)
00668                 gi.Error("Could not find item in inventory of ent %i (type %i) at %i:%i",
00669                         ent->number, ent->type, fx, fy);
00670             G_ActorInvMove(ent, fromPtr, fromItem, toPtr, tx, ty, qtrue);
00671         }
00672         break;
00673 
00674     case PA_USE_DOOR:
00675         if (ent->clientAction) {
00676             edict_t *door;
00677 
00678             /* read the door the client wants to open */
00679             gi.ReadFormat(format, &i);
00680 
00681             /* get the door edict */
00682             door = G_EdictsGetByNum(i);
00683 
00684             /* maybe the door is no longer 'alive' because it was destroyed */
00685             if (door && ent->clientAction == door) {
00686                 /* check whether it's part of an edict group but not the master */
00687                 if (door->flags & FL_GROUPSLAVE)
00688                     door = door->groupMaster;
00689 
00690                 G_ActorUseDoor(ent, door);
00691             }
00692         }
00693         break;
00694 
00695     case PA_REACT_SELECT:
00696         gi.ReadFormat(format, &hand, &fmIdx, &objIdx);
00697         G_ReactionFireUpdate(ent, fmIdx, hand, INVSH_GetItemByIDX(objIdx));
00698         break;
00699 
00700     case PA_RESERVE_STATE:
00701         gi.ReadFormat(format, &resShot, &resCrouch);
00702 
00703         G_ActorReserveTUs(ent, ent->chr.reservedTus.reaction, resShot, resCrouch);
00704         break;
00705 
00706     default:
00707         gi.Error("G_ClientAction: Unknown action!\n");
00708     }
00709     return action;
00710 }
00711 
00717 static void G_GetTeam (player_t * player)
00718 {
00719     player_t *p;
00720     int i, j;
00721     int playersInGame = 0;
00722 
00723     /* player has already a team */
00724     if (player->pers.team > 0) {
00725         Com_DPrintf(DEBUG_GAME, "Player %s is already on team %i\n", player->pers.netname, player->pers.team);
00726         return;
00727     }
00728 
00729     /* number of currently connected players (no ai players) */
00730     for (j = 0, p = game.players; j < game.sv_maxplayersperteam; j++, p++)
00731         if (p->inuse)
00732             playersInGame++;
00733 
00734     /* randomly assign a teamnumber in deathmatch games */
00735     if (playersInGame <= 1 && sv_maxclients->integer > 1 && !sv_teamplay->integer) {
00736         int spawnCheck[MAX_TEAMS];
00737         int spawnSpots = 0;
00738         int randomSpot;
00739         /* skip civilian teams */
00740         for (i = TEAM_PHALANX; i < MAX_TEAMS; i++) {
00741             spawnCheck[i] = 0;
00742             /* check whether there are spawnpoints for this team */
00743             if (level.num_spawnpoints[i])
00744                 spawnCheck[spawnSpots++] = i;
00745         }
00746         /* we need at least 2 different team spawnpoints for multiplayer in a death match game */
00747         if (spawnSpots < 2)
00748             gi.Error("G_GetTeam: Not enough spawn spots in map!");
00749 
00750         /* assign random valid team number */
00751         randomSpot = rand() % spawnSpots;
00752         G_SetTeamForPlayer(player, spawnCheck[randomSpot]);
00753         gi.DPrintf("You have been randomly assigned to team %i\n", player->pers.team);
00754         return;
00755     }
00756 
00757     /* find a team */
00758     if (sv_maxclients->integer == 1)
00759         G_SetTeamForPlayer(player, TEAM_PHALANX);
00760     else if (sv_teamplay->integer) {
00761         /* set the team specified in the userinfo */
00762         gi.DPrintf("Get a team for teamplay for %s\n", player->pers.netname);
00763         i = G_ClientGetTeamNumPref(player);
00764         /* civilians are at team zero */
00765         if (i > TEAM_CIVILIAN && sv_maxteams->integer >= i) {
00766             G_SetTeamForPlayer(player, i);
00767             gi.BroadcastPrintf(PRINT_CONSOLE, "serverconsole: %s has chosen team %i\n", player->pers.netname, i);
00768         } else {
00769             gi.DPrintf("Team %i is not valid - choose a team between 1 and %i\n", i, sv_maxteams->integer);
00770             G_SetTeamForPlayer(player, TEAM_DEFAULT);
00771         }
00772     } else {
00773         /* search team */
00774         gi.DPrintf("Getting a multiplayer team for %s\n", player->pers.netname);
00775         for (i = TEAM_CIVILIAN + 1; i < MAX_TEAMS; i++) {
00776             if (level.num_spawnpoints[i]) {
00777                 qboolean teamAvailable = qtrue;
00778                 /* check if team is in use (only human controlled players) */
00779                 for (j = 0, p = game.players; j < game.sv_maxplayersperteam; j++, p++)
00780                     if (p->inuse && p->pers.team == i) {
00781                         Com_DPrintf(DEBUG_GAME, "Team %i is already in use\n", i);
00782                         /* team already in use */
00783                         teamAvailable = qfalse;
00784                         break;
00785                     }
00786                 if (teamAvailable)
00787                     break;
00788             }
00789         }
00790 
00791         /* set the team */
00792         if (i < MAX_TEAMS) {
00793             /* remove ai player */
00794             for (j = 0, p = game.players + game.sv_maxplayersperteam; j < game.sv_maxplayersperteam; j++, p++)
00795                 if (p->inuse && p->pers.team == i) {
00796                     gi.BroadcastPrintf(PRINT_CONSOLE, "Removing ai player...");
00797                     p->inuse = qfalse;
00798                     break;
00799                 }
00800             Com_DPrintf(DEBUG_GAME, "Assigning %s to team %i\n", player->pers.netname, i);
00801             G_SetTeamForPlayer(player, i);
00802         } else {
00803             gi.DPrintf("No free team - disconnecting '%s'\n", player->pers.netname);
00804             G_ClientDisconnect(player);
00805         }
00806     }
00807 }
00808 
00809 void G_SetTeamForPlayer (player_t* player, const int team)
00810 {
00811     assert(player);
00812     assert(team >= TEAM_NO_ACTIVE && team < MAX_TEAMS);
00813     player->pers.team = team;
00814 
00815     /* if we started in dev mode, we maybe don't have a
00816      * starting position in this map */
00817     if (!g_nospawn->integer) {
00818         if (team >= 0 && team < MAX_TEAMS) {
00819             if (!level.num_spawnpoints[team])
00820                 gi.Error("No spawnpoints for team %i", team);
00821         }
00822     }
00823 
00824     if (!G_IsAIPlayer(player))
00825         Info_SetValueForKeyAsInteger(player->pers.userinfo, sizeof(player->pers.userinfo), "cl_team", team);
00826 }
00827 
00831 int G_ClientGetTeamNum (const player_t * player)
00832 {
00833     assert(player);
00834     return player->pers.team;
00835 }
00836 
00840 int G_ClientGetTeamNumPref (const player_t * player)
00841 {
00842     assert(player);
00843     return atoi(Info_ValueForKey(player->pers.userinfo, "cl_teamnum"));
00844 }
00845 
00849 qboolean G_ClientIsReady (const player_t * player)
00850 {
00851     assert(player);
00852     return player->isReady;
00853 }
00854 
00860 static void G_GetStartingTeam (const player_t* player)
00861 {
00862     int i, j, teamCount = 0;
00863     int playerCount = 0;
00864     int knownTeams[MAX_TEAMS];
00865     player_t *p;
00866 
00867     /* return with no action if activeTeam already assigned or if are in single-player mode */
00868     if (G_MatchIsRunning())
00869         return;
00870 
00871     if (sv_maxclients->integer == 1) {
00872         level.activeTeam = player->pers.team;
00873         return;
00874     }
00875 
00876     /* count number of currently connected unique teams and players (human controlled players only) */
00877     for (i = 0, p = game.players; i < game.sv_maxplayersperteam; i++, p++) {
00878         if (p->inuse) {
00879             playerCount++;
00880             for (j = 0; j < teamCount; j++) {
00881                 if (p->pers.team == knownTeams[j])
00882                     break;
00883             }
00884             if (j == teamCount)
00885                 knownTeams[teamCount++] = p->pers.team;
00886         }
00887     }
00888 
00889     if (teamCount) {
00890         const int teamIndex = (int) (frand() * (teamCount - 1) + 0.5);
00891         G_PrintStats(va("Starting new game: %s with %i teams", level.mapname, teamCount));
00892         level.activeTeam = knownTeams[teamIndex];
00893         for (i = 0, p = game.players; i < game.sv_maxplayersperteam; i++, p++)
00894             if (p->inuse && p->pers.team != level.activeTeam)
00895                 p->roundDone = qtrue;
00896     }
00897 }
00898 
00906 static edict_t *G_ClientGetFreeSpawnPoint (const player_t * player, int spawnType)
00907 {
00908     edict_t *ent = NULL;
00909 
00910     /* Abort for non-spawnpoints */
00911     assert(spawnType == ET_ACTORSPAWN || spawnType == ET_ACTOR2x2SPAWN);
00912 
00913     if (level.randomSpawn) {
00914         edict_t *list[MAX_EDICTS];
00915         int count = 0;
00916         while ((ent = G_EdictsGetNext(ent)))
00917             if (ent->type == spawnType && player->pers.team == ent->team)
00918                 list[count++] = ent;
00919 
00920         if (count)
00921             return list[rand() % count];
00922     } else {
00923         while ((ent = G_EdictsGetNext(ent)))
00924             if (ent->type == spawnType && player->pers.team == ent->team)
00925                 return ent;
00926     }
00927 
00928     return NULL;
00929 }
00930 
00940 static inline qboolean G_ActorSpawnIsAllowed (const int num, const int team)
00941 {
00942     if (sv_maxclients->integer == 1)
00943         return qtrue;
00944 
00945     return (num < sv_maxsoldiersperplayer->integer && level.num_spawned[team] < sv_maxsoldiersperteam->integer);
00946 }
00947 
00952 static void G_ThinkActorDieAfterSpawn (edict_t *ent)
00953 {
00954     G_ActorDieOrStun(ent, NULL);
00955     ent->think = NULL;
00956 }
00957 
00962 static void G_ThinkActorGoCrouch (edict_t *ent)
00963 {
00964     G_ClientStateChange(G_PLAYER_FROM_ENT(ent), ent, STATE_CROUCHED, qtrue);
00965     ent->think = NULL;
00966 }
00967 
00974 edict_t* G_ClientGetFreeSpawnPointForActorSize (const player_t *player, const actorSizeEnum_t actorSize)
00975 {
00976     edict_t *ent;
00977 
00978     if (actorSize == ACTOR_SIZE_NORMAL) {
00979         /* Find valid actor spawn fields for this player. */
00980         ent = G_ClientGetFreeSpawnPoint(player, ET_ACTORSPAWN);
00981         if (ent) {
00982             ent->type = ET_ACTOR;
00983         }
00984     } else if (actorSize == ACTOR_SIZE_2x2) {
00985         /* Find valid actor spawn fields for this player. */
00986         ent = G_ClientGetFreeSpawnPoint(player, ET_ACTOR2x2SPAWN);
00987         if (ent) {
00988             ent->type = ET_ACTOR2x2;
00989             ent->morale = 100;
00990         }
00991     } else {
00992         gi.Error("G_ClientGetFreeSpawnPointForActorSize: unknown fieldSize for actor edict (actorSize: %i)\n",
00993                 actorSize);
00994     }
00995 
00996     if (!ent)
00997         return NULL;
00998 
00999     level.num_alive[ent->team]++;
01000     level.num_spawned[ent->team]++;
01001     ent->pnum = player->num;
01002     ent->chr.fieldSize = actorSize;
01003     ent->fieldSize = ent->chr.fieldSize;
01004     ent->flags |= FL_DESTROYABLE;
01005 
01006     gi.LinkEdict(ent);
01007 
01008     if (ent->spawnflags & STATE_CROUCHED) {
01009         ent->think = G_ThinkActorGoCrouch;
01010         ent->nextthink = 1;
01011     }
01012 
01013     if (ent->spawnflags & STATE_STUN) {
01014         if (ent->spawnflags & STATE_DEAD)
01015             ent->HP = 0;
01016         ent->think = G_ThinkActorDieAfterSpawn;
01017         ent->nextthink = 1;
01018     }
01019 
01020     G_TouchTriggers(ent);
01021 
01022     return ent;
01023 }
01024 
01029 static void G_ClientReadInventory (edict_t *ent)
01030 {
01031     /* inventory */
01032     int nr = gi.ReadShort() / INV_INVENTORY_BYTES;
01033 
01034     for (; nr-- > 0;) {
01035         invDef_t *container;
01036         item_t item;
01037         int x, y;
01038         G_ReadItem(&item, &container, &x, &y);
01039         if (game.i.AddToInventory(&game.i, &ent->chr.i, item, container, x, y, 1) == NULL)
01040             gi.Error("G_ClientReadInventory failed, could not add item '%s' to container %i (x:%i,y:%i)",
01041                     item.t->id, container->id, x, y);
01042     }
01043 }
01044 
01049 static void G_ClientReadCharacter (edict_t *ent)
01050 {
01051     int k;
01052     int teamDefIdx;
01053 
01054     /* model */
01055     ent->chr.ucn = gi.ReadShort();
01056     gi.ReadString(ent->chr.name, sizeof(ent->chr.name));
01057     gi.ReadString(ent->chr.path, sizeof(ent->chr.path));
01058     gi.ReadString(ent->chr.body, sizeof(ent->chr.body));
01059     gi.ReadString(ent->chr.head, sizeof(ent->chr.head));
01060     ent->chr.skin = gi.ReadByte();
01061 
01062     ent->chr.HP = gi.ReadShort();
01063     ent->chr.minHP = ent->chr.HP;
01064     ent->chr.maxHP = gi.ReadShort();
01065     teamDefIdx = gi.ReadByte();
01066     if (teamDefIdx < 0 || teamDefIdx >= MAX_TEAMDEFS)
01067         gi.Error("Invalid team definition index given: %i", teamDefIdx);
01068     ent->chr.teamDef = &gi.csi->teamDef[teamDefIdx];
01069 
01070     ent->chr.gender = gi.ReadByte();
01071     ent->chr.STUN = gi.ReadByte();
01072     ent->chr.morale = gi.ReadByte();
01073 
01074     for (k = 0; k < SKILL_NUM_TYPES + 1; k++) /* new attributes */
01075         ent->chr.score.experience[k] = gi.ReadLong();
01076     for (k = 0; k < SKILL_NUM_TYPES; k++) /* new attributes */
01077         ent->chr.score.skills[k] = gi.ReadByte();
01078     for (k = 0; k < SKILL_NUM_TYPES + 1; k++)
01079         ent->chr.score.initialSkills[k] = gi.ReadByte();
01080     for (k = 0; k < KILLED_NUM_TYPES; k++)
01081         ent->chr.score.kills[k] = gi.ReadShort();
01082     for (k = 0; k < KILLED_NUM_TYPES; k++)
01083         ent->chr.score.stuns[k] = gi.ReadShort();
01084     ent->chr.score.assignedMissions = gi.ReadShort();
01085 }
01086 
01092 static void G_ClientSkipActorInfo (void)
01093 {
01094     int k, j;
01095     edict_t ent;
01096 
01097     G_ClientReadCharacter(&ent);
01098 
01099     /* skip inventory */
01100     j = gi.ReadShort();
01101     for (k = 0; k < j; k++)
01102         gi.ReadByte(); /* inventory */
01103 }
01104 
01110 static void G_ClientAssignDefaultActorValues (edict_t *ent)
01111 {
01112     /* Mission Scores */
01113     memset(&scoreMission[scoreMissionNum], 0, sizeof(scoreMission[scoreMissionNum]));
01114     ent->chr.scoreMission = &scoreMission[scoreMissionNum];
01115     scoreMissionNum++;
01116 
01117     /* set initial vital statistics */
01118     ent->HP = ent->chr.HP;
01119     ent->morale = ent->chr.morale;
01120 
01122     ent->morale = GET_MORALE(ent->chr.score.skills[ABILITY_MIND]);
01123 
01124     /* set models */
01125     ent->body = gi.ModelIndex(CHRSH_CharGetBody(&ent->chr));
01126     ent->head = gi.ModelIndex(CHRSH_CharGetHead(&ent->chr));
01127 }
01128 
01133 void G_ClientInitActorStates (const player_t * player)
01134 {
01135     const int length = gi.ReadByte(); /* Get the actor amount that the client sent. */
01136     int i;
01137 
01138     for (i = 0; i < length; i++) {
01139         const int ucn = gi.ReadShort();
01140         int saveTU;
01141         edict_t *ent = G_ActorGetByUCN(ucn, player->pers.team);
01142         if (!ent)
01143             gi.Error("Could not find character on team %i with unique character number %i", player->pers.team, ucn);
01144 
01145         /* these state changes are not consuming any TUs */
01146         saveTU = ent->TU;
01147         G_ClientStateChange(player, ent, gi.ReadShort(), qfalse);
01148         G_ActorSetTU(ent, saveTU);
01149         G_ClientStateChangeUpdate(ent);
01150     }
01151 }
01152 
01159 void G_ClientTeamInfo (const player_t * player)
01160 {
01161     const int length = gi.ReadByte(); /* Get the actor amount that the client sent. */
01162     int i;
01163 
01164     for (i = 0; i < length; i++) {
01165         const actorSizeEnum_t actorFieldSize = gi.ReadByte();
01166         /* Search for a spawn point for each entry the client sent */
01167         if (player->pers.team == TEAM_NO_ACTIVE || !G_ActorSpawnIsAllowed(i, player->pers.team))
01168             G_ClientSkipActorInfo();
01169         else {
01170             edict_t *ent = G_ClientGetFreeSpawnPointForActorSize(player, actorFieldSize);
01171             if (ent) {
01172                 Com_DPrintf(DEBUG_GAME, "Player: %i - team %i - size: %i\n", player->num, ent->team, ent->fieldSize);
01173 
01174                 G_ClientReadCharacter(ent);
01175 
01176                 G_ClientReadInventory(ent);
01177 
01178                 G_ClientAssignDefaultActorValues(ent);
01179             } else {
01180                 gi.DPrintf("Not enough spawn points for team %i (actorsize: %i)\n", player->pers.team, actorFieldSize);
01181 
01182                 G_ClientSkipActorInfo();
01183             }
01184         }
01185     }
01186 
01187     Com_Printf("Free inventory slots client %s spawn: %i\n", player->pers.netname, game.i.GetUsedSlots(&game.i));
01188 }
01189 
01199 static void G_ClientSendEdictsAndBrushModels (const player_t *player)
01200 {
01201     const int mask = G_PlayerToPM(player);
01202     /* skip the world */
01203     edict_t *ent = G_EdictsGetFirst();
01204 
01205     /* make SOLID_BSP edicts visible to the client */
01206     while ((ent = G_EdictsGetNextInUse(ent))) {
01207         /* brush models that have a type - not the world - keep in
01208          * mind that there are several world edicts in the list in case of
01209          * a map assembly */
01210         if (ent->solid != SOLID_BSP)
01211             continue;
01212 
01213         /* skip the world(s) in case of map assembly */
01214         if (ent->type > ET_NULL) {
01215             gi.AddEvent(mask, EV_ADD_BRUSH_MODEL);
01216             gi.WriteByte(ent->type);
01217             gi.WriteShort(ent->number);
01218             gi.WriteShort(ent->modelindex);
01219             /* strip the higher bits - only send levelflags */
01220             gi.WriteByte(ent->spawnflags & 0xFF);
01221             gi.WritePos(ent->origin);
01222             gi.WritePos(ent->angles);
01223             gi.WriteShort(ent->speed);
01224             gi.WriteByte(ent->angle);
01225             ent->visflags |= ~ent->visflags;
01226         }
01227     }
01228 }
01229 
01235 qboolean G_ClientBegin (player_t* player)
01236 {
01237     player->began = qtrue;
01238     level.numplayers++;
01239 
01240     /* find a team */
01241     G_GetTeam(player);
01242     if (!player->began)
01243         return qfalse;
01244 
01245     gi.ConfigString(CS_PLAYERCOUNT, "%i", level.numplayers);
01246 
01247     /* spawn camera (starts client rendering) */
01248     gi.AddEvent(G_PlayerToPM(player), EV_START | EVENT_INSTANTLY);
01249     gi.WriteByte(sv_teamplay->integer);
01250 
01251     /* send things like doors and breakables */
01252     G_ClientSendEdictsAndBrushModels(player);
01253 
01254     /* ensure that the start event is send */
01255     gi.EndEvents();
01256 
01257     /* set the net name */
01258     gi.ConfigString(CS_PLAYERNAMES + player->num, "%s", player->pers.netname);
01259 
01260     /* inform all clients */
01261     gi.BroadcastPrintf(PRINT_CONSOLE, "%s has joined team %i\n", player->pers.netname, player->pers.team);
01262 
01263     return qtrue;
01264 }
01265 
01275 void G_ClientSpawn (player_t * player)
01276 {
01277     G_GetStartingTeam(player);
01278 
01279     /* do all the init events here... */
01280     /* reset the data */
01281     gi.AddEvent(G_PlayerToPM(player), EV_RESET | EVENT_INSTANTLY);
01282     gi.WriteByte(player->pers.team);
01283     gi.WriteByte(level.activeTeam);
01284 
01285     /* show visible actors and add invisible actor */
01286     G_ClearVisFlags(player->pers.team);
01287     G_CheckVisPlayer(player, qfalse);
01288     G_SendInvisible(player);
01289 
01290     /* submit stats */
01291     G_SendPlayerStats(player);
01292 
01293     /* give time units */
01294     G_GiveTimeUnits(player->pers.team);
01295 
01296     /* ensure that the last event is send, too */
01297     gi.EndEvents();
01298 
01299     /* inform all clients */
01300     gi.BroadcastPrintf(PRINT_CONSOLE, "%s has taken control over team %i.\n", player->pers.netname, player->pers.team);
01301 }
01302 
01307 void G_ClientUserinfoChanged (player_t * player, char *userinfo)
01308 {
01309     const char *s;
01310     const qboolean alreadyReady = player->isReady;
01311 
01312     /* check for malformed or illegal info strings */
01313     if (!Info_Validate(userinfo))
01314         Q_strncpyz(userinfo, "\\cl_name\\badinfo", sizeof(userinfo));
01315 
01316     /* set name */
01317     s = Info_ValueForKey(userinfo, "cl_name");
01318     Q_strncpyz(player->pers.netname, s, sizeof(player->pers.netname));
01319 
01320     Q_strncpyz(player->pers.userinfo, userinfo, sizeof(player->pers.userinfo));
01321 
01322     s = Info_ValueForKey(userinfo, "cl_autostand");
01323     player->autostand = atoi(s);
01324 
01325     s = Info_ValueForKey(userinfo, "cl_reactionleftover");
01326     player->reactionLeftover = atoi(s);
01327 
01328     s = Info_ValueForKey(userinfo, "cl_ready");
01329     player->isReady = atoi(s);
01330 
01331     /* send the updated config string */
01332     gi.ConfigString(CS_PLAYERNAMES + player->num, "%s", player->pers.netname);
01333 
01334     /* try to update to the prefered team */
01335     if (!G_MatchIsRunning()) {
01336         /* if the player is marked as ready he can't change his team */
01337         if (!alreadyReady || !player->isReady) {
01338             player->pers.team = TEAM_NO_ACTIVE;
01339             G_GetTeam(player);
01340         } else {
01341             Com_DPrintf(DEBUG_GAME, "G_ClientUserinfoChanged: player %s is already marked as being ready\n",
01342                     player->pers.netname);
01343         }
01344     }
01345 }
01346 
01359 qboolean G_ClientConnect (player_t * player, char *userinfo, size_t userinfoSize)
01360 {
01361     const char *value;
01362 
01363     value = Info_ValueForKey(userinfo, "ip");
01364 
01365     Com_Printf("connection attempt from %s\n", value);
01366 
01367     /* check to see if they are on the banned IP list */
01368     if (SV_FilterPacket(value)) {
01369         Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", REJ_BANNED);
01370         return qfalse;
01371     }
01372 
01373     if (!G_PlayerToPM(player)) {
01374         Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", "Server is full");
01375         return qfalse;
01376     }
01377 
01378     value = Info_ValueForKey(userinfo, "password");
01379     if (password->string[0] != '\0' && strcmp(password->string, "none") && strcmp(password->string, value)) {
01380         Info_SetValueForKey(userinfo, userinfoSize, "rejmsg", REJ_PASSWORD_REQUIRED_OR_INCORRECT);
01381         return qfalse;
01382     }
01383 
01384     /* fix for fast reconnects after a disconnect */
01385     if (player->inuse) {
01386         gi.BroadcastPrintf(PRINT_CONSOLE, "%s already in use.\n", player->pers.netname);
01387         G_ClientDisconnect(player);
01388     }
01389 
01390     /* reset persistent data */
01391     memset(&player->pers, 0, sizeof(player->pers));
01392     G_ClientUserinfoChanged(player, userinfo);
01393 
01394     gi.BroadcastPrintf(PRINT_CONSOLE, "%s is connecting...\n", player->pers.netname);
01395     return qtrue;
01396 }
01397 
01401 void G_ClientDisconnect (player_t * player)
01402 {
01403 #if 0
01404     edict_t *ent = NULL;
01405 #endif
01406 
01407     /* only if the player already sent his began */
01408     if (player->began) {
01409         level.numplayers--;
01410         gi.ConfigString(CS_PLAYERCOUNT, "%i", level.numplayers);
01411 
01412         if (level.activeTeam == player->pers.team)
01413             G_ClientEndRound(player);
01414 
01415         /* if no more players are connected - stop the server */
01416         G_MatchEndCheck();
01417     }
01418 
01419 #if 0
01420     /* now let's remove all the edicts that belongs to this player */
01421     while ((ent = G_EdictsGetNextLivingActor(ent))) {
01422         if (ent->pnum == player->num)
01423             G_ActorDie(ent, STATE_DEAD, NULL);
01424     }
01425     G_MatchEndCheck();
01426 #endif
01427 
01428     player->began = qfalse;
01429     player->roundDone = qfalse;
01430     player->isReady = qfalse;
01431 
01432     gi.BroadcastPrintf(PRINT_CONSOLE, "%s disconnected.\n", player->pers.netname);
01433 }
01434 
01438 void G_ResetClientData (void)
01439 {
01440     scoreMissionNum = 0;
01441     memset(scoreMission, 0, sizeof(scoreMission));
01442 }

Generated by  doxygen 1.6.2