g_move.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 
00027 #define ACTOR_SPEED_NORMAL 100
00028 #define ACTOR_SPEED_CROUCHED (ACTOR_SPEED_NORMAL / 2)
00029 static const float FALLING_DAMAGE_FACTOR = 10.0f;
00030 
00035 static pos_t *forbiddenList[MAX_FORBIDDENLIST];
00036 static int forbiddenListLength;
00037 
00050 static void G_BuildForbiddenList (int team, const edict_t *movingActor)
00051 {
00052     edict_t *ent = NULL;
00053     int visMask;
00054 
00055     forbiddenListLength = 0;
00056 
00057     /* team visibility */
00058     if (team)
00059         visMask = G_TeamToVisMask(team);
00060     else
00061         visMask = TEAM_ALL;
00062 
00063     while ((ent = G_EdictsGetNextInUse(ent))) {
00064         /* Dead 2x2 unit will stop walking, too. */
00065         if (G_IsBlockingMovementActor(ent) && (G_IsAI(movingActor) || (ent->visflags & visMask))) {
00066             forbiddenList[forbiddenListLength++] = ent->pos;
00067             forbiddenList[forbiddenListLength++] = (byte*) &ent->fieldSize;
00068         } else if (ent->type == ET_SOLID) {
00069             int j;
00070             for (j = 0; j < ent->forbiddenListSize; j++) {
00071                 forbiddenList[forbiddenListLength++] = ent->forbiddenListPos[j];
00072                 forbiddenList[forbiddenListLength++] = (byte*) &ent->fieldSize;
00073             }
00074         }
00075     }
00076 
00077     if (forbiddenListLength > MAX_FORBIDDENLIST)
00078         gi.Error("G_BuildForbiddenList: list too long\n");
00079 }
00080 
00093 void G_MoveCalc (int team, const edict_t *movingActor, const pos3_t from, byte crouchingState, int distance)
00094 {
00095     G_MoveCalcLocal(&level.pathingMap, team, movingActor, from, crouchingState, distance);
00096 }
00097 
00109 void G_MoveCalcLocal (pathing_t *pt, int team, const edict_t *movingActor, const pos3_t from, byte crouchingState, int distance)
00110 {
00111     G_BuildForbiddenList(team, movingActor);
00112     gi.MoveCalc(gi.routingMap, movingActor->fieldSize, pt, from, crouchingState, distance,
00113             forbiddenList, forbiddenListLength);
00114 }
00115 
00121 edict_t *G_GetActorFromPos (const pos3_t pos)
00122 {
00123     edict_t *actor = G_GetEdictFromPos(pos, ET_ACTOR);
00124     if (actor == NULL)
00125         actor = G_GetEdictFromPos(pos, ET_ACTOR2x2);
00126     if (actor == NULL)
00127         actor = G_GetEdictFromPos(pos, ET_ACTORHIDDEN);
00128 
00129     return actor;
00130 }
00131 
00137 void G_ActorFall (edict_t *ent)
00138 {
00139     edict_t* entAtPos;
00140     const int oldZ = ent->pos[2];
00141 
00142     ent->pos[2] = gi.GridFall(gi.routingMap, ent->fieldSize, ent->pos);
00143 
00144     entAtPos = G_GetEdictFromPos(ent->pos, ET_NULL);
00145     if (entAtPos != NULL && (G_IsBreakable(entAtPos) || G_IsBlockingMovementActor(entAtPos))) {
00146         const int diff = oldZ - ent->pos[2];
00147         G_TakeDamage(entAtPos, (int)(FALLING_DAMAGE_FACTOR * (float)diff));
00148     }
00149 
00150     G_EdictCalcOrigin(ent);
00151     gi.LinkEdict(ent);
00152 
00153     G_CheckVis(ent, qtrue);
00154 
00155     G_EventActorFall(ent);
00156 
00157     gi.EndEvents();
00158 }
00159 
00166 static qboolean G_ActorShouldStopInMidMove (const edict_t *ent, int visState, byte* dvtab, int max)
00167 {
00168     if (visState & VIS_STOP)
00169          return qtrue;
00170 
00171      /* check that the appearing unit is not on a grid position the actor wanted to walk to.
00172       * this might be the case if the edict got visible in mid mode */
00173      if (visState & VIS_APPEAR) {
00174          pos3_t pos;
00175          VectorCopy(ent->pos, pos);
00176          while (max >= 0) {
00177              int tmp = 0;
00178              const edict_t *blockEdict;
00179 
00180              PosAddDV(pos, tmp, dvtab[max]);
00181              max--;
00182              blockEdict = G_GetEdictFromPos(pos, ET_NULL);
00183 
00184              if (blockEdict && G_IsBlockingMovementActor(blockEdict)) {
00185                  const qboolean visible = G_IsVisibleForTeam(blockEdict, ent->team);
00186                  if (visible)
00187                      return qtrue;
00188              }
00189          }
00190      }
00191      return qfalse;
00192 }
00193 
00207 void G_ClientMove (const player_t * player, int visTeam, edict_t* ent, const pos3_t to)
00208 {
00209     int status, initTU;
00210     byte dvtab[MAX_DVTAB];
00211     int dv, dir;
00212     byte numdv, length;
00213     pos3_t pos;
00214     float div, truediv;
00215     int contentFlags;
00216     vec3_t pointTrace;
00217     byte* stepAmount = NULL;
00218     qboolean triggers = qfalse;
00219     int oldState;
00220     qboolean autoCrouchRequired = qfalse;
00221     byte crouchingState;
00222 
00223     /* check if action is possible */
00224     if (!G_ActionCheckForCurrentTeam(player, ent, TU_MOVE_STRAIGHT))
00225         return;
00226 
00227     crouchingState = G_IsCrouched(ent) ? 1 : 0;
00228     oldState = 0;
00229 
00230     /* calculate move table */
00231     G_MoveCalc(visTeam, ent, ent->pos, crouchingState, ent->TU);
00232     length = gi.MoveLength(&level.pathingMap, to, crouchingState, qfalse);
00233 
00234     /* length of ROUTING_NOT_REACHABLE means not reachable */
00235     if (length && length >= ROUTING_NOT_REACHABLE)
00236         return;
00237 
00238     /* Autostand: check if the actor is crouched and player wants autostanding...*/
00239     if (crouchingState && player->autostand) {
00240         /* ...and if this is a long walk... */
00241         if (SHOULD_USE_AUTOSTAND(length)) {
00242             /* ...make them stand first. If the player really wants them to walk a long
00243              * way crouched, he can move the actor in several stages.
00244              * Uses the threshold at which standing, moving and crouching again takes
00245              * fewer TU than just crawling while crouched. */
00246             G_ClientStateChange(player, ent, STATE_CROUCHED, qtrue); /* change to stand state */
00247             crouchingState = G_IsCrouched(ent) ? 1 : 0;
00248             if (!crouchingState) {
00249                 G_MoveCalc(visTeam, ent, ent->pos, crouchingState, ent->TU);
00250                 length = gi.MoveLength(&level.pathingMap, to, crouchingState, qfalse);
00251                 autoCrouchRequired = qtrue;
00252             }
00253         }
00254     }
00255 
00256     /* this let the footstep sounds play even over network */
00257     ent->think = G_PhysicsStep;
00258     ent->nextthink = level.time;
00259 
00260     /* assemble dv-encoded move data */
00261     VectorCopy(to, pos);
00262     numdv = 0;
00263     initTU = ent->TU;
00264 
00265     while ((dv = gi.MoveNext(&level.pathingMap, pos, crouchingState))
00266             != ROUTING_UNREACHABLE) {
00267         const int oldZ = pos[2];
00268         /* dv indicates the direction traveled to get to the new cell and the original cell height. */
00269         /* We are going backwards to the origin. */
00270         PosSubDV(pos, crouchingState, dv);
00271         /* Replace the z portion of the DV value so we can get back to where we were. */
00272         dvtab[numdv++] = NewDVZ(dv, oldZ);
00273         if (numdv >= lengthof(dvtab))
00274             break;
00275     }
00276 
00277     /* make sure to end any other pending events - we rely on EV_ACTOR_MOVE not being active anymore */
00278     gi.EndEvents();
00279 
00280     /* everything ok, found valid route? */
00281     if (VectorCompare(pos, ent->pos)) {
00282         int usedTUs = 0;
00283         /* no floor inventory at this point */
00284         FLOOR(ent) = NULL;
00285 
00286         while (numdv > 0) {
00287             /* A flag to see if we needed to change crouch state */
00288             int crouchFlag;
00289             const byte oldDir = ent->dir;
00290 
00291             /* get next dv */
00292             numdv--;
00293             dv = dvtab[numdv];
00294             /* This is the direction to make the step into */
00295             dir = getDVdir(dv);
00296 
00297             /* turn around first */
00298             status = G_ActorDoTurn(ent, dir);
00299             if (status & VIS_STOP) {
00300                 autoCrouchRequired = qfalse;
00301                 if (ent->moveinfo.steps == 0)
00302                     usedTUs += TU_TURN;
00303                 break;
00304             }
00305 
00306             if (G_ActorShouldStopInMidMove(ent, status, dvtab, numdv)) {
00307                 /* don't autocrouch if new enemy becomes visible */
00308                 autoCrouchRequired = qfalse;
00309                 /* if something appears on our route that didn't trigger a VIS_STOP, we have to
00310                  * send the turn event if this is our first step */
00311                 if (oldDir != ent->dir && ent->moveinfo.steps == 0) {
00312                     G_EventActorTurn(ent);
00313                     usedTUs += TU_TURN;
00314                 }
00315                 break;
00316             }
00317 
00318             /* decrease TUs */
00319             div = gi.GetTUsForDirection(dir);
00320             truediv = div;
00321             if (G_IsCrouched(ent) && dir < CORE_DIRECTIONS)
00322                 div *= TU_CROUCH_MOVING_FACTOR;
00323             if ((int) (usedTUs + div) > ent->TU)
00324                 break;
00325             usedTUs += div;
00326 
00327             /* This is now a flag to indicate a change in crouching - we need this for
00328              * the stop in mid move call(s), because we need the updated entity position */
00329             crouchFlag = 0;
00330             /* Calculate the new position after the decrease in TUs, otherwise the game
00331              * remembers the false position if the time runs out */
00332             PosAddDV(ent->pos, crouchFlag, dv);
00333 
00334             /* slower if crouched */
00335             if (G_IsCrouched(ent))
00336                 ent->speed = ACTOR_SPEED_CROUCHED;
00337             else
00338                 ent->speed = ACTOR_SPEED_NORMAL;
00339             ent->speed *= g_actorspeed->value;
00340 
00341             if (crouchFlag == 0) { /* No change in crouch */
00342                 edict_t* clientAction;
00343 
00344                 G_EdictCalcOrigin(ent);
00345                 VectorCopy(ent->origin, pointTrace);
00346                 pointTrace[2] += PLAYER_MIN;
00347 
00348                 contentFlags = gi.PointContents(pointTrace);
00349 
00350                 /* link it at new position - this must be done for every edict
00351                  * movement - to let the server know about it. */
00352                 gi.LinkEdict(ent);
00353 
00354                 /* Only the PHALANX team has these stats right now. */
00355                 if (ent->chr.scoreMission) {
00356                     if (G_IsCrouched(ent))
00357                         ent->chr.scoreMission->movedCrouched += truediv;
00358                     else
00359                         ent->chr.scoreMission->movedNormal += truediv;
00360                 }
00361 
00362                 /* write move header if not yet done */
00363                 if (gi.GetEvent() != EV_ACTOR_MOVE) {
00364                     gi.AddEvent(G_VisToPM(ent->visflags), EV_ACTOR_MOVE);
00365                     gi.WriteShort(ent->number);
00366                     /* stepAmount is a pointer to a location in the netchannel
00367                      * the value of this pointer depends on how far the actor walks
00368                      * and this might be influenced at a later stage inside this
00369                      * loop. That's why we can modify the value of this byte
00370                      * if e.g. a VIS_STOP occurred and no more steps should be made.
00371                      * But keep in mind, that no other events might be between
00372                      * this event and its successful end - otherwise the
00373                      * stepAmount pointer would no longer be valid and you would
00374                      * modify data in the new event. */
00375                     stepAmount = gi.WriteDummyByte(0);
00376                     /* Add three more dummy bytes.  These will be the final actor position. */
00377                     gi.WriteDummyByte(0); /* x */
00378                     gi.WriteDummyByte(0); /* y */
00379                     gi.WriteDummyByte(0); /* z */
00380                 } else if (!stepAmount) {
00381                     gi.DPrintf("Event %i activate and no stepAmount pointer set\n", gi.GetEvent());
00382                     break;
00383                 }
00384 
00385                 /* the moveinfo stuff is used inside the G_PhysicsStep think function */
00386                 if (ent->moveinfo.steps >= MAX_DVTAB) {
00387                     ent->moveinfo.steps = 0;
00388                     ent->moveinfo.currentStep = 0;
00389                 }
00390                 ent->moveinfo.contentFlags[ent->moveinfo.steps] = contentFlags;
00391                 ent->moveinfo.visflags[ent->moveinfo.steps] = ent->visflags;
00392                 ent->moveinfo.steps++;
00393 
00394                 /* store steps in netchannel */
00395                 (*stepAmount)++;
00396                 /* store the position too */
00397                 *(stepAmount + 1) = ent->pos[0];
00398                 *(stepAmount + 2) = ent->pos[1];
00399                 *(stepAmount + 3) = ent->pos[2];
00400 
00401                 /* write move header and always one step after another - because the next step
00402                  * might already be the last one due to some stop event */
00403                 gi.WriteByte(dv);
00404                 gi.WriteShort(ent->speed);
00405                 gi.WriteShort(contentFlags);
00406 
00407                 /* check if player appears/perishes, seen from other teams */
00408                 G_CheckVis(ent, qtrue);
00409 
00410                 /* check for anything appearing, seen by "the moving one" */
00411                 status = G_CheckVisTeamAll(ent->team, qfalse, ent);
00412 
00413                 /* Set ent->TU because the reaction code relies on ent->TU being accurate. */
00414                 G_ActorSetTU(ent, max(0, initTU - usedTUs));
00415 
00416                 clientAction = ent->clientAction;
00417                 oldState = ent->state;
00418                 /* check triggers at new position */
00419                 if (G_TouchTriggers(ent)) {
00420                     triggers = qtrue;
00421                     if (!clientAction)
00422                         status |= VIS_STOP;
00423                 }
00424                 /* state has changed - maybe we walked on a trigger_hurt */
00425                 if (oldState != ent->state)
00426                     status |= VIS_STOP;
00427             } else if (crouchFlag == 1) {
00428                 /* Actor is standing */
00429                 G_ClientStateChange(player, ent, STATE_CROUCHED, qtrue);
00430             } else if (crouchFlag == -1) {
00431                 /* Actor is crouching and should stand up */
00432                 G_ClientStateChange(player, ent, STATE_CROUCHED, qfalse);
00433             }
00434 
00435             /* check for reaction fire */
00436             if (G_ReactionFireOnMovement(ent)) {
00437                 status |= VIS_STOP;
00438 
00439                 autoCrouchRequired = qfalse;
00440             }
00441 
00442             /* Restore ent->TU because the movement code relies on it not being modified! */
00443             G_ActorSetTU(ent, initTU);
00444 
00445             /* check for death */
00446             if (oldState != ent->state && !G_IsDazed(ent)) {
00448                 /* maybe this was due to rf - then the G_ActorDie was already called */
00449                 if (!G_IsDead(ent)) {
00450                     G_PrintActorStats(ent, NULL, NULL);
00451 
00452                     G_ActorDieOrStun(ent, NULL);
00453                 }
00454                 return;
00455             }
00456 
00457             if (G_ActorShouldStopInMidMove(ent, status, dvtab, numdv - 1)) {
00458                 /* don't autocrouch if new enemy becomes visible */
00459                 autoCrouchRequired = qfalse;
00460                 break;
00461             }
00462         }
00463 
00464         /* submit the TUs / round down */
00465         if (g_notu != NULL && g_notu->integer)
00466             G_ActorSetTU(ent, initTU);
00467         else
00468             G_ActorSetTU(ent, max(0, initTU - usedTUs));
00469 
00470         G_SendStats(ent);
00471 
00472         /* end the move */
00473         G_GetFloorItems(ent);
00474         gi.EndEvents();
00475     }
00476 
00477     if (autoCrouchRequired) {
00478         /* toggle back to crouched state */
00479         G_ClientStateChange(player, ent, STATE_CROUCHED, qtrue);
00480     }
00481 }

Generated by  doxygen 1.6.2