/** * @brief Perform the reaction fire shot * @param[in] player The player this action belongs to (the human player or the ai) * @param[in] shooter The actor that is trying to shoot * @param[in] at Position to fire on. * @param[in] type What type of shot this is (left, right reaction-left etc...). * @param[in] firemode The firemode index of the ammo for the used weapon (objDef.fd[][x]) . * @return true if everything went ok (i.e. the shot(s) where fired ok), otherwise false. * @sa G_ClientShoot */ static bool G_ReactionFireShoot (const player_t *player, edict_t *shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode) { const int minhit = 30; shot_mock_t mock; int ff, i; /* this is the max amount of friendly units that were hit during the mock calculation */ int maxff; if (G_IsInsane(shooter)) maxff = 100; else if (G_IsRaged(shooter)) maxff = 60; else if (G_IsPaniced(shooter)) maxff = 30; else if (G_IsShaken(shooter)) maxff = 15; else maxff = 5; /* calculate the mock values - e.g. how many friendly units we would hit * when opening the reaction fire */ OBJZERO(mock); for (i = 0; i < 100; i++) if (!G_ClientShoot(player, shooter, at, type, firemode, &mock, false, 0)) break; ff = mock.friendCount + (G_IsAlien(shooter) ? 0 : mock.civilian); if (ff <= maxff && mock.enemyCount >= minhit) return G_ClientShoot(player, shooter, at, type, firemode, NULL, false, 0); return false; }
/** * @brief Perform the reaction fire shot * @param[in] shooter The actor that is trying to shoot * @param[in] at Position to fire on. * @param[in] type What type of shot this is (left, right reaction-left etc...). * @param[in] firemode The firemode index of the ammo for the used weapon (objDef.fd[][x]) . * @return true if everything went ok (i.e. the shot(s) where fired ok), otherwise false. * @sa G_ClientShoot */ bool ReactionFire::shoot (Actor* shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode) { /* this is the max amount of friendly units that were hit during the mock calculation */ int maxff; if (shooter->isInsane()) maxff = 100; else if (shooter->isRaged()) maxff = 60; else if (shooter->isPanicked()) maxff = 30; else if (shooter->isShaken()) maxff = 15; else maxff = 5; /* calculate the mock values - e.g. how many friendly units we would hit * when opening the reaction fire */ const int minhit = 30; shot_mock_t mock; const Player& player = shooter->getPlayer(); for (int i = 0; i < 100; i++) if (!G_ClientShoot(player, shooter, at, type, firemode, &mock, false, 0)) break; const int ff = mock.friendCount + (G_IsAlien(shooter) ? 0 : mock.civilian); if (ff <= maxff && mock.enemyCount >= minhit) return G_ClientShoot(player, shooter, at, type, firemode, nullptr, false, 0); return false; }
/** * @brief Perform the reaction fire shot * @param[in] shooter The actor that is trying to shoot * @param[in] at Position to fire on. * @param[in] type What type of shot this is (left, right reaction-left etc...). * @param[in] firemode The firemode index of the ammo for the used weapon (objDef.fd[][x]) . * @return true if everything went ok (i.e. the shot(s) where fired ok), otherwise false. * @sa G_ClientShoot */ bool ReactionFire::shoot (Actor* shooter, const pos3_t at, shoot_types_t type, fireDefIndex_t firemode) { const Item* weapon = nullptr; if (IS_SHOT_RIGHT(type)) { weapon = shooter->getRightHandItem(); if (!weapon) return false; } else { weapon = shooter->getLeftHandItem(); if (!weapon) return false; } const fireDef_t* fdArray = weapon->getFiredefs(); if (!fdArray) return false; /* Adjust the number of samples we take so that we don't end firing thousands of shots * in case the fire mode is multi-shot */ const int shotsPerFD = fdArray[firemode].shots; const int samples = std::max(1, 100 / shotsPerFD); const Player& player = shooter->getPlayer(); shot_mock_t mock; for (int i = 0; i < samples; ++i) if (!G_ClientShoot(player, shooter, at, type, firemode, &mock, false, 0)) break; /* this is the max amount of friendly units that were hit during the mock calculation */ const int maxShots = samples * shotsPerFD; int maxff; if (shooter->isInsane()) maxff = maxShots; else if (shooter->isRaged()) maxff = maxShots * 2 / 3; else if (shooter->isPanicked()) maxff = maxShots / 3; else if (shooter->isShaken()) maxff = maxShots / 6; else maxff = maxShots / 20; /* calculate the mock values - e.g. how many friendly units we would hit * when opening the reaction fire */ const int ff = mock.friendCount + (G_IsAlien(shooter) ? 0 : mock.civilian); const int hits = shooter->isInsane() ? ff + mock.enemyCount : mock.enemyCount; if (ff <= maxff && hits > 0) return G_ClientShoot(player, shooter, at, type, firemode, nullptr, false, 0); return false; }
/** * @brief Shoots the actor. */ static int actorL_shoot (lua_State *L) { int tu, shots; shoot_types_t shootType; const item_t *item; const fireDef_t *fdArray; assert(lua_isactor(L, 1)); /* Target */ aiActor_t *target = lua_toactor(L, 1); /* Number of TU to spend shooting, fire mode will adjust to that. */ if (lua_gettop(L) > 1) { assert(lua_isnumber(L, 2)); /* Must be a number. */ tu = (int) lua_tonumber(L, 2); } else { tu = G_ActorUsableTUs(AIL_ent); } shootType = ST_RIGHT; item = AI_GetItemForShootType(shootType, AIL_ent); if (item == NULL) { shootType = ST_LEFT; item = AI_GetItemForShootType(shootType, AIL_ent); } /* Failure - no weapon. */ if (item == NULL) { lua_pushboolean(L, 0); return 1; } /** @todo Choose fire mode based on TU available - currently the first one is used. */ fdArray = FIRESH_FiredefForWeapon(item); if (fdArray == NULL) { /* Failure - no weapon. */ lua_pushboolean(L, 0); return 1; } shots = tu / G_GetActorTimeForFiredef(AIL_ent, fdArray, false); while (shots > 0) { shots--; /** @todo actually handle fire modes */ G_ClientShoot(AIL_player, AIL_ent, target->ent->pos, shootType, 0, NULL, true, 0); } /* Success. */ lua_pushboolean(L, 1); return 1; }
/** * @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; }