00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "../client.h"
00027 #include "cl_actor.h"
00028 #include "../cl_game.h"
00029 #include "cl_hud.h"
00030 #include "cl_parse.h"
00031 #include "cl_particle.h"
00032 #include "../cl_team.h"
00033 #include "cl_view.h"
00034 #include "../ui/ui_main.h"
00035 #include "../ui/ui_popup.h"
00036 #include "../ui/node/ui_node_container.h"
00037 #include "../renderer/r_entity.h"
00038 #include "../renderer/r_mesh_anim.h"
00039 #include "../sound/s_main.h"
00040 #include "../../common/routing.h"
00041 #include "../../common/grid.h"
00042
00044 static cvar_t *confirm_actions;
00046 static cvar_t *cl_autostand;
00047 static cvar_t *cl_showactors;
00048 static cvar_t *cl_reactionleftover;
00049
00050
00051 le_t *selActor;
00052 pos3_t truePos;
00053 pos3_t mousePos;
00063 static int mousePosTargettingAlign = 0;
00064
00065 static le_t *mouseActor;
00066 static pos3_t mouseLastPos;
00067
00073 void MSG_Write_PA (player_action_t playerAction, int entnum, ...)
00074 {
00075 va_list ap;
00076 struct dbuffer *msg = new_dbuffer();
00077
00078 va_start(ap, entnum);
00079 NET_WriteFormat(msg, "bbs", clc_action, playerAction, entnum);
00080 NET_vWriteFormat(msg, pa_format[playerAction], ap);
00081 va_end(ap);
00082 NET_WriteMsg(cls.netStream, msg);
00083 }
00084
00085
00086
00087
00088
00089
00090
00096 const char *CL_ActorGetSkillString (const int skill)
00097 {
00098 const int skillLevel = skill * 10 / MAX_SKILL;
00099 #ifdef DEBUG
00100 if (skill > MAX_SKILL) {
00101 Com_Printf("CL_GetSkillString: Skill is bigger than max allowed skill value (%i/%i)\n", skill, MAX_SKILL);
00102 }
00103 #endif
00104 switch (skillLevel) {
00105 case 0:
00106 return _("Poor");
00107 case 1:
00108 return _("Mediocre");
00109 case 2:
00110 return _("Average");
00111 case 3:
00112 return _("Competent");
00113 case 4:
00114 return _("Proficient");
00115 case 5:
00116 return _("Very Good");
00117 case 6:
00118 return _("Highly Proficient");
00119 case 7:
00120 return _("Excellent");
00121 case 8:
00122 return _("Outstanding");
00123 case 9:
00124 case 10:
00125 return _("Superhuman");
00126 default:
00127 Com_Printf("CL_GetSkillString: Unknown skill: %i (index: %i)\n", skill, skillLevel);
00128 return "";
00129 }
00130 }
00131
00132 void CL_ActorSetFireDef (le_t *actor, const fireDef_t *fd)
00133 {
00134 if (actor->fd != fd)
00135 mousePosTargettingAlign = 0;
00136 actor->fd = fd;
00137 }
00138
00144 int CL_ActorMoveMode (const le_t *le, int length)
00145 {
00146 assert(le);
00147 if (LE_IsCrouched(le)) {
00148 if (cl_autostand->integer) {
00149 if (SHOULD_USE_AUTOSTAND(length)) {
00150 return WALKTYPE_AUTOSTAND_BEING_USED;
00151 } else {
00152 return WALKTYPE_AUTOSTAND_BUT_NOT_FAR_ENOUGH;
00153 }
00154 } else {
00155 return WALKTYPE_CROUCH_WALKING;
00156 }
00157 } else {
00158 return WALKTYPE_WALKING;
00159 }
00160 }
00161
00167 static int CL_ActorGetNumber (const le_t * le)
00168 {
00169 int actorIdx;
00170
00171 assert(le);
00172
00173 for (actorIdx = 0; actorIdx < cl.numTeamList; actorIdx++) {
00174 if (cl.teamList[actorIdx] == le)
00175 return actorIdx;
00176 }
00177 return -1;
00178 }
00179
00186 character_t *CL_ActorGetChr (const le_t * le)
00187 {
00188 const int actorIdx = CL_ActorGetNumber(le);
00189 if (actorIdx == -1) {
00190 Com_DPrintf(DEBUG_CLIENT, "CL_GetActorChr: Could not find actor in the team list - maybe already dead? (state is %i)!\n",
00191 le->state);
00192 return NULL;
00193 }
00194
00195 return cl.chrList.chr[actorIdx];
00196 }
00197
00205 void CL_ActorSetRFMode (character_t *chr, actorHands_t hand, int fireModeIndex, const objDef_t *weapon)
00206 {
00207 chr->RFmode.hand = hand;
00208 chr->RFmode.fmIdx = fireModeIndex;
00209 chr->RFmode.weapon = weapon;
00210 }
00211
00219 void CL_ActorSetShotSettings (character_t *chr, actorHands_t hand, int fireModeIndex, const objDef_t *weapon)
00220 {
00221 chr->reservedTus.shotSettings.hand = hand;
00222 chr->reservedTus.shotSettings.fmIdx = fireModeIndex;
00223 chr->reservedTus.shotSettings.weapon = weapon;
00224 }
00225
00233 int CL_ActorReservedTUs (const le_t * le, const reservation_types_t type)
00234 {
00235 character_t *chr;
00236 int reservedReaction, reservedCrouch, reservedShot;
00237
00238 if (!le)
00239 return -1;
00240
00241 chr = CL_ActorGetChr(le);
00242 if (!chr) {
00243 Com_DPrintf(DEBUG_CLIENT, "CL_ActorReservedTUs: No character found for le.\n");
00244 return -1;
00245 }
00246
00247 reservedReaction = max(0, chr->reservedTus.reaction);
00248 reservedCrouch = max(0, chr->reservedTus.crouch);
00249 reservedShot = max(0, chr->reservedTus.shot);
00250
00251 switch (type) {
00252 case RES_ALL:
00253
00254 return reservedReaction + reservedCrouch + reservedShot;
00255 case RES_ALL_ACTIVE: {
00256
00257
00258 if ((le->state & STATE_REACTION))
00259 return reservedReaction + reservedShot + reservedCrouch;
00260 else
00261 return reservedShot + reservedCrouch;
00262 }
00263 case RES_REACTION:
00264 return reservedReaction;
00265 case RES_CROUCH:
00266 return reservedCrouch;
00267 case RES_SHOT:
00268 return reservedShot;
00269 default:
00270 Com_DPrintf(DEBUG_CLIENT, "CL_ActorReservedTUs: Bad type given: %i\n", type);
00271 return -1;
00272 }
00273 }
00274
00281 int CL_ActorUsableTUs (const le_t * le)
00282 {
00283 if (!le)
00284 return -1;
00285
00286 return le->TU - CL_ActorReservedTUs(le, RES_ALL_ACTIVE);
00287 }
00288
00295 void CL_ActorReserveTUs (const le_t * le, const reservation_types_t type, const int tus)
00296 {
00297 character_t *chr;
00298
00299 assert(type != RES_REACTION);
00300
00301 if (!le || tus < 0)
00302 return;
00303
00304 chr = CL_ActorGetChr(le);
00305 if (chr) {
00306 chrReservations_t res = chr->reservedTus;
00307
00308 if (type == RES_CROUCH)
00309 res.crouch = tus;
00310 else if (type == RES_SHOT)
00311 res.shot = tus;
00312
00313 MSG_Write_PA(PA_RESERVE_STATE, le->entnum, res.shot, res.crouch);
00314 }
00315 }
00316
00317
00318
00319
00320
00321
00322
00329 void CL_ActorAddToTeamList (le_t * le)
00330 {
00331 int actorIdx;
00332 const size_t size = lengthof(cl.teamList);
00333
00334
00335 if (!le || le->team != cls.team || le->pnum != cl.pnum || LE_IsDead(le))
00336 return;
00337
00338
00339 actorIdx = CL_ActorGetNumber(le);
00340
00341
00342 if (actorIdx == -1) {
00343
00344 if (cl.numTeamList >= size) {
00345 Com_Printf("Too many actors on the teamlist\n");
00346 return;
00347 }
00348 actorIdx = cl.numTeamList;
00349 le->pathMap = Mem_PoolAlloc(sizeof(*le->pathMap), cl_genericPool, 0);
00350 cl.teamList[cl.numTeamList] = le;
00351 UI_ExecuteConfunc("hudenable %i", cl.numTeamList);
00352 cl.numTeamList++;
00353 if (cl.numTeamList == 1)
00354 CL_ActorSelectList(0);
00355 } else {
00356 UI_ExecuteConfunc("hudenable %i", actorIdx);
00357 }
00358 }
00359
00360 void CL_ActorCleanup (le_t *le)
00361 {
00362 if (le->pathMap)
00363 Mem_Free(le->pathMap);
00364 le->pathMap = NULL;
00365 cls.i.DestroyInventory(&cls.i, &le->i);
00366 }
00367
00374 void CL_ActorRemoveFromTeamList (le_t * le)
00375 {
00376 int i;
00377
00378 if (!le)
00379 return;
00380
00381 for (i = 0; i < cl.numTeamList; i++) {
00382 if (cl.teamList[i] == le) {
00383 if (!LE_IsStunned(le)) {
00384 CL_ActorCleanup(le);
00385
00386 cl.teamList[i] = NULL;
00387 } else {
00389 le->left = le->right = le->extension = le->headgear = NONE;
00390 cls.i.DestroyInventory(&cls.i, &le->i);
00391 }
00392
00393
00394 UI_ExecuteConfunc("huddisable %i", i);
00395
00396 break;
00397 }
00398 }
00399
00400
00401 if (le->selected) {
00402 for (i = 0; i < cl.numTeamList; i++) {
00403 if (cl.teamList[i] && CL_ActorSelect(cl.teamList[i]))
00404 break;
00405 }
00406
00407 if (i == cl.numTeamList)
00408 CL_ActorSelect(NULL);
00409 }
00410 }
00411
00418 qboolean CL_ActorSelect (le_t * le)
00419 {
00420 int actorIdx;
00421 character_t* chr;
00422
00423
00424 if (!le) {
00425 if (selActor)
00426 selActor->selected = qfalse;
00427 selActor = NULL;
00428 ui_inventory = NULL;
00429 return qfalse;
00430 }
00431
00432 if (le->team != cls.team || LE_IsDead(le) || !le->inuse)
00433 return qfalse;
00434
00435 if (le->selected) {
00436 mousePosTargettingAlign = 0;
00437 return qtrue;
00438 }
00439
00440 if (selActor)
00441 selActor->selected = qfalse;
00442
00444 HUD_HideFiremodes();
00445
00446 mousePosTargettingAlign = 0;
00447 selActor = le;
00448 selActor->selected = qtrue;
00449 ui_inventory = &selActor->i;
00450
00451 actorIdx = CL_ActorGetNumber(le);
00452 if (actorIdx == -1)
00453 return qfalse;
00454
00455
00456 Cvar_ForceSet("cl_selected", va("%i", actorIdx));
00457
00458 chr = CL_ActorGetChr(le);
00459 if (!chr)
00460 Com_Error(ERR_DROP, "No character given for local entity");
00461
00462 CL_UpdateCharacterValues(chr, "mn_");
00463
00464 CL_ActorConditionalMoveCalc(le);
00465
00466 return qtrue;
00467 }
00468
00480 qboolean CL_ActorSelectList (int num)
00481 {
00482 le_t *le;
00483
00484
00485 if (num >= cl.numTeamList || num < 0)
00486 return qfalse;
00487
00488
00489 le = cl.teamList[num];
00490 if (!le || !CL_ActorSelect(le))
00491 return qfalse;
00492
00493
00494 LE_CenterView(le);
00495 Cvar_SetValue("cl_worldlevel", le->pos[2]);
00496
00497 return qtrue;
00498 }
00499
00503 qboolean CL_ActorSelectNext (void)
00504 {
00505 int selIndex = -1;
00506 const int num = cl.numTeamList;
00507 int i;
00508
00509
00510 for (i = 0; i < num; i++) {
00511 const le_t *le = cl.teamList[i];
00512 if (le && le->selected && le->inuse && !LE_IsDead(le)) {
00513 selIndex = i;
00514 break;
00515 }
00516 }
00517 if (selIndex < 0)
00518 return qfalse;
00519
00520
00521 i = selIndex;
00522 while (qtrue) {
00523 i = (i + 1) % num;
00524 if (i == selIndex)
00525 break;
00526 if (CL_ActorSelectList(i))
00527 return qtrue;
00528 }
00529 return qfalse;
00530 }
00531
00532
00533
00534
00535
00536
00537
00538
00544 static pos_t *forbiddenList[MAX_FORBIDDENLIST];
00550 static int forbiddenListLength;
00551
00560 static void CL_BuildForbiddenList (void)
00561 {
00562 le_t *le = NULL;
00563
00564 forbiddenListLength = 0;
00565
00566 while ((le = LE_GetNextInUse(le))) {
00567 if (le->invis)
00568 continue;
00569
00570 if (le->type == ET_ACTOR2x2 || (!LE_IsStunned(le) && LE_IsLivingAndVisibleActor(le))) {
00571 forbiddenList[forbiddenListLength++] = le->pos;
00572 forbiddenList[forbiddenListLength++] = (byte*)&le->fieldSize;
00573 }
00574 }
00575
00576 #ifdef PARANOID
00577 if (forbiddenListLength > MAX_FORBIDDENLIST)
00578 Com_Error(ERR_DROP, "CL_BuildForbiddenList: list too long");
00579 #endif
00580 }
00581
00582 #ifdef DEBUG
00583
00590 static void CL_DisplayBlockedPaths_f (void)
00591 {
00592 le_t *le = NULL;
00593 int j;
00594 ptl_t *ptl;
00595 vec3_t s;
00596
00597 while ((le = LE_GetNextInUse(le))) {
00598 switch (le->type) {
00599 case ET_ACTOR:
00600 case ET_ACTOR2x2:
00601
00602 if (!LE_IsDead(le))
00603 Grid_PosToVec(cl.mapData->map, le->fieldSize, le->pos, s);
00604 break;
00605 case ET_DOOR:
00606 case ET_BREAKABLE:
00607 case ET_ROTATING:
00608 VectorCopy(le->origin, s);
00609 break;
00610 default:
00611 continue;
00612 }
00613
00614 ptl = CL_ParticleSpawn("blocked_field", 0, s, NULL, NULL);
00615 ptl->rounds = 2;
00616 ptl->roundsCnt = 2;
00617 ptl->life = 10000;
00618 ptl->t = 0;
00619 if (le->fieldSize == ACTOR_SIZE_2x2) {
00620
00621 for (j = 0; j < 3; j++) {
00622 ptl_t *ptl2 = CL_ParticleSpawn("blocked_field", 0, s, NULL, NULL);
00623 ptl2->rounds = ptl->rounds;
00624 ptl2->roundsCnt = ptl->roundsCnt;
00625 ptl2->life = ptl->life;
00626 ptl2->t = ptl->t;
00627 }
00628 }
00629 }
00630 }
00631 #endif
00632
00639 void CL_ActorConditionalMoveCalc (le_t *le)
00640 {
00641 CL_BuildForbiddenList();
00642 if (le && le->selected) {
00643 const byte crouchingState = LE_IsCrouched(le) ? 1 : 0;
00644 Grid_MoveCalc(cl.mapData->map, le->fieldSize, le->pathMap, le->pos, crouchingState, MAX_ROUTE, forbiddenList, forbiddenListLength);
00645 CL_ActorResetMoveLength(le);
00646 }
00647 }
00648
00654 int CL_ActorCheckAction (const le_t *le)
00655 {
00656 if (!le)
00657 return qfalse;
00658
00659
00660 if (le->pathLength)
00661 return qfalse;
00662
00663 if (cls.team != cl.actTeam) {
00664 HUD_DisplayMessage(_("This isn't your round\n"));
00665 return qfalse;
00666 }
00667
00668 return qtrue;
00669 }
00670
00679 static byte CL_ActorMoveLength (const le_t *le, const pos3_t to)
00680 {
00681 const byte crouchingState = LE_IsCrouched(le) ? 1 : 0;
00682 const byte length = Grid_MoveLength(le->pathMap, to, crouchingState, qfalse);
00683 return length;
00684 }
00685
00690 void CL_ActorResetMoveLength (le_t *le)
00691 {
00692 le->actorMoveLength = CL_ActorMoveLength(le, mousePos);
00693 }
00694
00702 static qboolean CL_ActorTraceMove (const pos3_t to)
00703 {
00704 byte length;
00705 vec3_t vec, oldVec;
00706 pos3_t pos;
00707 int dv;
00708 byte crouchingState;
00709
00710 if (!selActor)
00711 return qfalse;
00712
00713 length = CL_ActorMoveLength(selActor, to);
00714 if (!length || length >= ROUTING_NOT_REACHABLE)
00715 return qfalse;
00716
00717 crouchingState = LE_IsCrouched(selActor) ? 1 : 0;
00718
00719 Grid_PosToVec(cl.mapData->map, selActor->fieldSize, to, oldVec);
00720 VectorCopy(to, pos);
00721
00722 Com_DPrintf(DEBUG_PATHING, "Starting pos: (%i, %i, %i).\n", pos[0], pos[1], pos[2]);
00723
00724 while ((dv = Grid_MoveNext(selActor->pathMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
00725 length = CL_ActorMoveLength(selActor, pos);
00726 PosSubDV(pos, crouchingState, dv);
00727 Com_DPrintf(DEBUG_PATHING, "Next pos: (%i, %i, %i, %i) [%i].\n", pos[0], pos[1], pos[2], crouchingState, dv);
00728 Grid_PosToVec(cl.mapData->map, selActor->fieldSize, pos, vec);
00729 if (length > CL_ActorUsableTUs(selActor))
00730 CL_ParticleSpawn("longRangeTracer", 0, vec, oldVec, NULL);
00731 else if (crouchingState)
00732 CL_ParticleSpawn("crawlTracer", 0, vec, oldVec, NULL);
00733 else
00734 CL_ParticleSpawn("moveTracer", 0, vec, oldVec, NULL);
00735 VectorCopy(vec, oldVec);
00736 }
00737 return qtrue;
00738 }
00739
00747 static void CL_ActorMaximumMove (const pos3_t to, const le_t *le, pos3_t pos)
00748 {
00749 int dv;
00750 byte crouchingState = le && LE_IsCrouched(le) ? 1 : 0;
00751 const int tus = CL_ActorUsableTUs(le);
00752 const byte length = CL_ActorMoveLength(le, to);
00753 if (!length || length >= ROUTING_NOT_REACHABLE)
00754 return;
00755
00756 VectorCopy(to, pos);
00757
00758 while ((dv = Grid_MoveNext(le->pathMap, pos, crouchingState)) != ROUTING_UNREACHABLE) {
00759 const byte length2 = CL_ActorMoveLength(le, pos);
00760 if (length2 <= tus)
00761 return;
00762 PosSubDV(pos, crouchingState, dv);
00763 }
00764 }
00765
00766 void CL_ActorSetMode (le_t *actor, actorModes_t actorMode)
00767 {
00768 actor->actorMode = actorMode;
00769 }
00770
00778 void CL_ActorStartMove (le_t * le, const pos3_t to)
00779 {
00780 byte length;
00781 pos3_t toReal;
00782
00783 if (mouseSpace != MS_WORLD)
00784 return;
00785
00786 if (!CL_ActorCheckAction(le))
00787 return;
00788
00789 length = CL_ActorMoveLength(le, to);
00790
00791 if (!length || length >= ROUTING_NOT_REACHABLE) {
00792
00793 return;
00794 }
00795
00796
00797 CL_ActorMaximumMove(to, le, toReal);
00798
00799
00800 length = CL_ActorMoveLength(le, toReal);
00801
00802 if (CL_ActorUsableTUs(le) < length) {
00803
00804
00805 return;
00806 }
00807
00808
00809 CL_ActorSetMode(le, M_MOVE);
00810
00811
00812 MSG_Write_PA(PA_MOVE, le->entnum, toReal);
00813 }
00814
00815
00821 void CL_ActorShoot (const le_t * le, const pos3_t at)
00822 {
00823 int type;
00824
00825 if (mouseSpace != MS_WORLD)
00826 return;
00827
00828 if (!CL_ActorCheckAction(le))
00829 return;
00830
00831 if (IS_MODE_FIRE_RIGHT(le->actorMode)) {
00832 type = ST_RIGHT;
00833 } else if (IS_MODE_FIRE_LEFT(le->actorMode)) {
00834 type = ST_LEFT;
00835 } else if (IS_MODE_FIRE_HEADGEAR(le->actorMode)) {
00836 type = ST_HEADGEAR;
00837 } else
00838 return;
00839
00840 MSG_Write_PA(PA_SHOOT, le->entnum, at, type, le->currentSelectedFiremode, mousePosTargettingAlign);
00841 }
00842
00850 int CL_ActorGetContainerForReload (invList_t **invList, const inventory_t *inv, const objDef_t *weapon)
00851 {
00852 containerIndex_t container;
00853 int tu = 100;
00854 containerIndex_t bestContainer = NONE;
00855
00856
00857 for (container = 0; container < csi.numIDs; container++) {
00858 if (INVDEF(container)->out < tu) {
00859 invList_t *ic;
00860
00861
00862
00863
00864 for (ic = inv->c[container]; ic; ic = ic->next) {
00865 objDef_t *od = ic->item.t;
00866 if (INVSH_LoadableInWeapon(od, weapon) && GAME_ItemIsUseable(od)) {
00867 tu = INVDEF(container)->out;
00868 bestContainer = container;
00869 *invList = ic;
00870 break;
00871 }
00872 }
00873 }
00874 }
00875 return bestContainer;
00876 }
00877
00884 void CL_ActorReload (le_t *le, containerIndex_t containerID)
00885 {
00886 inventory_t *inv;
00887 invList_t *ic;
00888 objDef_t *weapon;
00889 containerIndex_t bestContainer;
00890
00891 if (!CL_ActorCheckAction(le))
00892 return;
00893
00894
00895 inv = &le->i;
00896
00897 if (inv->c[containerID]) {
00898 weapon = inv->c[containerID]->item.t;
00899 } else if (containerID == csi.idLeft && inv->c[csi.idRight]->item.t->holdTwoHanded) {
00900
00901 containerID = csi.idRight;
00902 weapon = inv->c[containerID]->item.t;
00903 } else
00904
00905 return;
00906
00907 if (!weapon)
00908 return;
00909
00910
00911 if (!weapon->reload)
00912 return;
00913
00914 if (!GAME_ItemIsUseable(weapon)) {
00915 HUD_DisplayMessage(_("You cannot reload this unknown item.\n"));
00916 return;
00917 }
00918
00919 bestContainer = CL_ActorGetContainerForReload(&ic, inv, weapon);
00920
00921 if (bestContainer != NONE) {
00922 int x, y;
00923
00924 INVSH_GetFirstShapePosition(ic, &x, &y);
00925 x += ic->x;
00926 y += ic->y;
00927
00928 CL_ActorInvMove(le, bestContainer, x, y, containerID, 0, 0);
00929 }
00930 }
00931
00942 void CL_ActorInvMove (const le_t *le, containerIndex_t fromContainer, int fromX, int fromY, containerIndex_t toContainer, int toX, int toY)
00943 {
00944 assert(CL_BattlescapeRunning());
00945 assert(le);
00946 assert(LE_IsActor(le));
00947 MSG_Write_PA(PA_INVMOVE, le->entnum, fromContainer, fromX, fromY, toContainer, toX, toY);
00948 }
00949
00955 static void CL_ActorUseDoor (const le_t *le)
00956 {
00957 if (!CL_ActorCheckAction(le))
00958 return;
00959
00960 assert(le->clientAction);
00961
00962 MSG_Write_PA(PA_USE_DOOR, le->entnum, le->clientAction->entnum);
00963 Com_DPrintf(DEBUG_CLIENT, "CL_ActorUseDoor: Use door number: %i (actor %i)\n", le->clientAction->entnum, le->entnum);
00964 }
00965
00969 void CL_ActorDoorAction_f (void)
00970 {
00971 if (!CL_ActorCheckAction(selActor))
00972 return;
00973
00974
00975 if (selActor->clientAction == NULL) {
00976 Com_DPrintf(DEBUG_CLIENT, "CL_ActorDoorAction_f: No client_action set for actor with entnum %i\n", selActor->entnum);
00977 return;
00978 }
00979
00980
00981 if (CL_ActorUsableTUs(selActor) >= TU_DOOR_ACTION)
00982 CL_ActorUseDoor(selActor);
00983 }
00984
00990 qboolean CL_ActorFireModeActivated (const actorModes_t mode)
00991 {
00992 return IS_MODE_FIRE_RIGHT(mode) || IS_MODE_FIRE_LEFT(mode) || IS_MODE_FIRE_HEADGEAR(mode);
00993 }
00994
00998 void CL_ActorTurnMouse (void)
00999 {
01000 vec3_t div;
01001 byte dv;
01002
01003 if (mouseSpace != MS_WORLD)
01004 return;
01005
01006 if (!CL_ActorCheckAction(selActor))
01007 return;
01008
01009 if (CL_ActorUsableTUs(selActor) < TU_TURN) {
01010
01011 return;
01012 }
01013
01014
01015 if (CL_ActorFireModeActivated(selActor->actorMode)) {
01016 CL_ActorActionMouse();
01017 return;
01018 }
01019
01020
01021 VectorSubtract(mousePos, selActor->pos, div);
01022 dv = AngleToDV((int) (atan2(div[1], div[0]) * todeg));
01023
01024
01025 MSG_Write_PA(PA_TURN, selActor->entnum, dv);
01026 }
01027
01031 static void CL_ActorStandCrouch_f (void)
01032 {
01033 if (!CL_ActorCheckAction(selActor))
01034 return;
01035
01036
01037 if (CL_ActorUsableTUs(selActor) >= TU_CROUCH || CL_ActorReservedTUs(selActor, RES_CROUCH) >= TU_CROUCH) {
01038
01039 MSG_Write_PA(PA_STATE, selActor->entnum, STATE_CROUCHED);
01040 }
01041 }
01042
01046 static void CL_ActorUseHeadgear_f (void)
01047 {
01048 invList_t* headgear;
01049 const int tmpMouseSpace = mouseSpace;
01050
01051
01052
01053
01054 mouseSpace = MS_WORLD;
01055
01056 if (!CL_ActorCheckAction(selActor))
01057 return;
01058
01059 headgear = HEADGEAR(selActor);
01060 if (!headgear)
01061 return;
01062
01063 CL_ActorSetMode(selActor, M_FIRE_HEADGEAR);
01065 selActor->currentSelectedFiremode = 0;
01066 CL_ActorShoot(selActor, selActor->pos);
01067 CL_ActorSetMode(selActor, M_MOVE);
01068
01069
01070 mouseSpace = tmpMouseSpace;
01071 }
01072
01073
01074
01075
01076
01077
01078
01084 static void CL_ActorMoveMouse (void)
01085 {
01086
01087
01088
01089
01090 if (mousePos[2] > cl_worldlevel->integer)
01091 return;
01092
01093 if (selActor->actorMode == M_PEND_MOVE) {
01094 if (VectorCompare(mousePos, selActor->mousePendPos)) {
01095
01096 CL_ActorStartMove(selActor, mousePos);
01097 } else {
01098
01099 VectorCopy(mousePos, selActor->mousePendPos);
01100 }
01101 } else {
01102
01103
01104 if (confirm_actions->integer || cls.team != cl.actTeam) {
01105
01106 VectorCopy(mousePos, selActor->mousePendPos);
01107
01108 CL_ActorSetMode(selActor, M_PEND_MOVE);
01109 } else {
01110
01111 CL_ActorStartMove(selActor, mousePos);
01112 }
01113 }
01114 }
01115
01120 void CL_ActorSelectMouse (void)
01121 {
01122 if (mouseSpace != MS_WORLD || !selActor)
01123 return;
01124
01125 switch (selActor->actorMode) {
01126 case M_MOVE:
01127 case M_PEND_MOVE:
01128
01129 if (mouseActor && !mouseActor->selected && CL_ActorSelect(mouseActor)) {
01130
01131 CL_ActorSetMode(selActor, M_MOVE);
01132 } else {
01133 CL_ActorMoveMouse();
01134 }
01135 break;
01136 case M_PEND_FIRE_R:
01137 case M_PEND_FIRE_L:
01138 if (VectorCompare(mousePos, selActor->mousePendPos)) {
01139
01140 CL_ActorShoot(selActor, mousePos);
01141
01142
01143 if (selActor->actorMode == M_PEND_FIRE_R)
01144 CL_ActorSetMode(selActor, M_FIRE_R);
01145 else
01146 CL_ActorSetMode(selActor, M_FIRE_L);
01147 } else {
01148
01149 VectorCopy(mousePos, selActor->mousePendPos);
01150 }
01151 break;
01152 case M_FIRE_R:
01153 if (mouseActor && mouseActor->selected)
01154 break;
01155
01156
01157 if (confirm_actions->integer == 1) {
01158 CL_ActorSetMode(selActor, M_PEND_FIRE_R);
01159 VectorCopy(mousePos, selActor->mousePendPos);
01160 } else {
01161 CL_ActorShoot(selActor, mousePos);
01162 }
01163 break;
01164 case M_FIRE_L:
01165 if (mouseActor && mouseActor->selected)
01166 break;
01167
01168
01169 if (confirm_actions->integer == 1) {
01170 CL_ActorSetMode(selActor, M_PEND_FIRE_L);
01171 VectorCopy(mousePos, selActor->mousePendPos);
01172 } else {
01173 CL_ActorShoot(selActor, mousePos);
01174 }
01175 break;
01176 default:
01177 break;
01178 }
01179 }
01180
01181
01187 void CL_ActorActionMouse (void)
01188 {
01189 if (!selActor || mouseSpace != MS_WORLD)
01190 return;
01191
01192 if (CL_ActorFireModeActivated(selActor->actorMode)) {
01193 CL_ActorSetMode(selActor, M_MOVE);
01194 } else {
01195 CL_ActorMoveMouse();
01196 }
01197 }
01198
01199
01200
01201
01202
01203
01204
01210 static le_t* CL_ActorSearchAtGridPos (const pos3_t pos, qboolean includingStunned)
01211 {
01212 le_t *le;
01213
01214
01215 le = NULL;
01216 while ((le = LE_GetNextInUse(le))) {
01217 if (LE_IsLivingAndVisibleActor(le) && (includingStunned || !LE_IsStunned(le)))
01218 switch (le->fieldSize) {
01219 case ACTOR_SIZE_NORMAL:
01220 if (VectorCompare(le->pos, pos))
01221 return le;
01222 break;
01223 case ACTOR_SIZE_2x2: {
01224 pos3_t actor2x2[3];
01225
01226 VectorSet(actor2x2[0], le->pos[0] + 1, le->pos[1], le->pos[2]);
01227 VectorSet(actor2x2[1], le->pos[0], le->pos[1] + 1, le->pos[2]);
01228 VectorSet(actor2x2[2], le->pos[0] + 1, le->pos[1] + 1, le->pos[2]);
01229 if (VectorCompare(le->pos, pos)
01230 || VectorCompare(actor2x2[0], pos)
01231 || VectorCompare(actor2x2[1], pos)
01232 || VectorCompare(actor2x2[2], pos))
01233 return le;
01234 break;
01235 }
01236 default:
01237 Com_Error(ERR_DROP, "Grid_MoveCalc: unknown actor-size: %i", le->fieldSize);
01238 }
01239 }
01240
01241 return NULL;
01242 }
01243
01249 qboolean CL_ActorMouseTrace (void)
01250 {
01251 int restingLevel;
01252 float cur[2], frustumSlope[2];
01253 const float projectionDistance = 2048.0f;
01254 float nDotP2minusP1;
01255 vec3_t forward, right, up, stop;
01256 vec3_t from, end;
01257 vec3_t mapNormal, P3, P2minusP1;
01258 vec3_t pA, pB, pC;
01259 pos3_t testPos;
01260
01261
01262 cur[0] = (mousePosX * viddef.rx - viddef.viewWidth * 0.5 - viddef.x) / (viddef.viewWidth * 0.5);
01263 cur[1] = (mousePosY * viddef.ry - viddef.viewHeight * 0.5 - viddef.y) / (viddef.viewHeight * 0.5);
01264
01265
01266 VectorCopy(cl.cam.camorg, from);
01267 VectorCopy(cl.cam.axis[0], forward);
01268 VectorCopy(cl.cam.axis[1], right);
01269 VectorCopy(cl.cam.axis[2], up);
01270
01271 if (cl_isometric->integer)
01272 frustumSlope[0] = 10.0 * refdef.fieldOfViewX;
01273 else
01274 frustumSlope[0] = tan(refdef.fieldOfViewX * (M_PI / 360.0)) * projectionDistance;
01275 frustumSlope[1] = frustumSlope[0] * ((float)viddef.viewHeight / (float)viddef.viewWidth);
01276
01277
01278 VectorMA(from, projectionDistance, forward, stop);
01279 VectorMA(stop, cur[0] * frustumSlope[0], right, stop);
01280 VectorMA(stop, cur[1] * -frustumSlope[1], up, stop);
01281
01282
01283 if (cl_isometric->integer)
01284 VectorMA(stop, -projectionDistance * 2, forward, from);
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303 VectorSet(P3, 0., 0., cl_worldlevel->integer * UNIT_HEIGHT + CURSOR_OFFSET);
01304 VectorSet(mapNormal, 0., 0., 1.);
01305 VectorSubtract(stop, from, P2minusP1);
01306 nDotP2minusP1 = DotProduct(mapNormal, P2minusP1);
01307
01308
01309 if (nDotP2minusP1 > 0.01 || nDotP2minusP1 < -0.01) {
01310 float u;
01311 vec3_t dir, P3minusP1;
01312
01313 VectorSubtract(P3, from, P3minusP1);
01314 u = DotProduct(mapNormal, P3minusP1) / nDotP2minusP1;
01315 VectorScale(P2minusP1, (vec_t)u, dir);
01316 VectorAdd(from, dir, end);
01317 } else {
01318 CM_EntTestLineDM(cl.mapTiles, from, stop, end, TL_FLAG_ACTORCLIP, cl.leInlineModelList);
01319 }
01320
01321 VecToPos(end, testPos);
01322
01323
01324
01325 PosToVec(testPos, pA);
01326
01327
01328
01329
01330 if (CL_OutsideMap(pA, MAP_SIZE_OFFSET))
01331 return qfalse;
01332
01333 VectorCopy(pA, pB);
01334 pA[2] += UNIT_HEIGHT;
01335 pB[2] -= UNIT_HEIGHT;
01339 restingLevel = Grid_Fall(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), testPos);
01340 CM_EntTestLineDM(cl.mapTiles, pA, pB, pC, TL_FLAG_ACTORCLIP, cl.leInlineModelList);
01341 VecToPos(pC, testPos);
01342 restingLevel = min(restingLevel, Grid_Fall(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), testPos));
01343
01344
01345 if (restingLevel < cl_worldlevel->integer) {
01346 VectorCopy(end, from);
01347 from[2] -= CURSOR_OFFSET;
01348 CM_EntTestLineDM(cl.mapTiles, from, stop, end, TL_FLAG_ACTORCLIP, cl.leInlineModelList);
01349 VecToPos(end, testPos);
01350 restingLevel = Grid_Fall(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), testPos);
01351 }
01352
01353
01354 if (restingLevel < 0 || restingLevel >= PATHFINDING_HEIGHT)
01355 return qfalse;
01356
01357
01358 VectorCopy(testPos, truePos);
01359 truePos[2] = cl_worldlevel->integer;
01360
01361
01362 testPos[2] = restingLevel;
01363 VectorCopy(testPos, mousePos);
01364
01365 mouseActor = CL_ActorSearchAtGridPos(mousePos, qfalse);
01366
01367
01368 if (selActor && !VectorCompare(mousePos, mouseLastPos)) {
01369 VectorCopy(mousePos, mouseLastPos);
01370 CL_ActorResetMoveLength(selActor);
01371 }
01372
01373 return qtrue;
01374 }
01375
01376
01377
01378
01379
01380
01381
01388 static inline qboolean CL_AddActorWeapon (int objID)
01389 {
01390 if (objID != NONE) {
01391 const objDef_t *od = INVSH_GetItemByIDX(objID);
01392 if (od->isVirtual)
01393 return qfalse;
01394 return qtrue;
01395 }
01396 return qfalse;
01397 }
01398
01408 qboolean CL_AddActor (le_t * le, entity_t * ent)
01409 {
01410 entity_t add;
01411
01412 if (!cl_showactors->integer)
01413 return qfalse;
01414
01415 if (LE_IsStunned(le)) {
01416 CL_ParticleSpawn("stunnedactor", 0, le->origin, NULL, NULL);
01417 } else if (!LE_IsDead(le)) {
01418
01419 const qboolean addLeftHandWeapon = CL_AddActorWeapon(le->left);
01420 const qboolean addRightHandWeapon = CL_AddActorWeapon(le->right);
01421
01422 if (addLeftHandWeapon) {
01423 memset(&add, 0, sizeof(add));
01424
01425 add.model = cls.modelPool[le->left];
01426 if (!add.model)
01427 Com_Error(ERR_DROP, "Actor model for left hand weapon wasn't found");
01428
01429
01430 add.tagent = R_GetFreeEntity() + 2 + addRightHandWeapon;
01431 add.tagname = "tag_lweapon";
01432
01433 R_AddEntity(&add);
01434 }
01435
01436
01437 if (addRightHandWeapon) {
01438 memset(&add, 0, sizeof(add));
01439
01440 add.alpha = le->alpha;
01441 add.model = cls.modelPool[le->right];
01442 if (!add.model)
01443 Com_Error(ERR_DROP, "Actor model for right hand weapon wasn't found");
01444
01445
01446 add.tagent = R_GetFreeEntity() + 2;
01447 add.tagname = "tag_rweapon";
01448
01449 R_AddEntity(&add);
01450 }
01451 }
01452
01453
01454 memset(&add, 0, sizeof(add));
01455
01456 add.alpha = le->alpha;
01457 add.model = le->model2;
01458 if (!add.model)
01459 Com_Error(ERR_DROP, "Actor model wasn't found");
01460 add.skinnum = le->skinnum;
01461
01462
01463 add.tagent = R_GetFreeEntity() + 1;
01464 add.tagname = "tag_head";
01465
01466 if (le->team != cls.team)
01467 add.flags |= RF_IRGOGGLES;
01468
01469 R_AddEntity(&add);
01470
01474 if (LE_IsStunned(le) && le->HP <= le->maxHP / 2)
01475 ent->flags |= RF_BLOOD;
01476 else if (LE_IsDead(le))
01477 ent->flags |= RF_BLOOD;
01478 else
01479 ent->flags |= RF_SHADOW;
01480
01481 ent->flags |= RF_ACTOR;
01482
01483 if (le->team != cls.team)
01484 ent->flags |= RF_IRGOGGLES;
01485
01486 if (!LE_IsDead(le) && !LE_IsStunned(le)) {
01487 if (le->selected)
01488 ent->flags |= RF_SELECTED;
01489 if (le->team == cls.team) {
01490 if (le->pnum == cl.pnum)
01491 ent->flags |= RF_MEMBER;
01492 if (le->pnum != cl.pnum)
01493 ent->flags |= RF_ALLIED;
01494 }
01495 }
01496
01497 if (ent->flags & RF_BLOOD) {
01498 const char *deathTextureName;
01499 assert(le->teamDef != NULL);
01500 deathTextureName = le->teamDef->deathTextureName;
01501 ent->deathTexture = R_FindImage(deathTextureName, it_effect);
01502 }
01503
01504 return qtrue;
01505 }
01506
01507
01508
01509
01510
01511
01512
01518 static void CL_TargetingRadius (const vec3_t center, const float radius)
01519 {
01520 ptl_t *particle = CL_ParticleSpawn("circle", 0, center, NULL, NULL);
01521 particle->size[0] = radius;
01522 }
01523
01534 static void CL_TargetingStraight (const pos3_t fromPos, actorSizeEnum_t fromActorSize, const pos3_t toPos)
01535 {
01536 vec3_t start, end;
01537 vec3_t dir, mid, temp;
01538 trace_t tr;
01539 qboolean crossNo;
01540 le_t *target = NULL;
01541 actorSizeEnum_t toActorSize;
01542
01543 if (!selActor || !selActor->fd)
01544 return;
01545
01546
01547 target = CL_ActorSearchAtGridPos(toPos, qtrue);
01548
01549
01550 toActorSize = target
01551 ? target->fieldSize
01552 : ACTOR_SIZE_NORMAL;
01553
01554 Grid_PosToVec(cl.mapData->map, fromActorSize, fromPos, start);
01555 Grid_PosToVec(cl.mapData->map, toActorSize, toPos, end);
01556 if (mousePosTargettingAlign)
01557 end[2] -= mousePosTargettingAlign;
01558
01559
01560 VectorSubtract(end, start, dir);
01561 VectorNormalize(dir);
01562
01563
01564 if (VectorDistSqr(start, end) > selActor->fd->range * selActor->fd->range) {
01565 VectorMA(start, selActor->fd->range, dir, mid);
01566 crossNo = qtrue;
01567 } else {
01568 VectorCopy(end, mid);
01569 crossNo = qfalse;
01570 }
01571
01572 VectorMA(start, UNIT_SIZE * 1.4, dir, temp);
01573
01574 tr = CL_Trace(start, temp, vec3_origin, vec3_origin, selActor, NULL, MASK_SHOT, cl.mapMaxLevel - 1);
01575 if (tr.le && (tr.le->team == cls.team || LE_IsCivilian(tr.le)) && LE_IsCrouched(tr.le))
01576 VectorMA(start, UNIT_SIZE * 1.4, dir, temp);
01577 else
01578 VectorCopy(start, temp);
01579
01580 tr = CL_Trace(temp, mid, vec3_origin, vec3_origin, selActor, target, MASK_SHOT, cl.mapMaxLevel - 1);
01581
01582 if (tr.fraction < 1.0) {
01583 const float d = VectorDist(temp, mid);
01584 VectorMA(start, tr.fraction * d, dir, mid);
01585 crossNo = qtrue;
01586 }
01587
01588
01589 CL_ParticleSpawn("inRangeTracer", 0, start, mid, NULL);
01590 if (crossNo) {
01591 CL_ParticleSpawn("longRangeTracer", 0, mid, end, NULL);
01592 CL_ParticleSpawn("cross_no", 0, end, NULL, NULL);
01593 } else {
01594 CL_ParticleSpawn("cross", 0, end, NULL, NULL);
01595 }
01596 }
01597
01598
01599 #define GRENADE_PARTITIONS 20
01600
01608 static void CL_TargetingGrenade (const pos3_t fromPos, actorSizeEnum_t fromActorSize, const pos3_t toPos)
01609 {
01610 vec3_t from, at, cross;
01611 float vz, dt;
01612 vec3_t v0, ds, next;
01613 trace_t tr;
01614 qboolean obstructed = qfalse;
01615 int i;
01616 le_t *target = NULL;
01617 actorSizeEnum_t toActorSize;
01618
01619 if (!selActor || !selActor->fd || Vector2Compare(fromPos, toPos))
01620 return;
01621
01622
01623 target = CL_ActorSearchAtGridPos(toPos, qtrue);
01624
01625
01626 toActorSize = target
01627 ? target->fieldSize
01628 : ACTOR_SIZE_NORMAL;
01629
01630
01631 Grid_PosToVec(cl.mapData->map, fromActorSize, fromPos, from);
01632 Grid_PosToVec(cl.mapData->map, toActorSize, toPos, at);
01633 from[2] += selActor->fd->shotOrg[1];
01634
01635
01636 at[2] -= GROUND_DELTA;
01637 if (mousePosTargettingAlign)
01638 at[2] -= mousePosTargettingAlign;
01639 VectorCopy(at, cross);
01640
01641
01642 dt = Com_GrenadeTarget(from, at, selActor->fd->range, selActor->fd->launched, selActor->fd->rolled, v0);
01643 if (!dt) {
01644 CL_ParticleSpawn("cross_no", 0, cross, NULL, NULL);
01645 return;
01646 }
01647
01648 dt /= GRENADE_PARTITIONS;
01649 VectorSubtract(at, from, ds);
01650 VectorScale(ds, 1.0 / GRENADE_PARTITIONS, ds);
01651 ds[2] = 0;
01652
01653
01654 vz = v0[2];
01655
01656 for (i = 0; i < GRENADE_PARTITIONS; i++) {
01657 VectorAdd(from, ds, next);
01658 next[2] += dt * (vz - 0.5 * GRAVITY * dt);
01659 vz -= GRAVITY * dt;
01660 VectorScale(v0, (i + 1.0) / GRENADE_PARTITIONS, at);
01661
01662
01663
01664 tr = CL_Trace(from, next, vec3_origin, vec3_origin, selActor, target, MASK_SHOT, cl.mapMaxLevel - 1);
01665
01666
01667 if (tr.fraction < 1.0) {
01668 obstructed = qtrue;
01669 }
01670
01671
01674 if (obstructed || VectorLength(at) > selActor->fd->range)
01675 CL_ParticleSpawn("longRangeTracer", 0, from, next, NULL);
01676 else
01677 CL_ParticleSpawn("inRangeTracer", 0, from, next, NULL);
01678 VectorCopy(next, from);
01679 }
01680
01681 if (obstructed || VectorLength(at) > selActor->fd->range)
01682 CL_ParticleSpawn("cross_no", 0, cross, NULL, NULL);
01683 else
01684 CL_ParticleSpawn("cross", 0, cross, NULL, NULL);
01685
01686 if (selActor->fd->splrad) {
01687 Grid_PosToVec(cl.mapData->map, toActorSize, toPos, at);
01688 CL_TargetingRadius(at, selActor->fd->splrad);
01689 }
01690 }
01691
01696 static const vec3_t boxSize = { BOX_DELTA_WIDTH, BOX_DELTA_LENGTH, BOX_DELTA_HEIGHT };
01697 #define BoxSize(i,source,target) (target[0]=i*source[0]+((i-1)*UNIT_SIZE),target[1]=i*source[1]+((i-1)*UNIT_SIZE),target[2]=source[2])
01698 #define BoxOffset(i, target) (target[0]=(i-1)*(UNIT_SIZE+BOX_DELTA_WIDTH), target[1]=(i-1)*(UNIT_SIZE+BOX_DELTA_LENGTH), target[2]=0)
01699
01705 static void CL_AddTargetingBox (pos3_t pos, qboolean pendBox)
01706 {
01707 entity_t ent;
01708 vec3_t realBoxSize;
01709 vec3_t cursorOffset;
01710
01711 if (!cl_showactors->integer)
01712 return;
01713
01714 memset(&ent, 0, sizeof(ent));
01715 ent.flags = RF_BOX;
01716
01717 Grid_PosToVec(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), pos, ent.origin);
01718
01719
01720
01721
01722 if (selActor && selActor->actorMoveLength < ROUTING_NOT_REACHABLE
01723 && selActor->actorMoveLength <= CL_ActorUsableTUs(selActor))
01724 VectorSet(ent.color, 0, 1, 0);
01725 else
01726 VectorSet(ent.color, 0.6, 0.68, 1);
01727
01728 VectorAdd(ent.origin, boxSize, ent.oldorigin);
01729
01730
01731 if (mouseActor && !mouseActor->selected) {
01732 ent.alpha = 0.6 + 0.2 * sin((float) cl.time / 80);
01733
01734
01735 if (mouseActor->team != cls.team) {
01736 switch (mouseActor->team) {
01737 case TEAM_CIVILIAN:
01738
01739 VectorSet(ent.color, 1, 1, 0);
01740 break;
01741 default:
01742 if (LE_IsAlien(mouseActor)) {
01743 if (GAME_TeamIsKnown(mouseActor->teamDef))
01744 UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, _(mouseActor->teamDef->name));
01745 else
01746 UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, _("Unknown alien race"));
01747 } else {
01748
01749
01750 UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, CL_PlayerGetName(mouseActor->pnum));
01751 }
01752
01753 VectorSet(ent.color, 1, 0, 0);
01754 break;
01755 }
01756 } else {
01757
01758 if (mouseActor->pnum != cl.pnum) {
01759 UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, CL_PlayerGetName(mouseActor->pnum));
01760 } else {
01761
01762 character_t* chr = CL_ActorGetChr(mouseActor);
01763 assert(chr);
01764 UI_RegisterText(TEXT_MOUSECURSOR_PLAYERNAMES, chr->name);
01765 }
01766
01767 VectorSet(ent.color, 0.2, 0.3, 1);
01768 }
01769
01770 if (selActor) {
01771 BoxOffset(selActor->fieldSize, cursorOffset);
01772 VectorAdd(ent.oldorigin, cursorOffset, ent.oldorigin);
01773 VectorAdd(ent.origin, cursorOffset, ent.origin);
01774 BoxSize(selActor->fieldSize, boxSize, realBoxSize);
01775 VectorSubtract(ent.origin, realBoxSize, ent.origin);
01776 }
01777 } else {
01778 if (selActor) {
01779 BoxOffset(selActor->fieldSize, cursorOffset);
01780 VectorAdd(ent.oldorigin, cursorOffset, ent.oldorigin);
01781 VectorAdd(ent.origin, cursorOffset, ent.origin);
01782
01783 BoxSize(selActor->fieldSize, boxSize, realBoxSize);
01784 VectorSubtract(ent.origin, realBoxSize, ent.origin);
01785 } else {
01786 VectorSubtract(ent.origin, boxSize, ent.origin);
01787 }
01788 ent.alpha = 0.3;
01789 }
01790
01791
01792 if (pendBox) {
01793 VectorSet(ent.color, 0, 1, 1);
01794 ent.alpha = 0.15;
01795 }
01796
01797
01798 R_AddEntity(&ent);
01799 }
01800
01805 void CL_ActorTargetAlign_f (void)
01806 {
01807 int align = GROUND_DELTA;
01808 static int currentPos = 0;
01809
01810
01811 if (!selActor || !selActor->fd)
01812 return;
01813 if (!CL_ActorFireModeActivated(selActor->actorMode))
01814 return;
01815
01816
01817 if (Cmd_Argc() == 2) {
01818 align = atoi(Cmd_Argv(1));
01819 } else {
01820 switch (currentPos) {
01821 case 0:
01822 if (selActor->fd->gravity)
01823 align = -align;
01824 currentPos = 1;
01825 break;
01826 case 1:
01827
01828
01829 if (selActor->fd->gravity)
01830 align = -(2 * align);
01831 else
01832 align = -align;
01833 currentPos = 2;
01834 break;
01835 case 2:
01836
01837 if (selActor->fd->gravity) {
01838 align = 0;
01839 currentPos = 0;
01840 } else {
01841 align = -(2 * align);
01842 currentPos = 3;
01843 }
01844 break;
01845 case 3:
01846 align = 0;
01847 currentPos = 0;
01848 break;
01849 }
01850 }
01851 mousePosTargettingAlign = align;
01852 }
01853
01863 void CL_AddTargeting (void)
01864 {
01865 if (mouseSpace != MS_WORLD || !selActor)
01866 return;
01867
01868 switch (selActor->actorMode) {
01869 case M_MOVE:
01870 case M_PEND_MOVE:
01871
01872
01873
01874
01875 if (mousePos[2] > cl_worldlevel->integer)
01876 return;
01877
01878
01879 CL_AddTargetingBox(mousePos, qfalse);
01880
01881 if (selActor->actorMode == M_PEND_MOVE) {
01882
01883 CL_AddTargetingBox(selActor->mousePendPos, qtrue);
01884 if (!CL_ActorTraceMove(selActor->mousePendPos))
01885 CL_ActorSetMode(selActor, M_MOVE);
01886 }
01887 break;
01888 case M_FIRE_R:
01889 case M_FIRE_L:
01890 if (!selActor->fd)
01891 return;
01892
01893 if (!selActor->fd->gravity)
01894 CL_TargetingStraight(selActor->pos, selActor->fieldSize, mousePos);
01895 else
01896 CL_TargetingGrenade(selActor->pos, selActor->fieldSize, mousePos);
01897 break;
01898 case M_PEND_FIRE_R:
01899 case M_PEND_FIRE_L:
01900 if (!selActor->fd)
01901 return;
01902
01903
01904 CL_AddTargetingBox(mousePos, qfalse);
01905
01906
01907 CL_AddTargetingBox(selActor->mousePendPos, qtrue);
01908
01909 if (!selActor->fd->gravity)
01910 CL_TargetingStraight(selActor->pos, selActor->fieldSize, selActor->mousePendPos);
01911 else
01912 CL_TargetingGrenade(selActor->pos, selActor->fieldSize, selActor->mousePendPos);
01913 break;
01914 default:
01915 break;
01916 }
01917 }
01918
01919 static const vec3_t boxShift = { PLAYER_WIDTH, PLAYER_WIDTH, UNIT_HEIGHT / 2 - DIST_EPSILON };
01920
01925 static void CL_AddPathingBox (pos3_t pos)
01926 {
01927 if (selActor) {
01928 entity_t ent;
01929 int height;
01930 int base;
01931
01932 const byte crouchingState = LE_IsCrouched(selActor) ? 1 : 0;
01933 const int TUneed = Grid_MoveLength(selActor->pathMap, pos, crouchingState, qfalse);
01934 const int TUhave = CL_ActorUsableTUs(selActor);
01935
01936 memset(&ent, 0, sizeof(ent));
01937 ent.flags = RF_PATH;
01938
01939 Grid_PosToVec(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), pos, ent.origin);
01940 VectorSubtract(ent.origin, boxShift, ent.origin);
01941
01942 base = Grid_Floor(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), pos);
01943
01944
01945
01946
01947 if (base < -QuantToModel(PATHFINDING_MAX_FALL)) {
01948 VectorSet(ent.color, 0.0, 0.0, 0.0);
01949 } else {
01950
01951
01952
01953 VectorSet(ent.color, (TUneed > TUhave), (TUneed != ROUTING_NOT_REACHABLE), 0);
01954 }
01955
01956
01957 height = 2 + min(TUneed * (UNIT_HEIGHT - 2) / ROUTING_NOT_REACHABLE, 16);
01958 ent.oldorigin[2] = height;
01959 ent.oldorigin[0] = TUneed;
01960 ent.oldorigin[1] = TUhave;
01961
01962 ent.alpha = 0.25;
01963
01964
01965 R_AddEntity(&ent);
01966 }
01967 }
01968
01974 void CL_AddPathing (void)
01975 {
01976 pos3_t pos;
01977
01978 pos[2] = cl_worldlevel->integer;
01979 for (pos[1] = max(mousePos[1] - 8, 0); pos[1] <= min(mousePos[1] + 8, PATHFINDING_WIDTH - 1); pos[1]++)
01980 for (pos[0] = max(mousePos[0] - 8, 0); pos[0] <= min(mousePos[0] + 8, PATHFINDING_WIDTH - 1); pos[0]++)
01981 CL_AddPathingBox(pos);
01982 }
01983
01989 void CL_ActorPlaySound (const le_t *le, actorSound_t soundType)
01990 {
01991 const char *actorSound = Com_GetActorSound(le->teamDef, le->gender, soundType);
01992 if (actorSound) {
01993 s_sample_t *sample = S_LoadSample(actorSound);
01994 if (sample) {
01995 Com_DPrintf(DEBUG_SOUND|DEBUG_CLIENT, "CL_PlayActorSound: ActorSound: '%s'\n", actorSound);
01996 S_PlaySample(le->origin, sample, SOUND_ATTN_IDLE, SND_VOLUME_DEFAULT);
01997 }
01998 }
01999 }
02000
02004 static void CL_AddArrow (vec3_t from, vec3_t to, float red, float green, float blue)
02005 {
02006 entity_t ent;
02007
02008
02009
02010 memset(&ent, 0, sizeof(ent));
02011 ent.flags = RF_ARROW;
02012 VectorCopy(from, ent.origin);
02013 VectorCopy(to, ent.oldorigin);
02014 VectorSet(ent.color, red, green, blue);
02015
02016 ent.alpha = 0.25;
02017
02018
02019 R_AddEntity(&ent);
02020 }
02021
02025 void CL_DisplayFloorArrows (void)
02026 {
02027 vec3_t base, start;
02028
02029 Grid_PosToVec(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), truePos, base);
02030 VectorCopy(base, start);
02031 base[2] -= QUANT;
02032 start[2] += QUANT;
02033 CL_AddArrow(base, start, 0.0, 0.0, 0.0);
02034 }
02035
02039 void CL_DisplayObstructionArrows (void)
02040 {
02041 vec3_t base, start;
02042
02043 Grid_PosToVec(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), truePos, base);
02044 VectorCopy(base, start);
02045 CL_AddArrow(base, start, 0.0, 0.0, 0.0);
02046 }
02047
02048 #ifdef DEBUG
02049
02052 static void CL_DumpMoveMark_f (void)
02053 {
02054 const byte crouchingState = selActor ? (LE_IsCrouched(selActor) ? 1 : 0) : 0;
02055 const int temp = developer->integer;
02056
02057 if (!selActor)
02058 return;
02059
02060 developer->integer |= DEBUG_PATHING;
02061
02062 CL_BuildForbiddenList();
02063 Grid_MoveCalc(cl.mapData->map, ACTOR_GET_FIELDSIZE(selActor), selActor->pathMap, truePos, crouchingState, MAX_ROUTE, forbiddenList, forbiddenListLength);
02064
02065 developer->integer ^= DEBUG_PATHING;
02066
02067 CL_ActorConditionalMoveCalc(selActor);
02068 developer->integer = temp;
02069 }
02070
02075 static void CL_DumpTUs_f (void)
02076 {
02077 int x, y, crouchingState;
02078 pos3_t pos, loc;
02079
02080 if (!selActor)
02081 return;
02082
02083 crouchingState = LE_IsCrouched(selActor) ? 1 : 0;
02084 VectorCopy(selActor->pos, pos);
02085
02086 Com_Printf("TUs around (%i, %i, %i)\n", pos[0], pos[1], pos[2]);
02087
02088 for (y = max(0, pos[1] - 8); y <= min(PATHFINDING_WIDTH, pos[1] + 8); y++) {
02089 for (x = max(0, pos[0] - 8); x <= min(PATHFINDING_WIDTH, pos[0] + 8); x++) {
02090 VectorSet(loc, x, y, pos[2]);
02091 Com_Printf("%3i ", Grid_MoveLength(selActor->pathMap, loc, crouchingState, qfalse));
02092 }
02093 Com_Printf("\n");
02094 }
02095 Com_Printf("TUs at (%i, %i, %i) = %i\n", pos[0], pos[1], pos[2], Grid_MoveLength(selActor->pathMap, pos, crouchingState, qfalse));
02096 }
02097
02103 static void CL_DebugPathDisplay (actorSizeEnum_t actorSize, int x, int y, int z)
02104 {
02105 Com_Printf("data at cursor XYZ(%i, %i, %i) Floor(%i) Ceiling(%i)\n", x, y, z,
02106 RT_FLOOR(cl.mapData->map, actorSize, x, y, z),
02107 RT_CEILING(cl.mapData->map, actorSize, x, y, z) );
02108 Com_Printf("connections ortho: (PX=%i, NX=%i, PY=%i, NY=%i))\n",
02109 RT_CONN_PX(cl.mapData->map, actorSize, x, y, z),
02110 RT_CONN_NX(cl.mapData->map, actorSize, x, y, z),
02111 RT_CONN_PY(cl.mapData->map, actorSize, x, y, z),
02112 RT_CONN_NY(cl.mapData->map, actorSize, x, y, z) );
02113 Com_Printf("connections diago: (PX_PY=%i, NX_NY=%i, NX_PY=%i, PX_NY=%i))\n",
02114 RT_CONN_PX_PY(cl.mapData->map, actorSize, x, y, z),
02115 RT_CONN_NX_NY(cl.mapData->map, actorSize, x, y, z),
02116 RT_CONN_NX_PY(cl.mapData->map, actorSize, x, y, z),
02117 RT_CONN_PX_NY(cl.mapData->map, actorSize, x, y, z) );
02118 Com_Printf("stepup ortho: (PX=%i, NX=%i, PY=%i, NY=%i))\n",
02119 RT_STEPUP_PX(cl.mapData->map, actorSize, x, y, z),
02120 RT_STEPUP_NX(cl.mapData->map, actorSize, x, y, z),
02121 RT_STEPUP_PY(cl.mapData->map, actorSize, x, y, z),
02122 RT_STEPUP_NY(cl.mapData->map, actorSize, x, y, z) );
02123 }
02124
02125 static void CL_DebugPath_f (void)
02126 {
02127 const actorSizeEnum_t actorSize = ACTOR_SIZE_NORMAL;
02128 const pos_t x = mousePos[0];
02129 const pos_t y = mousePos[1];
02130 const pos_t z = mousePos[2];
02131 int dir;
02132 dir = 3;
02133
02134 if (mouseSpace != MS_WORLD)
02135 return;
02136
02137 CL_DebugPathDisplay(actorSize, x, y, z);
02138 }
02139 #endif
02140
02141
02145 static void CL_ActorNext_f (void)
02146 {
02147 if (CL_BattlescapeRunning()) {
02148 CL_ActorSelectNext();
02149 }
02150 }
02151
02152 static void CL_ActorEquipmentSelect_f (void)
02153 {
02154 int num;
02155 character_t *chr;
02156
02157
02158 if (Cmd_Argc() < 2) {
02159 Com_Printf("Usage: %s <num>\n", Cmd_Argv(0));
02160 return;
02161 }
02162
02163 num = atoi(Cmd_Argv(1));
02164 if (num < 0 || num >= chrDisplayList.num)
02165 return;
02166
02167 chr = chrDisplayList.chr[num];
02168
02169 if (ui_inventory && ui_inventory != &chr->i) {
02170 CONTAINER(chr, csi.idEquip) = ui_inventory->c[csi.idEquip];
02171
02172 ui_inventory->c[csi.idEquip] = NULL;
02173 }
02174 ui_inventory = &chr->i;
02175
02176
02177 UI_ExecuteConfunc("equipdeselect %i", cl_selected->integer);
02178 UI_ExecuteConfunc("equipselect %i", num);
02179
02180
02181 Cvar_ForceSet("cl_selected", va("%i", num));
02182 Cvar_SetValue("mn_ucn", chrDisplayList.chr[num]->ucn);
02183
02184
02185 CL_UpdateCharacterValues(chr, "mn_");
02186 }
02187
02191 static void CL_ActorSelect_f (void)
02192 {
02193
02194 if (Cmd_Argc() < 2) {
02195 Com_Printf("Usage: %s <num>\n", Cmd_Argv(0));
02196 return;
02197 }
02198
02199
02200 if (CL_BattlescapeRunning()) {
02201 const int num = atoi(Cmd_Argv(1));
02202 CL_ActorSelectList(num);
02203 }
02204 }
02205
02209 static void CL_ActorUpdate_f (void)
02210 {
02211 const int num = cl_selected->integer;
02212
02213
02214 if (num < chrDisplayList.num) {
02215 const character_t *chr = chrDisplayList.chr[num];
02216 CL_UpdateCharacterValues(chr, "mn_");
02217 }
02218 }
02219
02226 static qboolean CL_ActorVis (const le_t *le, const le_t *check)
02227 {
02228 vec3_t test, dir;
02229 float delta;
02230 int i;
02231 vec3_t from;
02232
02233 VectorCopy(le->origin, from);
02234
02235
02236 VectorCopy(check->origin, test);
02237 if (LE_IsDead(check)) {
02238 test[2] += PLAYER_DEAD;
02239 delta = 0;
02240 } else if (LE_IsCrouched(check)) {
02241 test[2] += PLAYER_CROUCH - 2;
02242 delta = (PLAYER_CROUCH - PLAYER_MIN) / 2 - 2;
02243 } else {
02244 test[2] += PLAYER_STAND;
02245 delta = (PLAYER_STAND - PLAYER_MIN) / 2 - 2;
02246 }
02247
02248
02249 dir[0] = from[1] - check->origin[1];
02250 dir[1] = check->origin[0] - from[0];
02251 dir[2] = 0;
02252 VectorNormalize(dir);
02253 VectorMA(test, -7, dir, test);
02254
02255
02256 for (i = 0; i < 3; i++) {
02257 const trace_t tr = CL_Trace(from, test, vec3_origin, vec3_origin, le, NULL, MASK_SOLID, cl_worldlevel->integer);
02258
02259 if (tr.fraction < 1.0) {
02260
02261 if (!delta)
02262 return qfalse;
02263 VectorMA(test, 7, dir, test);
02264 test[2] -= delta;
02265 continue;
02266 }
02267
02268 return qtrue;
02269 }
02270
02271 return qfalse;
02272 }
02273
02278 static void CL_NextAlienVisibleFromActor_f (void)
02279 {
02280 static int lastAlien = 0;
02281 int i;
02282
02283 if (!selActor)
02284 return;
02285
02286 if (lastAlien >= cl.numLEs)
02287 lastAlien = 0;
02288
02289 i = lastAlien;
02290 do {
02291 const le_t *le;
02292 if (++i >= cl.numLEs)
02293 i = 0;
02294 le = &cl.LEs[i];
02295 if (le->inuse && LE_IsLivingAndVisibleActor(le) && le->team != cls.team
02296 && !LE_IsCivilian(le)) {
02297 if (CL_ActorVis(selActor, le)) {
02298 lastAlien = i;
02299 CL_ViewCenterAtGridPosition(le->pos);
02300 CL_ParticleSpawn("fadeTracer", 0, selActor->origin, le->origin, NULL);
02301 return;
02302 }
02303 }
02304 } while (i != lastAlien);
02305 }
02306
02311 static void CL_NextAlien_f (void)
02312 {
02313 static int lastAlien = 0;
02314 int i;
02315
02316 if (lastAlien >= cl.numLEs)
02317 lastAlien = 0;
02318
02319 i = lastAlien;
02320 do {
02321 const le_t *le;
02322 if (++i >= cl.numLEs)
02323 i = 0;
02324 le = &cl.LEs[i];
02325 if (le->inuse && LE_IsLivingAndVisibleActor(le) && le->team != cls.team
02326 && le->team != TEAM_CIVILIAN) {
02327 lastAlien = i;
02328 CL_ViewCenterAtGridPosition(le->pos);
02329 return;
02330 }
02331 } while (i != lastAlien);
02332 }
02333
02338 static void CL_ActorConfirmAction (le_t *le)
02339 {
02340 if (le->team != cl.actTeam)
02341 return;
02342
02343
02344 if (le->pnum != cl.pnum)
02345 return;
02346
02347 switch (le->actorMode) {
02348 case M_PEND_MOVE:
02349 CL_ActorStartMove(le, le->mousePendPos);
02350 break;
02351 case M_PEND_FIRE_R:
02352 case M_PEND_FIRE_L:
02353 CL_ActorShoot(le, le->mousePendPos);
02354 break;
02355 default:
02356 break;
02357 }
02358 }
02359
02366 static void CL_ActorConfirmAction_f (void)
02367 {
02368 static int time = 0;
02369
02370 if (time - cl.time < 1000) {
02371 le_t *le = NULL;
02372 while ((le = LE_GetNextInUse(le))) {
02373 if (LE_IsLivingActor(le) && !LE_IsStunned(le) && le->team == cls.team)
02374 CL_ActorConfirmAction(le);
02375 }
02376 } else {
02377 time = cl.time;
02378 if (!selActor)
02379 return;
02380 CL_ActorConfirmAction(selActor);
02381 }
02382 }
02383
02384 void ACTOR_InitStartup (void)
02385 {
02386 cl_reactionleftover = Cvar_Get("cl_reactionleftover", "0", CVAR_USERINFO | CVAR_ARCHIVE, "Minimum TU left over by reaction fire");
02387 cl_autostand = Cvar_Get("cl_autostand","1", CVAR_USERINFO | CVAR_ARCHIVE, "Save accidental TU waste by allowing server to autostand before long walks");
02388 confirm_actions = Cvar_Get("confirm_actions", "0", CVAR_ARCHIVE, "Confirm all actions in tactical mode");
02389 cl_showactors = Cvar_Get("cl_showactors", "1", 0, "Show actors on the battlefield");
02390 Cmd_AddCommand("actor_next", CL_ActorNext_f, _("Toggle to next actor"));
02391 Cmd_AddCommand("actor_select", CL_ActorSelect_f, _("Select an actor from list"));
02392 Cmd_AddCommand("actor_updatecurrent", CL_ActorUpdate_f, _("Update an actor"));
02393 Cmd_AddCommand("actor_select_equip", CL_ActorEquipmentSelect_f, "Select an actor in the equipment menu");
02394 Cmd_AddCommand("actor_standcrouch", CL_ActorStandCrouch_f, _("Toggle stand/crounch"));
02395 Cmd_AddCommand("actor_useheadgear", CL_ActorUseHeadgear_f, _("Toggle the headgear"));
02396 Cmd_AddCommand("actor_dooraction", CL_ActorDoorAction_f, _("Opens or closes a door"));
02397 Cmd_AddCommand("actor_confirmaction", CL_ActorConfirmAction_f, _("Confirm the current action"));
02398 Cmd_AddCommand("actor_nextalien", CL_NextAlienVisibleFromActor_f, _("Toggle to next alien visible from selected actor."));
02399
02400 Cmd_AddCommand("nextalien", CL_NextAlien_f, _("Toggle to next alien"));
02401
02402 #ifdef DEBUG
02403 Cmd_AddCommand("debug_path", CL_DebugPath_f, "Display routing data for current mouse position.");
02404 Cmd_AddCommand("debug_drawblocked", CL_DisplayBlockedPaths_f, "Draws a marker for all blocked map-positions.");
02405 Cmd_AddCommand("debug_movemark", CL_DumpMoveMark_f, "Triggers Grid_MoveMark in every direction at the current truePos.");
02406 Cmd_AddCommand("debug_tus", CL_DumpTUs_f, "Shows a table of the TUs that would be used by the current actor to move relative to its current location");
02407 Cmd_AddCommand("debug_actorinvlist", NULL, "Shows the inventory list of all actors");
02408 #endif
02409 }