/** * @brief Change the amount of available ammo for the given entity * @param ent The entity to change the amount of ammo for * @param ammo The ammo to change * @param amount The new amount of the left ammo * @param shootType The shooting type to determine which container to use */ void G_EventInventoryAmmo (const Edict& ent, const objDef_t* ammo, int amount, shoot_types_t shootType) { G_EventAdd(G_VisToPM(ent.visflags), EV_INV_AMMO, ent.number); gi.WriteByte(amount); gi.WriteByte(ammo->idx); if (IS_SHOT_RIGHT(shootType)) gi.WriteByte(CID_RIGHT); else gi.WriteByte(CID_LEFT); /* x and y value */ gi.WriteByte(0); gi.WriteByte(0); G_EventEnd(); }
/** * @brief Change the amount of available ammo for the given entity * @param ent The entity to change the amount of ammo for * @param ammo The ammo to change * @param amount The new amount of the left ammo * @param shootType The shooting type to determine which container to use */ void G_EventInventoryAmmo (const edict_t* ent, const objDef_t* ammo, int amount, shoot_types_t shootType) { G_EventAdd(G_VisToPM(ent->visflags), EV_INV_AMMO, ent->number); gi.WriteByte(amount); gi.WriteByte(ammo->idx); if (IS_SHOT_RIGHT(shootType)) gi.WriteByte(gi.csi->idRight); else gi.WriteByte(gi.csi->idLeft); /* x and y value */ gi.WriteByte(0); gi.WriteByte(0); G_EventEnd(); }
/** * @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 Calculates the muzzle for the current weapon the actor is shooting with * @param[in] actor The actor that is shooting. Might not be @c nullptr * @param[out] muzzle The muzzle vector to spawn the particle at. Might not be @c nullptr. This is not * modified if there is no tag for the muzzle found for the weapon or item the actor has * in the hand (also see the given shoot type) * @param[in] shootType The shoot type to determine which tag of the actor should be used * to resolve the world coordinates. Also used to determine which item (or better which hand) * should be used to resolve the actor's item. */ static void CL_ActorGetMuzzle (const le_t* actor, vec3_t muzzle, shoot_types_t shootType) { if (actor == nullptr) return; const Item* weapon; const char* tag; if (IS_SHOT_RIGHT(shootType)) { tag = "tag_rweapon"; weapon = actor->getRightHandItem(); } else { tag = "tag_lweapon"; weapon = actor->getLeftHandItem(); } if (!weapon || !weapon->def()) return; const objDef_t* od = weapon->def(); const model_t* model = cls.modelPool[od->idx]; if (!model) Com_Error(ERR_DROP, "Model for item %s is not precached", od->id); /* not every weapon has a muzzle tag assigned */ if (R_GetTagIndexByName(model, "tag_muzzle") == -1) return; float modifiedMatrix[16]; if (!R_GetTagMatrix(actor->model1, tag, actor->as.frame, modifiedMatrix)) Com_Error(ERR_DROP, "Could not find tag %s for actor model %s", tag, actor->model1->name); float mc[16]; GLMatrixAssemble(actor->origin, actor->angles, mc); float matrix[16]; GLMatrixMultiply(mc, modifiedMatrix, matrix); R_GetTagMatrix(model, "tag_muzzle", 0, modifiedMatrix); GLMatrixMultiply(matrix, modifiedMatrix, mc); muzzle[0] = mc[12]; muzzle[1] = mc[13]; muzzle[2] = mc[14]; }
/** * @brief Starts shooting with actor. * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The netchannel message * @sa CL_ActorShootHidden * @sa CL_ActorShoot * @sa CL_ActorDoShoot * @todo Improve detection of left- or right animation. * @sa EV_ACTOR_START_SHOOT */ void CL_ActorStartShoot (const eventRegister_t *self, dbuffer *msg) { le_t *le; pos3_t from, target; int entnum; shoot_types_t shootType; NET_ReadFormat(msg, self->formatString, &entnum, &shootType, &from, &target); /* shooting actor */ le = LE_Get(entnum); /* center view (if wanted) */ if (cl.actTeam != cls.team) CL_CameraRoute(from, target); /* actor dependent stuff following */ if (!le) /* it's OK, the actor is not visible */ return; if (!LE_IsLivingActor(le) || LE_IsStunned(le)) { Com_Printf("CL_ActorStartShoot: LE (%i) not a living actor (type: %i)\n", entnum, le->type); return; } /* ET_ACTORHIDDEN actors don't have a model yet */ if (le->type == ET_ACTORHIDDEN) return; /* Animate - we have to check if it is right or left weapon usage. */ if (IS_SHOT_RIGHT(shootType)) { R_AnimChange(&le->as, le->model1, LE_GetAnim("move", le->right, le->left, le->state)); } else if (IS_SHOT_LEFT(shootType)) { R_AnimChange(&le->as, le->model1, LE_GetAnim("move", le->left, le->right, le->state)); } else if (!IS_SHOT_HEADGEAR(shootType)) { /* no animation for headgear (yet) */ Com_Error(ERR_DROP, "CL_ActorStartShoot: Invalid shootType given (entnum: %i, shootType: %i).\n", shootType, entnum); } }
/** * @brief Shoot with weapon. * @sa CL_ActorShoot * @sa CL_ActorShootHidden * @todo Improve detection of left- or right animation. * @sa EV_ACTOR_SHOOT */ void CL_ActorDoShoot (const eventRegister_t* self, dbuffer* msg) { vec3_t muzzle, impact; int flags, normal, shooterEntnum, victimEntnum; int objIdx; int first; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fdIdx; int surfaceFlags; shoot_types_t shootType; /* read data */ NET_ReadFormat(msg, self->formatString, &shooterEntnum, &victimEntnum, &first, &objIdx, &weapFdsIdx, &fdIdx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &normal); le_t* leVictim; if (victimEntnum != SKIP_LOCAL_ENTITY) { leVictim = LE_Get(victimEntnum); if (!leVictim) LE_NotFoundError(victimEntnum); } else { leVictim = nullptr; } /* get shooter le */ le_t* leShooter = LE_Get(shooterEntnum); /* get the fire def */ const objDef_t* obj = INVSH_GetItemByIDX(objIdx); const fireDef_t* fd = FIRESH_GetFiredef(obj, weapFdsIdx, fdIdx); CL_ActorGetMuzzle(leShooter, muzzle, shootType); /* add effect le */ LE_AddProjectile(fd, flags, muzzle, impact, normal, leVictim); /* start the sound */ if ((first || !fd->soundOnce) && fd->fireSound != nullptr && !(flags & SF_BOUNCED)) S_LoadAndPlaySample(fd->fireSound, muzzle, fd->fireAttenuation, SND_VOLUME_WEAPONS); /* do actor related stuff */ if (!leShooter) return; /* maybe hidden or inuse is false? */ if (!LE_IsActor(leShooter)) Com_Error(ERR_DROP, "Can't shoot, LE not an actor (type: %i)", leShooter->type); /* no animations for hidden actors */ if (leShooter->type == ET_ACTORHIDDEN) return; if (LE_IsDead(leShooter)) { Com_DPrintf(DEBUG_CLIENT, "Can't shoot, actor dead or stunned.\n"); return; } /* Animate - we have to check if it is right or left weapon usage. */ if (IS_SHOT_RIGHT(shootType)) { R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->right, leShooter->left, leShooter->state)); R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->right, leShooter->left, leShooter->state)); } else if (IS_SHOT_LEFT(shootType)) { R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->left, leShooter->right, leShooter->state)); R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->left, leShooter->right, leShooter->state)); } else if (IS_SHOT_HEADGEAR(shootType)) { if (fd->irgoggles) { leShooter->state |= RF_IRGOGGLESSHOT; if (LE_IsSelected(leShooter)) refdef.rendererFlags |= RDF_IRGOGGLES; } } else { /* no animation for headgear (yet) */ Com_Error(ERR_DROP, "CL_ActorDoShoot: Invalid shootType given (entnum: %i, shootType: %i).\n", shootType, shooterEntnum); } }