g_actor.c

Go to the documentation of this file.
00001 
00005 /*
00006 Copyright (C) 2002-2010 UFO: Alien Invasion.
00007 
00008 This program is free software; you can redistribute it and/or
00009 modify it under the terms of the GNU General Public License
00010 as published by the Free Software Foundation; either version 2
00011 of the License, or (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016 
00017 See the GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00023 */
00024 
00025 #include "g_local.h"
00026 
00032 qboolean G_IsLivingActor (const edict_t *ent)
00033 {
00034     return G_IsActor(ent) && (G_IsStunned(ent) || !G_IsDead(ent));
00035 }
00036 
00044 void G_ActorUseDoor (edict_t *actor, edict_t *door)
00045 {
00046     edict_t *closeActor = NULL;
00047 
00048     G_ClientUseEdict(G_PLAYER_FROM_ENT(actor), actor, door);
00049 
00050     while ((closeActor = G_FindRadius(closeActor, door->origin, UNIT_SIZE * 3, ET_ACTOR))) {
00051         /* check whether the door is still reachable (this might have
00052          * changed due to the rotation) or whether an actor can reach it now */
00053         G_TouchTriggers(closeActor);
00054     }
00055 }
00056 
00062 qboolean G_ActorIsInRescueZone (const edict_t* actor)
00063 {
00064     return actor->inRescueZone;
00065 }
00066 
00072 void G_ActorSetInRescueZone (edict_t* actor, qboolean inRescueZone)
00073 {
00074     if (inRescueZone == G_ActorIsInRescueZone(actor))
00075         return;
00076 
00077     if (inRescueZone)
00078         G_ClientPrintf(G_PLAYER_FROM_ENT(actor), PRINT_HUD, _("Soldier entered the rescue zone\n"));
00079     else
00080         G_ClientPrintf(G_PLAYER_FROM_ENT(actor), PRINT_HUD, _("Soldier left the rescue zone\n"));
00081 
00082     actor->inRescueZone = inRescueZone;
00083 }
00084 
00090 void G_ActorSetClientAction (edict_t *actor, edict_t *ent)
00091 {
00092     if (actor->clientAction == ent)
00093         return;
00094 
00095     actor->clientAction = ent;
00096     if (ent == NULL) {
00097         G_EventResetClientAction(actor);
00098     } else {
00099         G_EventSetClientAction(actor);
00100     }
00101 }
00102 
00108 int G_ActorGetReservedTUs (const edict_t *ent)
00109 {
00110     const chrReservations_t *res = &ent->chr.reservedTus;
00111     return res->reaction + res->shot + res->crouch;
00112 }
00113 
00120 int G_ActorUsableTUs (const edict_t *ent)
00121 {
00122     if (!ent)
00123         return 0;
00124 
00125     return ent->TU - G_ActorGetReservedTUs(ent);
00126 }
00127 
00134 int G_ActorGetTUForReactionFire (const edict_t *ent)
00135 {
00136     const invList_t *invlistWeapon;
00137     const chrFiremodeSettings_t *fm = &ent->chr.RFmode;
00138     const fireDef_t *fd;
00139 
00140     invlistWeapon = ACTOR_GET_INV(ent, fm->hand);
00141     assert(invlistWeapon);
00142     assert(invlistWeapon->item.t);
00143 
00144     fd = FIRESH_FiredefForWeapon(&invlistWeapon->item);
00145     assert(fd);
00146     return fd[fm->fmIdx].time;
00147 }
00148 
00156 void G_ActorReserveTUs (edict_t *ent, int resReaction, int resShot, int resCrouch)
00157 {
00158     if (ent->TU >= resReaction + resShot + resCrouch) {
00159         ent->chr.reservedTus.reaction = resReaction;
00160         ent->chr.reservedTus.shot = resShot;
00161         ent->chr.reservedTus.crouch = resCrouch;
00162     }
00163 
00164     G_EventActorSendReservations(ent);
00165 }
00166 
00173 edict_t *G_ActorGetByUCN (const int ucn, const int team)
00174 {
00175     edict_t *ent = NULL;
00176 
00177     while ((ent = G_EdictsGetNextActor(ent)))
00178         if (team == ent->team && ent->chr.ucn == ucn)
00179             return ent;
00180 
00181     return NULL;
00182 }
00183 
00191 int G_ActorDoTurn (edict_t * ent, byte dir)
00192 {
00193     float angleDiv;
00194     const byte *rot;
00195     int i, num;
00196     int status;
00197 
00198     assert(ent->dir < CORE_DIRECTIONS);
00199     assert(dir < PATHFINDING_DIRECTIONS);
00200 
00201     /*
00202      * If dir is at least CORE_DIRECTIONS but less than FLYING_DIRECTIONS,
00203      * then the direction is an action.
00204      */
00208     if (dir >= CORE_DIRECTIONS && dir < FLYING_DIRECTIONS)
00209         return 0;
00210 
00211     /* Clamp dir between 0 and CORE_DIRECTIONS - 1. */
00212     dir &= (CORE_DIRECTIONS - 1);
00213     assert(dir < CORE_DIRECTIONS);
00214 
00215     /* Return if no rotation needs to be done. */
00216     if (ent->dir == dir)
00217         return 0;
00218 
00219     /* calculate angle difference */
00220     angleDiv = directionAngles[dir] - directionAngles[ent->dir];
00221     if (angleDiv > 180.0)
00222         angleDiv -= 360.0;
00223     if (angleDiv < -180.0)
00224         angleDiv += 360.0;
00225 
00226     /* prepare rotation - decide whether the actor turns around the left
00227      * shoulder or the right - this is needed the get the rotation vector
00228      * that is used below to check in each of the rotation steps
00229      * (1/8, 22.5 degree) whether something became visible while turning */
00230     if (angleDiv > 0) {
00231         const int angleStep = (360.0 / CORE_DIRECTIONS);
00232         rot = dvleft;
00233         num = (angleDiv + angleStep / 2) / angleStep;
00234     } else {
00235         const int angleStep = (360.0 / CORE_DIRECTIONS);
00236         rot = dvright;
00237         num = (-angleDiv + angleStep / 2) / angleStep;
00238     }
00239 
00240     /* do rotation and vis checks */
00241     status = 0;
00242 
00243     /* check every angle (1/8 steps - on the way to the end direction) in the rotation
00244      * whether something becomes visible and stop before reaching the final direction
00245      * if this happened */
00246     for (i = 0; i < num; i++) {
00247         ent->dir = rot[ent->dir];
00248         assert(ent->dir < CORE_DIRECTIONS);
00249         status |= G_CheckVisTeamAll(ent->team, qfalse, ent);
00250     }
00251 
00252     if (status & VIS_STOP) {
00253         /* send the turn */
00254         G_EventActorTurn(ent);
00255     }
00256 
00257     return status;
00258 }
00259 
00266 void G_ActorSetMaxs (edict_t* ent)
00267 {
00268     if (G_IsCrouched(ent))
00269         VectorSet(ent->maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_CROUCH);
00270     else if (G_IsDead(ent))
00271         VectorSet(ent->maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_DEAD);
00272     else
00273         VectorSet(ent->maxs, PLAYER_WIDTH, PLAYER_WIDTH, PLAYER_STAND);
00274 
00275     /* Link it. */
00276     gi.LinkEdict(ent);
00277 }
00278 
00283 void G_ActorGiveTimeUnits (edict_t *ent)
00284 {
00285     const int tus = G_IsDazed(ent) ? 0 : GET_TU(ent->chr.score.skills[ABILITY_SPEED]);
00286     G_ActorSetTU(ent, tus);
00287     G_RemoveDazed(ent);
00288 }
00289 
00290 void G_ActorSetTU (edict_t *ent, int tus)
00291 {
00292     ent->TU = max(tus, 0);
00293 }
00294 
00295 void G_ActorUseTU (edict_t *ent, int tus)
00296 {
00297     G_ActorSetTU(ent, ent->TU - tus);
00298 }
00299 
00300 static void G_ActorStun (edict_t * ent, const edict_t *attacker)
00301 {
00302     /* no other state should be set here */
00303     ent->state = STATE_STUN;
00304     if (attacker != NULL)
00305         level.num_stuns[attacker->team][ent->team]++;
00306 }
00307 
00308 static void G_ActorRevitalise (edict_t *ent)
00309 {
00310     if (G_IsStunned(ent)) {
00311         G_RemoveStunned(ent);
00314         level.num_alive[ent->team]++;
00315 
00316         G_GetFloorItems(ent);
00317     }
00318     G_ActorSetMaxs(ent);
00319 
00320     /* check if the player appears/perishes, seen from other teams */
00321     G_CheckVis(ent, qtrue);
00322 
00323     /* calc new vis for this player */
00324     G_CheckVisTeamAll(ent->team, qfalse, ent);
00325 }
00326 
00327 void G_ActorCheckRevitalise (edict_t *ent)
00328 {
00329     if (G_IsStunned(ent) && ent->STUN < ent->HP) {
00330         G_ActorRevitalise(ent);
00331         G_EventActorRevitalise(ent);
00332         G_SendStats(ent);
00333     }
00334 }
00335 
00336 static void G_ActorDie (edict_t * ent, const edict_t *attacker)
00337 {
00338     G_RemoveStunned(ent);
00339     G_SetState(ent, 1 + rand() % MAX_DEATH);
00340     if (attacker != NULL)
00341         level.num_kills[attacker->team][ent->team]++;
00342     G_ActorSetMaxs(ent);
00343 }
00344 
00353 void G_ActorDieOrStun (edict_t * ent, edict_t *attacker)
00354 {
00355     if (ent->HP == 0)
00356         G_ActorDie(ent, attacker);
00357     else
00358         G_ActorStun(ent, attacker);
00359 
00360     level.num_alive[ent->team]--;
00361     /* send death */
00362     G_EventActorDie(ent);
00363 
00364     /* handle inventory - drop everything but the armour to the floor */
00365     G_InventoryToFloor(ent);
00366 
00367     /* check if the player appears/perishes, seen from other teams */
00368     G_CheckVis(ent, qtrue);
00369 
00370     /* check if the attacker appears/perishes, seen from other teams */
00371     if (attacker)
00372         G_CheckVis(attacker, qtrue);
00373 
00374     /* calc new vis for this player */
00375     G_CheckVisTeamAll(ent->team, qfalse, attacker);
00376 
00377     /* unlink the floor container */
00378     FLOOR(ent) = NULL;
00379 }
00380 
00393 void G_ActorInvMove (edict_t *ent, const invDef_t * from, invList_t *fItem, const invDef_t * to, int tx,
00394         int ty, qboolean checkaction)
00395 {
00396     player_t *player;
00397     edict_t *floor;
00398     qboolean newFloor;
00399     invList_t *ic;
00400     item_t item;
00401     int mask;
00402     inventory_action_t ia;
00403     invList_t fItemBackup;
00404     int fx, fy;
00405     int originalTU, reservedTU = 0;
00406 
00407     player = G_PLAYER_FROM_ENT(ent);
00408 
00409     assert(fItem);
00410     assert(fItem->item.t);
00411 
00412     /* Store the location/item of 'from' BEFORE actually moving items with I_MoveInInventory. */
00413     fItemBackup = *fItem;
00414 
00415     /* Get first used bit in item. */
00416     INVSH_GetFirstShapePosition(fItem, &fx, &fy);
00417     fx += fItem->x;
00418     fy += fItem->y;
00419 
00420     /* Check if action is possible */
00421     /* TUs are 1 here - but this is only a dummy - the real TU check is done in the inventory functions below */
00422     if (checkaction && !G_ActionCheckForCurrentTeam(player, ent, 1))
00423         return;
00424 
00425     /* "get floor ready" - searching for existing floor-edict */
00426     floor = G_GetFloorItems(ent);
00427     if (INV_IsFloorDef(to) && !floor) {
00428         /* We are moving to the floor, but no existing edict for this floor-tile found -> create new one */
00429         floor = G_SpawnFloor(ent->pos);
00430         newFloor = qtrue;
00431     } else if (INV_IsFloorDef(from) && !floor) {
00432         /* We are moving from the floor, but no existing edict for this floor-tile found -> this should never be the case. */
00433         gi.DPrintf("G_ClientInvMove: No source-floor found.\n");
00434         return;
00435     } else {
00436         /* There already exists an edict for this floor-tile. */
00437         newFloor = qfalse;
00438     }
00439 
00440     /* search for space */
00441     if (tx == NONE) {
00442         ic = INVSH_SearchInInventory(&ent->chr.i, from, fItem->x, fItem->y);
00443         if (ic)
00444             INVSH_FindSpace(&ent->chr.i, &ic->item, to, &tx, &ty, fItem);
00445         if (tx == NONE)
00446             return;
00447     }
00448 
00450     /* Because I_MoveInInventory don't know anything about character_t and it updates ent->TU,
00451      * we need to save original ent->TU for the sake of checking TU reservations. */
00452     originalTU = ent->TU;
00453     reservedTU = G_ActorGetReservedTUs(ent);
00454     /* Temporary decrease ent->TU to make I_MoveInInventory do what expected. */
00455     G_ActorUseTU(ent, reservedTU);
00456     /* Try to actually move the item and check the return value after restoring valid ent->TU. */
00457     ia = game.i.MoveInInventory(&game.i, &ent->chr.i, from, fItem, to, tx, ty, checkaction ? &ent->TU : NULL, &ic);
00458     /* Now restore the original ent->TU and decrease it for TU used for inventory move. */
00459     G_ActorSetTU(ent, originalTU - (originalTU - reservedTU - ent->TU));
00460 
00461     switch (ia) {
00462     case IA_NONE:
00463         /* No action possible - abort */
00464         return;
00465     case IA_NOTIME:
00466         G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not enough TUs!\n"));
00467         return;
00468     case IA_NORELOAD:
00469         G_ClientPrintf(player, PRINT_HUD,
00470                 _("Can't perform action - weapon already fully loaded with the same ammunition!\n"));
00471         return;
00472     default:
00473         /* Continue below. */
00474         break;
00475     }
00476 
00477     /* successful inventory change; remove the item in clients */
00478     if (INV_IsFloorDef(from)) {
00479         /* We removed an item from the floor - check how the client
00480          * needs to be updated. */
00481         assert(!newFloor);
00482         if (FLOOR(ent)) {
00483             /* There is still something on the floor. */
00484             FLOOR(floor) = FLOOR(ent);
00485             G_EventInventoryDelete(floor, G_VisToPM(floor->visflags), from, fx, fy);
00486         } else {
00487             /* Floor is empty, remove the edict (from server + client) if we are
00488              * not moving to it. */
00489             if (!INV_IsFloorDef(to)) {
00490                 G_EventPerish(floor);
00491                 G_FreeEdict(floor);
00492             }
00493         }
00494     } else {
00495         G_EventInventoryDelete(ent, G_TeamToPM(ent->team), from, fx, fy);
00496     }
00497 
00498     /* send tu's */
00499     G_SendStats(ent);
00500 
00501     assert(ic);
00502     item = ic->item;
00503 
00504     if (ia == IA_RELOAD || ia == IA_RELOAD_SWAP) {
00505         /* reload */
00506         if (INV_IsFloorDef(to))
00507             mask = G_VisToPM(floor->visflags);
00508         else
00509             mask = G_TeamToPM(ent->team);
00510 
00511         G_EventInventoryReload(INV_IsFloorDef(to) ? floor : ent, mask, &item, to, ic);
00512 
00513         if (ia == IA_RELOAD) {
00514             gi.EndEvents();
00515             return;
00516         } else { /* ia == IA_RELOAD_SWAP */
00517             item = fItemBackup.item;
00518             to = from;
00519             tx = fItemBackup.x;
00520             ty = fItemBackup.y;
00521         }
00522     }
00523 
00524     /* We moved an item to the floor - check how the client needs to be updated. */
00525     if (INV_IsFloorDef(to)) {
00526         /* we have to link the temp floor container to the new floor edict or add
00527          * the item to an already existing floor edict - the floor container that
00528          * is already linked might be from a different entity */
00529         FLOOR(floor) = FLOOR(ent);
00530         /* A new container was created for the floor. */
00531         if (newFloor) {
00532             /* Send item info to the clients */
00533             G_CheckVis(floor, qtrue);
00534         } else {
00535             /* use the backup item to use the old amount values, because the clients have to use the same actions
00536              * on the original amount. Otherwise they would end in a different amount of items as the server (+1) */
00537             G_EventInventoryAdd(floor, G_VisToPM(floor->visflags), 1);
00538             G_WriteItem(&fItemBackup.item, to, tx, ty);
00539         }
00540     } else {
00541         G_EventInventoryAdd(ent, G_TeamToPM(ent->team), 1);
00542         G_WriteItem(&item, to, tx, ty);
00543     }
00544 
00545     G_ReactionFireUpdate(ent, ent->chr.RFmode.fmIdx, ent->chr.RFmode.hand, ent->chr.RFmode.weapon);
00546 
00547     /* Other players receive weapon info only. */
00548     mask = G_VisToPM(ent->visflags) & ~G_TeamToPM(ent->team);
00549     if (mask) {
00550         if (INV_IsRightDef(from) || INV_IsLeftDef(from)) {
00551             G_EventInventoryDelete(ent, mask, from, fx, fy);
00552         }
00553         if (INV_IsRightDef(to) || INV_IsLeftDef(to)) {
00554             G_EventInventoryAdd(ent, mask, 1);
00555             G_WriteItem(&item, to, tx, ty);
00556         }
00557     }
00558     gi.EndEvents();
00559 }
00560 
00567 void G_ActorReload (edict_t* ent, const invDef_t *invDef)
00568 {
00569     invList_t *ic;
00570     invList_t *icFinal;
00571     objDef_t *weapon;
00572     int tu;
00573     containerIndex_t containerID;
00574     invDef_t *bestContainer;
00575 
00576     /* search for clips and select the one that is available easily */
00577     icFinal = NULL;
00578     tu = 100;
00579     bestContainer = NULL;
00580 
00581     if (CONTAINER(ent, invDef->id)) {
00582         weapon = CONTAINER(ent, invDef->id)->item.t;
00583     } else if (INV_IsLeftDef(invDef) && RIGHT(ent)->item.t->holdTwoHanded) {
00584         /* Check for two-handed weapon */
00585         invDef = INVDEF(gi.csi->idRight);
00586         weapon = CONTAINER(ent, invDef->id)->item.t;
00587     } else
00588         return;
00589 
00590     assert(weapon);
00591 
00592     /* LordHavoc: Check if item is researched when in singleplayer? I don't think this is really a
00593      * cheat issue as in singleplayer there is no way to inject fake client commands in the virtual
00594      * network buffer, and in multiplayer everything is researched */
00595 
00596     /* also try the temp containers */
00597     for (containerID = 0; containerID < gi.csi->numIDs; containerID++) {
00598         if (INVDEF(containerID)->out < tu) {
00599             /* Once we've found at least one clip, there's no point
00600              * searching other containers if it would take longer
00601              * to retrieve the ammo from them than the one
00602              * we've already found. */
00603             for (ic = CONTAINER(ent, containerID); ic; ic = ic->next)
00604                 if (INVSH_LoadableInWeapon(ic->item.t, weapon)) {
00605                     icFinal = ic;
00606                     bestContainer = INVDEF(containerID);
00607                     tu = bestContainer->out;
00608                     break;
00609                 }
00610         }
00611     }
00612 
00613     /* send request */
00614     if (bestContainer)
00615         G_ActorInvMove(ent, bestContainer, icFinal, invDef, 0, 0, qtrue);
00616 }

Generated by  doxygen 1.6.2