00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
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
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
00085
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
00162 if (!playerMask)
00163 return;
00164
00165 if (appear) {
00166
00167 switch (check->type) {
00168 case ET_ACTOR:
00169 case ET_ACTOR2x2:
00170
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
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
00263 while ((ent = G_EdictsGetNextActor(ent))) {
00264 if (ent->team != team) {
00265
00266 if (!G_IsVisibleForTeam(ent, team)) {
00267
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
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
00342 return qtrue;
00343 }
00344
00352 qboolean G_ActionCheckForCurrentTeam (const player_t *player, edict_t *ent, int TU)
00353 {
00354
00355 if (!player)
00356 return qtrue;
00357
00358
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
00388 if (!G_ActionCheckForCurrentTeam(player, ent, TU_TURN))
00389 return;
00390
00391
00392 if (ent->dir == dir)
00393 return;
00394
00395
00396 G_ActorDoTurn(ent, dir);
00397 G_ActorUseTU(ent, TU_TURN);
00398
00399
00400 G_EventActorTurn(ent);
00401
00402
00403 G_SendStats(ent);
00404
00405
00406 gi.EndEvents();
00407 }
00408
00415 static void G_ClientStateChangeUpdate (edict_t *ent)
00416 {
00417
00418 G_SendState(G_VisToPM(ent->visflags), ent);
00419
00420
00421 G_CheckVis(ent, qtrue);
00422
00423
00424 G_CheckVisTeamAll(ent->team, qfalse, ent);
00425
00426
00427 G_SendStats(ent);
00428
00429
00430 gi.EndEvents();
00431 }
00432
00442 void G_ClientStateChange (const player_t* player, edict_t* ent, int reqState, qboolean checkaction)
00443 {
00444
00445 if (checkaction && !G_ActionCheckForCurrentTeam(player, ent, 0))
00446 return;
00447
00448 if (!reqState)
00449 return;
00450
00451 switch (reqState) {
00452 case STATE_CROUCHED:
00453
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:
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
00466 G_RemoveReaction(ent);
00467 G_ActorReserveTUs(ent, 0, ent->chr.reservedTus.shot, ent->chr.reservedTus.crouch);
00468 }
00469 }
00470 break;
00471
00472 case STATE_REACTION_MANY:
00473 case STATE_REACTION_ONCE:
00474
00475 G_RemoveReaction(ent);
00476
00477 if (G_ReactionFireSetDefault(ent) && G_ReactionFireCanBeEnabled(ent)) {
00478 const int TUs = G_ActorGetTUForReactionFire(ent);
00479
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
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
00512 containerID = gi.csi->idRight;
00513 weapon = CONTAINER(ent, containerID)->item.t;
00514 } else
00515 return qfalse;
00516
00517 assert(weapon);
00518
00519
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
00543 if (!ent->chr.teamDef->weapons)
00544 return;
00545
00546
00547 tu = 100;
00548 invDef = INVDEF(gi.csi->idRight);
00549 bestContainer = NULL;
00550 icFinal = NULL;
00551
00552
00553 for (container = 0; container < gi.csi->numIDs; container++) {
00554 if (INVDEF(container)->out < tu) {
00555
00556
00557
00558
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
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
00589 if (!G_ActionCheckForCurrentTeam(player, actor, edict->TU))
00590 return qfalse;
00591
00592 if (!G_UseEdict(edict, actor))
00593 return qfalse;
00594
00595
00596 G_ActorUseTU(actor, edict->TU);
00597
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
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
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
00679 gi.ReadFormat(format, &i);
00680
00681
00682 door = G_EdictsGetByNum(i);
00683
00684
00685 if (door && ent->clientAction == door) {
00686
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
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
00730 for (j = 0, p = game.players; j < game.sv_maxplayersperteam; j++, p++)
00731 if (p->inuse)
00732 playersInGame++;
00733
00734
00735 if (playersInGame <= 1 && sv_maxclients->integer > 1 && !sv_teamplay->integer) {
00736 int spawnCheck[MAX_TEAMS];
00737 int spawnSpots = 0;
00738 int randomSpot;
00739
00740 for (i = TEAM_PHALANX; i < MAX_TEAMS; i++) {
00741 spawnCheck[i] = 0;
00742
00743 if (level.num_spawnpoints[i])
00744 spawnCheck[spawnSpots++] = i;
00745 }
00746
00747 if (spawnSpots < 2)
00748 gi.Error("G_GetTeam: Not enough spawn spots in map!");
00749
00750
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
00758 if (sv_maxclients->integer == 1)
00759 G_SetTeamForPlayer(player, TEAM_PHALANX);
00760 else if (sv_teamplay->integer) {
00761
00762 gi.DPrintf("Get a team for teamplay for %s\n", player->pers.netname);
00763 i = G_ClientGetTeamNumPref(player);
00764
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
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
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
00783 teamAvailable = qfalse;
00784 break;
00785 }
00786 if (teamAvailable)
00787 break;
00788 }
00789 }
00790
00791
00792 if (i < MAX_TEAMS) {
00793
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
00816
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
00868 if (G_MatchIsRunning())
00869 return;
00870
00871 if (sv_maxclients->integer == 1) {
00872 level.activeTeam = player->pers.team;
00873 return;
00874 }
00875
00876
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
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
00980 ent = G_ClientGetFreeSpawnPoint(player, ET_ACTORSPAWN);
00981 if (ent) {
00982 ent->type = ET_ACTOR;
00983 }
00984 } else if (actorSize == ACTOR_SIZE_2x2) {
00985
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
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
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++)
01075 ent->chr.score.experience[k] = gi.ReadLong();
01076 for (k = 0; k < SKILL_NUM_TYPES; k++)
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
01100 j = gi.ReadShort();
01101 for (k = 0; k < j; k++)
01102 gi.ReadByte();
01103 }
01104
01110 static void G_ClientAssignDefaultActorValues (edict_t *ent)
01111 {
01112
01113 memset(&scoreMission[scoreMissionNum], 0, sizeof(scoreMission[scoreMissionNum]));
01114 ent->chr.scoreMission = &scoreMission[scoreMissionNum];
01115 scoreMissionNum++;
01116
01117
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
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();
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
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();
01162 int i;
01163
01164 for (i = 0; i < length; i++) {
01165 const actorSizeEnum_t actorFieldSize = gi.ReadByte();
01166
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
01203 edict_t *ent = G_EdictsGetFirst();
01204
01205
01206 while ((ent = G_EdictsGetNextInUse(ent))) {
01207
01208
01209
01210 if (ent->solid != SOLID_BSP)
01211 continue;
01212
01213
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
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
01241 G_GetTeam(player);
01242 if (!player->began)
01243 return qfalse;
01244
01245 gi.ConfigString(CS_PLAYERCOUNT, "%i", level.numplayers);
01246
01247
01248 gi.AddEvent(G_PlayerToPM(player), EV_START | EVENT_INSTANTLY);
01249 gi.WriteByte(sv_teamplay->integer);
01250
01251
01252 G_ClientSendEdictsAndBrushModels(player);
01253
01254
01255 gi.EndEvents();
01256
01257
01258 gi.ConfigString(CS_PLAYERNAMES + player->num, "%s", player->pers.netname);
01259
01260
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
01280
01281 gi.AddEvent(G_PlayerToPM(player), EV_RESET | EVENT_INSTANTLY);
01282 gi.WriteByte(player->pers.team);
01283 gi.WriteByte(level.activeTeam);
01284
01285
01286 G_ClearVisFlags(player->pers.team);
01287 G_CheckVisPlayer(player, qfalse);
01288 G_SendInvisible(player);
01289
01290
01291 G_SendPlayerStats(player);
01292
01293
01294 G_GiveTimeUnits(player->pers.team);
01295
01296
01297 gi.EndEvents();
01298
01299
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
01313 if (!Info_Validate(userinfo))
01314 Q_strncpyz(userinfo, "\\cl_name\\badinfo", sizeof(userinfo));
01315
01316
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
01332 gi.ConfigString(CS_PLAYERNAMES + player->num, "%s", player->pers.netname);
01333
01334
01335 if (!G_MatchIsRunning()) {
01336
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
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
01385 if (player->inuse) {
01386 gi.BroadcastPrintf(PRINT_CONSOLE, "%s already in use.\n", player->pers.netname);
01387 G_ClientDisconnect(player);
01388 }
01389
01390
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
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
01416 G_MatchEndCheck();
01417 }
01418
01419 #if 0
01420
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 }