/** * @sa G_MoraleStopPanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoralePanic (Edict* ent) { G_ClientPrintf(ent->getPlayer(), PRINT_HUD, _("%s panics!"), ent->chr.name); G_PrintStats("%s panics (entnum %i).", ent->chr.name, ent->getIdNum()); /* drop items in hands */ if (G_IsInsane(ent) && ent->chr.teamDef->weapons) { if (ent->getRightHandItem()) G_ActorInvMove(ent, INVDEF(CID_RIGHT), ent->getRightHandItem(), INVDEF(CID_FLOOR), NONE, NONE, true); if (ent->getLeftHandItem()) G_ActorInvMove(ent, INVDEF(CID_LEFT), ent->getLeftHandItem(), INVDEF(CID_FLOOR), NONE, NONE, true); G_ClientStateChange(ent->getPlayer(), ent, ~STATE_REACTION, false); } /* get up */ G_RemoveCrouched(ent); G_ActorSetMaxs(ent); /* send panic */ G_SetPanic(ent); G_EventSendState(G_VisToPM(ent->visflags), *ent); /* center view */ G_EventCenterView(*ent); /* move around a bit, try to avoid opponents */ AI_ActorThink(ent->getPlayer(), ent); /* kill TUs */ G_ActorSetTU(ent, 0); }
/** * @sa G_MoraleStopPanic * @sa G_MoraleRage * @sa G_MoraleStopRage * @sa G_MoraleBehaviour */ static void G_MoralePanic (edict_t * ent, qboolean sanity) { G_ClientPrintf(G_PLAYER_FROM_ENT(ent), PRINT_HUD, _("%s panics!\n"), ent->chr.name); /* drop items in hands */ if (!sanity && ent->chr.teamDef->weapons) { if (RIGHT(ent)) G_ActorInvMove(ent, INVDEF(gi.csi->idRight), RIGHT(ent), INVDEF(gi.csi->idFloor), NONE, NONE, qtrue); if (LEFT(ent)) G_ActorInvMove(ent, INVDEF(gi.csi->idLeft), LEFT(ent), INVDEF(gi.csi->idFloor), NONE, NONE, qtrue); } /* get up */ G_RemoveCrouched(ent); G_ActorSetMaxs(ent); /* send panic */ G_SetPanic(ent); G_EventSendState(G_VisToPM(ent->visflags), ent); /* center view */ G_EventCenterView(ent); /* move around a bit, try to avoid opponents */ AI_ActorThink(G_PLAYER_FROM_ENT(ent), ent); /* kill TUs */ G_ActorSetTU(ent, 0); }
/** * @brief Mission trigger * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger * @todo use level.actualRound to determine the 'King of the Hill' time * @note Don't set a client action here - otherwise the movement event might * be corrupted */ bool G_MissionTouch (edict_t *self, edict_t *activator) { if (!self->owner) return false; switch (self->owner->team) { case TEAM_ALIEN: if (G_IsAlien(activator)) { if (!self->count) { self->count = level.actualRound; gi.BroadcastPrintf(PRINT_HUD, _("Aliens entered target zone!")); } return true; } else { /* reset king of the hill counter */ self->count = 0; } /* general case that also works for multiplayer teams */ default: if (activator->team == self->owner->team) { if (!self->owner->count) { self->owner->count = level.actualRound; if (self->owner->item) { /* search the item in the activator's inventory */ containerIndex_t container; for (container = 0; container < gi.csi->numIDs; container++) { const invDef_t *invDef = INVDEF(container); invList_t *ic; /* ignore items linked from any temp container the actor * must have this in his hands */ if (invDef->temp) continue; for (ic = CONTAINER(activator, container); ic; ic = ic->next) { const objDef_t *od = ic->item.t; /* check whether we found the searched item in the * actor's inventory */ if (Q_streq(od->id, self->owner->item)) { /* drop the weapon - even if out of TUs */ G_ActorInvMove(activator, invDef, ic, INVDEF(gi.csi->idFloor), NONE, NONE, false); gi.BroadcastPrintf(PRINT_HUD, _("Item was placed.")); self->owner->count = level.actualRound; return true; } } } } else { gi.BroadcastPrintf(PRINT_HUD, _("Target zone is occupied!")); } } return true; } else { /* reset king of the hill counter */ self->count = 0; } } return false; }
TEST_F(GameTest, InventoryForDiedAlien) { const char* mapName = "test_game"; ASSERT_NE(-1, FS_CheckFile("maps/%s.bsp", mapName)) << "Map resource '" << mapName << ".bsp' for test is missing."; Actor* diedEnt; Actor* actor; Edict* floorItems; Item* invlist; int count; SV_Map(true, mapName, nullptr); level.activeTeam = TEAM_ALIEN; /* first alien that should die and drop its inventory */ diedEnt = G_EdictsGetNextLivingActorOfTeam(nullptr, TEAM_ALIEN); ASSERT_TRUE(nullptr != diedEnt); diedEnt->HP = 0; ASSERT_TRUE(G_ActorDieOrStun(diedEnt, nullptr)); ASSERT_TRUE(diedEnt->isDead()); /* now try to collect the inventory with a second alien */ actor = G_EdictsGetNextLivingActorOfTeam(nullptr, TEAM_ALIEN); ASSERT_TRUE(nullptr != actor); /* move to the location of the first died alien to drop the inventory into the same floor container */ Player& player = actor->getPlayer(); ASSERT_TRUE(G_IsAIPlayer(&player)); G_ClientMove(player, 0, actor, diedEnt->pos); ASSERT_TRUE(VectorCompare(actor->pos, diedEnt->pos)); floorItems = G_GetFloorItems(actor); ASSERT_TRUE(nullptr != floorItems); ASSERT_EQ(floorItems->getFloor(), actor->getFloor()); /* drop everything to floor to make sure we have space in the backpack */ G_InventoryToFloor(actor); ASSERT_EQ(0, GAMETEST_GetItemCount(actor, CID_BACKPACK)); invlist = actor->getContainer(CID_BACKPACK); ASSERT_TRUE(nullptr == invlist); count = GAMETEST_GetItemCount(actor, CID_FLOOR); if (count > 0) { Item* entryToMove = actor->getFloor(); int tx, ty; actor->chr.inv.findSpace(INVDEF(CID_BACKPACK), entryToMove, &tx, &ty, entryToMove); if (tx == NONE) return; Com_Printf("trying to move item %s from floor into backpack to pos %i:%i\n", entryToMove->def()->name, tx, ty); ASSERT_TRUE(G_ActorInvMove(actor, INVDEF(CID_FLOOR), entryToMove, INVDEF(CID_BACKPACK), tx, ty, false)); ASSERT_EQ(count - 1, GAMETEST_GetItemCount(actor, CID_FLOOR)) << "item " << entryToMove->def()->name << " could not get moved successfully from floor into backpack"; Com_Printf("item %s was removed from floor\n", entryToMove->def()->name); ASSERT_EQ(1, GAMETEST_GetItemCount(actor, CID_BACKPACK)) << "item " << entryToMove->def()->name << " could not get moved successfully from floor into backpack"; Com_Printf("item %s was moved successfully into the backpack\n", entryToMove->def()->name); invlist = actor->getContainer(CID_BACKPACK); ASSERT_TRUE(nullptr != invlist); } }
/** * @brief Mission trigger * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger * @todo use level.actualRound to determine the 'King of the Hill' time * @note Don't set a client action here - otherwise the movement event might * be corrupted */ bool G_MissionTouch (Edict* self, Edict* activator) { if (!self->owner()) return false; switch (self->owner()->getTeam()) { case TEAM_ALIEN: if (G_IsAlien(activator)) { if (!self->count) { self->count = level.actualRound; gi.BroadcastPrintf(PRINT_HUD, _("Aliens entered target zone!")); } return true; } else { /* reset king of the hill counter */ self->count = 0; } /* general case that also works for multiplayer teams */ default: if (!activator->isSameTeamAs(self->owner())) { /* reset king of the hill counter */ self->count = 0; return false; } if (self->owner()->count) return false; self->owner()->count = level.actualRound; if (!self->owner()->item) { gi.BroadcastPrintf(PRINT_HUD, _("Target zone is occupied!")); return true; } /* search the item in the activator's inventory */ /* ignore items linked from any temp container the actor must have this in his hands */ const Container* cont = nullptr; while ((cont = activator->chr.inv.getNextCont(cont))) { Item* item = nullptr; while ((item = cont->getNextItem(item))) { const objDef_t* od = item->def(); /* check whether we found the searched item in the actor's inventory */ if (!Q_streq(od->id, self->owner()->item)) continue; /* drop the weapon - even if out of TUs */ G_ActorInvMove(makeActor(activator), cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false); gi.BroadcastPrintf(PRINT_HUD, _("Item was placed.")); self->owner()->count = level.actualRound; return true; } } break; } return true; }
/** * @brief Reload weapon with actor. * @param[in] ent Pointer to an actor reloading weapon. * @param[in] invDef Reloading weapon in right or left hand. * @sa AI_ActorThink */ void G_ActorReload (Edict* ent, const invDef_t* invDef) { const objDef_t* weapon; if (ent->getContainer(invDef->id)) { weapon = ent->getContainer(invDef->id)->def(); } else if (invDef->isLeftDef() && ent->getRightHandItem()->isHeldTwoHanded()) { /* Check for two-handed weapon */ invDef = INVDEF(CID_RIGHT); weapon = ent->getRightHandItem()->def(); } else return; assert(weapon); /* LordHavoc: Check if item is researched when in singleplayer? I don't think this is really a * cheat issue as in singleplayer there is no way to inject fake client commands in the virtual * network buffer, and in multiplayer everything is researched */ /* search for clips and select the one that is available easily */ /* also try the temp containers */ const invDef_t* bestContainer = nullptr; Item* ammoItem = nullptr; int tu = 100; const Container* cont = nullptr; while ((cont = ent->chr.inv.getNextCont(cont, true))) { if (cont->def()->out >= tu) continue; /* Once we've found at least one clip, there's no point * searching other containers if it would take longer * to retrieve the ammo from them than the one * we've already found. */ Item* item = nullptr; while ((item = cont->getNextItem(item))) { if (item->def()->isLoadableInWeapon(weapon)) { ammoItem = item; bestContainer = INVDEF(cont->id); tu = bestContainer->out; break; } } } /* send request */ if (bestContainer) G_ActorInvMove(ent, bestContainer, ammoItem, invDef, 0, 0, true); }
/** * @brief Retrieve or collect weapon from any linked container for the actor * @note This function will also collect items from floor containers when the actor * is standing on a given point. * @sa AI_ActorThink */ void G_ClientGetWeaponFromInventory (edict_t *ent) { invList_t *ic; invList_t *icFinal; const invDef_t *invDef; int tu; containerIndex_t container; const invDef_t *bestContainer; /* e.g. bloodspiders are not allowed to carry or collect weapons */ if (!ent->chr.teamDef->weapons) return; /* search for weapons and select the one that is available easily */ tu = 100; invDef = INVDEF(gi.csi->idRight); bestContainer = NULL; icFinal = NULL; /* also try the temp containers */ for (container = 0; container < gi.csi->numIDs; container++) { if (INVDEF(container)->out < tu) { /* Once we've found at least one clip, there's no point * searching other containers if it would take longer * to retrieve the ammo from them than the one * we've already found. */ for (ic = CONTAINER(ent, container); ic; ic = ic->next) { assert(ic->item.item); if (ic->item.item->weapon && (ic->item.ammoLeft > 0 || !ic->item.item->reload)) { icFinal = ic; bestContainer = INVDEF(container); tu = bestContainer->out; break; } } } } /* send request */ if (bestContainer) G_ActorInvMove(ent, bestContainer, icFinal, invDef, 0, 0, true); }
/** * @brief The client sent us a message that he did something. We now execute the related function(s) and notify him if necessary. * @param[in] player The player to execute the action for (the actor belongs to this player) * @note a client action will also send the server side edict number to determine the actor */ int G_ClientAction (player_t * player) { player_action_t action; int num; pos3_t pos; int i; fireDefIndex_t firemode; int from, fx, fy, to, tx, ty; actorHands_t hand; int fmIdx, objIdx; int resCrouch, resShot; edict_t *ent; const char *format; /* read the header */ action = (player_action_t)gi.ReadByte(); num = gi.ReadShort(); ent = G_EdictsGetByNum(num); if (ent == NULL) return action; format = pa_format[action]; switch (action) { case PA_NULL: /* do nothing on a null action */ break; case PA_TURN: gi.ReadFormat(format, &i); G_ClientTurn(player, ent, (dvec_t) i); break; case PA_MOVE: gi.ReadFormat(format, &pos); G_ClientMove(player, player->pers.team, ent, pos); break; case PA_STATE: gi.ReadFormat(format, &i); G_ClientStateChange(player, ent, i, true); break; case PA_SHOOT: gi.ReadFormat(format, &pos, &i, &firemode, &from); G_ClientShoot(player, ent, pos, i, firemode, NULL, true, from); break; case PA_INVMOVE: gi.ReadFormat(format, &from, &fx, &fy, &to, &tx, &ty); if (from < 0 || from >= gi.csi->numIDs || to < 0 || to >= gi.csi->numIDs) { gi.DPrintf("G_ClientAction: PA_INVMOVE Container index out of range. (from: %i, to: %i)\n", from, to); } else { const invDef_t *fromPtr = INVDEF(from); const invDef_t *toPtr = INVDEF(to); invList_t *fromItem = INVSH_SearchInInventory(&ent->chr.i, fromPtr, fx, fy); if (fromItem) G_ActorInvMove(ent, fromPtr, fromItem, toPtr, tx, ty, true); } break; case PA_USE: if (ent->clientAction) { edict_t *actionEnt; /* read the door the client wants to open */ gi.ReadFormat(format, &i); /* get the door edict */ actionEnt = G_EdictsGetByNum(i); /* maybe the door is no longer 'alive' because it was destroyed */ if (actionEnt && ent->clientAction == actionEnt) { if (G_IsDoor(actionEnt)) { G_ActorUseDoor(ent, actionEnt); } } } break; case PA_REACT_SELECT: gi.ReadFormat(format, &hand, &fmIdx, &objIdx); G_ReactionFireSettingsUpdate(ent, fmIdx, hand, INVSH_GetItemByIDX(objIdx)); break; case PA_RESERVE_STATE: gi.ReadFormat(format, &resShot, &resCrouch); G_ActorReserveTUs(ent, ent->chr.reservedTus.reaction, resShot, resCrouch); break; default: gi.Error("G_ClientAction: Unknown action!\n"); } return action; }
/** * @brief Mission trigger * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger * @note Don't set a client action here - otherwise the movement event might * be corrupted */ bool G_MissionTouch (Edict* self, Edict* activator) { if (!G_IsLivingActor(activator)) return false; Actor* actor = makeActor(activator); const char* const actorTeam = G_MissionGetTeamString(actor->getTeam()); if (!G_IsCivilian(actor) && self->isOpponent(actor)) { if (!self->item && self->count) { if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking the %s!"), actorTeam, self->targetname); } else { const char* const teamName = G_MissionGetTeamString(self->getTeam()); gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking %s target zone!"), actorTeam, teamName); } /* reset king of the hill counter */ self->count = 0; } return false; } if (self->count) return false; if (self->isSameTeamAs(actor)) { self->count = level.actualRound; if (!self->item) { linkedList_t* touched = self->touchedList; while (touched) { const Edict* const ent = static_cast<const Edict* const>(touched->data); if (!self->isSameTeamAs(ent) && !G_IsDead(ent)) { return true; } touched = touched->next; } if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied the %s!"), actorTeam, self->targetname); } else { gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied their target zone!"), actorTeam); } return true; } } /* search the item in the activator's inventory */ /* ignore items linked from any temp container the actor must have this in his hands */ const Container* cont = nullptr; while ((cont = actor->chr.inv.getNextCont(cont))) { Item* item = nullptr; while ((item = cont->getNextItem(item))) { const objDef_t* od = item->def(); /* check whether we found the searched item in the actor's inventory */ if (!Q_streq(od->id, self->item)) continue; /* drop the weapon - even if out of TUs */ G_ActorInvMove(actor, cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false); if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed at the %s."), item->def()->name, self->targetname); } else { gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed."), item->def()->name); } self->count = level.actualRound; return true; } } return false; }
static void testInventoryWithTwoDiedAliensOnTheSameGridTile (void) { const char *mapName = "test_game"; if (FS_CheckFile("maps/%s.bsp", mapName) != -1) { edict_t *diedEnt; edict_t *diedEnt2; edict_t *ent; edict_t *floorItems; invList_t *invlist; int count; /* the other tests didn't call the server shutdown function to clean up */ OBJZERO(*sv); SV_Map(true, mapName, NULL); level.activeTeam = TEAM_ALIEN; /* first alien that should die and drop its inventory */ diedEnt = G_EdictsGetNextLivingActorOfTeam(NULL, TEAM_ALIEN); CU_ASSERT_PTR_NOT_NULL_FATAL(diedEnt); diedEnt->HP = 0; G_ActorDieOrStun(diedEnt, NULL); CU_ASSERT_TRUE_FATAL(G_IsDead(diedEnt)); /* second alien that should die and drop its inventory */ diedEnt2 = G_EdictsGetNextLivingActorOfTeam(NULL, TEAM_ALIEN); CU_ASSERT_PTR_NOT_NULL_FATAL(diedEnt2); /* move to the location of the first died alien to drop the inventory into the same floor container */ Player &player = diedEnt2->getPlayer(); CU_ASSERT_TRUE_FATAL(G_IsAIPlayer(&player)); G_ClientMove(player, 0, diedEnt2, diedEnt->pos); CU_ASSERT_TRUE_FATAL(VectorCompare(diedEnt2->pos, diedEnt->pos)); diedEnt2->HP = 0; G_ActorDieOrStun(diedEnt2, NULL); CU_ASSERT_TRUE_FATAL(G_IsDead(diedEnt2)); /* now try to collect the inventory with a third alien */ ent = G_EdictsGetNextLivingActorOfTeam(NULL, TEAM_ALIEN); CU_ASSERT_PTR_NOT_NULL_FATAL(ent); player = ent->getPlayer(); CU_ASSERT_TRUE_FATAL(G_IsAIPlayer(&player)); G_ClientMove(player, 0, ent, diedEnt->pos); CU_ASSERT_TRUE_FATAL(VectorCompare(ent->pos, diedEnt->pos)); floorItems = G_GetFloorItems(ent); CU_ASSERT_PTR_NOT_NULL_FATAL(floorItems); CU_ASSERT_PTR_EQUAL(floorItems->getFloor(), ent->getFloor()); /* drop everything to floor to make sure we have space in the backpack */ G_InventoryToFloor(ent); CU_ASSERT_EQUAL(GAMETEST_GetItemCount(ent, CID_BACKPACK), 0); invlist = ent->getContainer(CID_BACKPACK); CU_ASSERT_PTR_NULL_FATAL(invlist); count = GAMETEST_GetItemCount(ent, CID_FLOOR); if (count > 0) { invList_t *entryToMove = ent->getFloor(); int tx, ty; ent->chr.inv.findSpace(INVDEF(CID_BACKPACK), entryToMove, &tx, &ty, entryToMove); if (tx != NONE) { Com_Printf("trying to move item %s from floor into backpack to pos %i:%i\n", entryToMove->def()->name, tx, ty); CU_ASSERT_TRUE(G_ActorInvMove(ent, INVDEF(CID_FLOOR), entryToMove, INVDEF(CID_BACKPACK), tx, ty, false)); UFO_CU_ASSERT_EQUAL_INT_MSG_FATAL(GAMETEST_GetItemCount(ent, CID_FLOOR), count - 1, va("item %s could not get moved successfully from floor into backpack", entryToMove->def()->name)); Com_Printf("item %s was removed from floor\n", entryToMove->def()->name); UFO_CU_ASSERT_EQUAL_INT_MSG_FATAL(GAMETEST_GetItemCount(ent, CID_BACKPACK), 1, va("item %s could not get moved successfully from floor into backpack", entryToMove->def()->name)); Com_Printf("item %s was moved successfully into the backpack\n", entryToMove->def()->name); invlist = ent->getContainer(CID_BACKPACK); CU_ASSERT_PTR_NOT_NULL_FATAL(invlist); } } SV_ShutdownGameProgs(); } else { UFO_CU_FAIL_MSG(va("Map resource '%s.bsp' for test is missing.", mapName)); } }