/** * @brief Parses the actor wound stats that come from the netchannel * @sa CL_ParseEvent * @sa G_SendStats */ void CL_ActorWound (const eventRegister_t *self, struct dbuffer *msg) { le_t *le; int entnum, bodyPart, wounds, treatment; NET_ReadFormat(msg, self->formatString, &entnum, &bodyPart, &wounds, &treatment); le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); switch (le->type) { case ET_ACTORHIDDEN: case ET_ACTOR: case ET_ACTOR2x2: break; default: Com_Printf("CL_ActorWound: LE (%i) not an actor (type: %i)\n", entnum, le->type); return; } le->wounds.woundLevel[bodyPart] = wounds; le->wounds.treatmentLevel[bodyPart] = treatment; }
/** * @brief Play a sound on the client side * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg holds the network data * @sa EV_SOUND * @note if the last character of the sound file string that was sent by the * server is a '+', we will select a random sound file by replacing the '+' * character with a number between 01..99 */ void CL_SoundEvent (const eventRegister_t *self, dbuffer *msg) { char sound[MAX_QPATH]; vec3_t origin; int number; le_t *le; size_t length; /* read data */ NET_ReadFormat(msg, self->formatString, &number, &origin, &sound, sizeof(sound)); le = LE_Get(number); if (le) { if (LE_IsLivingActor(le) && le->team != cls.team) { /** @todo render */ } else if (LE_IsDoor(le) || LE_IsBreakable(le)) { /** @todo render */ } } length = strlen(sound) - 1; if (sound[length] == '+') { int i; sound[length] = '\0'; for (i = 1; i <= 99; i++) { if (FS_CheckFile("sounds/%s%02i", sound, i) == -1) break; } Com_sprintf(sound + length, sizeof(sound) - length, "%02i", rand() % i + 1); } Com_DPrintf(DEBUG_SOUND, "Play network sample %s at (%f:%f:%f)\n", sound, origin[0], origin[1], origin[2]); S_LoadAndPlaySample(sound, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT); }
/** * @brief Parses the actor stats that comes from the netchannel * @sa CL_ParseEvent * @sa G_SendStats */ void CL_ActorStats (const eventRegister_t *self, dbuffer *msg) { le_t *le; int entnum, oldTUs = 0; entnum = NET_ReadShort(msg); le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); switch (le->type) { case ET_ACTORHIDDEN: case ET_ACTOR: case ET_ACTOR2x2: break; default: Com_Printf("CL_ActorStats: LE (%i) not an actor (type: %i)\n", entnum, le->type); return; } if (LE_IsSelected(le)) oldTUs = le->TU; NET_ReadFormat(msg, self->formatString, &le->TU, &le->HP, &le->STUN, &le->morale); if (le->TU > le->maxTU) le->maxTU = le->TU; if (le->HP > le->maxHP) le->maxHP = le->HP; if (le->morale > le->maxMorale) le->maxMorale = le->morale; /* if selActor's timeunits have changed, update movelength */ if (le->TU != oldTUs && LE_IsSelected(le)) CL_ActorResetMoveLength(le); }
/** * @brief Revitalizes a stunned actor (all that is needed is the local entity state set). * @param[in] msg The netchannel message * @param[in] self Pointer to the event structure that is currently executed */ void CL_ActorRevitalised (const eventRegister_t* self, dbuffer* msg) { int entnum, state; NET_ReadFormat(msg, self->formatString, &entnum, &state); /* get les */ le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); if (!LE_IsStunned(le) && !LE_IsLivingActor(le)) Com_Error(ERR_DROP, "CL_ActorRevitalised: Can't revitalise, LE is not a dead or stunned actor"); LE_Lock(le); /* link any floor container into the actor temp floor container */ le_t* floor = LE_Find(ET_ITEM, le->pos); if (floor) le->setFloor(floor); le->state = state; /* play animation */ LE_SetThink(le, LET_StartIdle); /* Print some info. */ if (le->team == cls.team) { const character_t* chr = CL_ActorGetChr(le); if (chr) { char tmpbuf[128]; Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was revitalised\n"), chr->name); HUD_DisplayMessage(tmpbuf); } } else { switch (le->team) { case (TEAM_CIVILIAN): HUD_DisplayMessage(_("A civilian was revitalised.")); break; case (TEAM_ALIEN): HUD_DisplayMessage(_("An alien was revitalised.")); break; case (TEAM_PHALANX): HUD_DisplayMessage(_("A soldier was revitalised.")); break; default: HUD_DisplayMessage(va(_("A member of team %i was revitalised."), le->team)); break; } } le->aabb.setMaxs(player_maxs); if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } /* add team members to the actor list */ CL_ActorAddToTeamList(le); /* update pathing as we maybe not can walk onto this actor anymore */ CL_ActorConditionalMoveCalc(selActor); LE_Unlock(le); }
/** * @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); } }
/** * @brief Kills an actor (all that is needed is the local entity state set to STATE_DEAD). * @note Also changes the animation to a random death sequence and appends the dead animation * @param[in] msg The netchannel message * @param[in] self Pointer to the event structure that is currently executed */ void CL_ActorDie (const eventRegister_t *self, dbuffer *msg) { le_t *le; int entnum, state, playerNum; NET_ReadFormat(msg, self->formatString, &entnum, &state, &playerNum); /* get les */ le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); if (!LE_IsLivingActor(le)) Com_Error(ERR_DROP, "CL_ActorDie: Can't kill, LE is not an actor (type: %i)", le->type); if (!LE_IsStunned(le) && LE_IsDead(le)) Com_Error(ERR_DROP, "CL_ActorDie: Can't kill, actor already dead"); LE_Lock(le); /* set relevant vars */ FLOOR(le) = NULL; le->state = state; /* count spotted aliens */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); /* play animation */ LE_SetThink(le, NULL); R_AnimChange(&le->as, le->model1, va("death%i", LE_GetAnimationIndexForDeath(le))); R_AnimAppend(&le->as, le->model1, va("dead%i", LE_GetAnimationIndexForDeath(le))); /* Print some info about the death or stun. */ if (le->team == cls.team) { if (playerNum != le->pnum) { const char *playerName = CL_PlayerGetName(playerNum); char tmpbuf[128]; Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s lost a soldier\n"), playerName); HUD_DisplayMessage(tmpbuf); } else { const character_t *chr = CL_ActorGetChr(le); if (chr) { char tmpbuf[128]; if (LE_IsStunned(le)) { Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was stunned\n"), chr->name); } else { Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was killed\n"), chr->name); } HUD_DisplayMessage(tmpbuf); } } } else { switch (le->team) { case (TEAM_CIVILIAN): if (LE_IsStunned(le)) HUD_DisplayMessage(_("A civilian was stunned.")); else HUD_DisplayMessage(_("A civilian was killed.")); break; case (TEAM_ALIEN): if (LE_IsStunned(le)) HUD_DisplayMessage(_("An alien was stunned.")); else HUD_DisplayMessage(_("An alien was killed.")); break; case (TEAM_PHALANX): if (LE_IsStunned(le)) HUD_DisplayMessage(_("A soldier was stunned.")); else HUD_DisplayMessage(_("A soldier was killed.")); break; default: if (LE_IsStunned(le)) HUD_DisplayMessage(va(_("A member of team %i was stunned."), le->team)); else HUD_DisplayMessage(va(_("A member of team %i was killed."), le->team)); break; } } /** * @todo CHRSH_IsTeamDefRobot: spawn smoke particles for robots */ CL_ActorPlaySound(le, SND_DEATH); VectorCopy(player_dead_maxs, le->maxs); if (!LE_IsStunned(le)) le->contents = CONTENTS_DEADACTOR; CL_ActorRemoveFromTeamList(le); /* update pathing as we maybe can walk onto the dead actor now */ CL_ActorConditionalMoveCalc(selActor); LE_Unlock(le); }
/** * @sa CL_ActorAddToTeamList * @sa G_AppearPerishEvent * @sa CL_ActorAdd * @note EV_ACTOR_APPEAR */ void CL_ActorAppear (const eventRegister_t *self, struct dbuffer *msg) { le_t *le, *leResponsible; int entnum, entnumResponsible, modelnum1, modelnum2; int teamDefID = -1; /* check if the actor is already visible */ entnum = NET_ReadShort(msg); entnumResponsible = NET_ReadShort(msg); le = LE_Get(entnum); leResponsible = LE_Get(entnumResponsible); if (entnumResponsible != SKIP_LOCAL_ENTITY && !leResponsible) LE_NotFoundError(entnumResponsible); /* mission start - no actor is spawned yet - so create it */ if (!le) le = LE_Add(entnum); /* Locking should be unnecessary if CL_CheckDefault filters this call, since this event starts and * ends in this function only. Adding lock/unlock just to be sure. */ LE_Lock(le); /* maybe added via CL_ActorAdd before */ le->invis = false; /* get the info */ NET_ReadFormat(msg, self->formatString, &le->team, &teamDefID, &le->gender, &le->ucn, &le->pnum, &le->pos, &le->angle, &le->right, &le->left, &modelnum1, &modelnum2, &le->bodySkin, &le->headSkin, &le->state, &le->fieldSize, &le->maxTU, &le->maxMorale, &le->maxHP); if (teamDefID < 0 || teamDefID > csi.numTeamDefs) Com_Printf("CL_ActorAppear: Invalid teamDef index\n"); else le->teamDef = &csi.teamDef[teamDefID]; switch (le->fieldSize) { case ACTOR_SIZE_NORMAL: le->addFunc = CL_AddActor; le->type = ET_ACTOR; break; case ACTOR_SIZE_2x2: le->addFunc = CL_AddUGV; le->type = ET_ACTOR2x2; break; default: Com_Error(ERR_DROP, "Unknown fieldSize for le in CL_ActorAppear (EV_ACTOR_APPEAR)"); } le->modelnum1 = modelnum1; le->modelnum2 = modelnum2; le->model1 = LE_GetDrawModel(modelnum1); le->model2 = LE_GetDrawModel(modelnum2); Grid_PosToVec(cl.mapData->map, le->fieldSize, le->pos, le->origin); le->angles[YAW] = directionAngles[le->angle]; if (LE_IsDead(le) && !LE_IsStunned(le)) le->contents = CONTENTS_DEADACTOR; else le->contents = CONTENTS_ACTOR; VectorCopy(player_mins, le->mins); if (LE_IsDead(le)) VectorCopy(player_dead_maxs, le->maxs); else VectorCopy(player_maxs, le->maxs); LE_SetThink(le, LET_StartIdle); /* count spotted aliens (also stunned) */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); if (LE_IsLivingActor(le)) { if (cl.actTeam != cls.team) { /* center view (if wanted) */ LE_CenterView(le); } /* draw line of sight */ if (le->team != cls.team) { if (leResponsible) CL_DrawLineOfSight(leResponsible, le); /* message */ if (le->team != TEAM_CIVILIAN) { if (GAME_TeamIsKnown(le->teamDef)) { char tmpbuf[128]; Com_sprintf(tmpbuf, sizeof(tmpbuf), _("Enemy spotted: %s!"), _(le->teamDef->name)); HUD_DisplayMessage(tmpbuf); } else HUD_DisplayMessage(_("Unknown enemy spotted!")); } else HUD_DisplayMessage(_("Civilian spotted.")); /* update pathing as new actor could block path */ CL_ActorConditionalMoveCalc(leResponsible ? leResponsible : selActor); } } /* add team members to the actor list */ CL_ActorAddToTeamList(le); LE_Unlock(le); }
/** * @brief Register local entities for SOLID_BSP models like func_breakable or func_door * @note func_breakable, func_door * @sa G_SendEdictsAndBrushModels * @sa EV_ADD_BRUSH_MODEL * @sa CL_SpawnParseEntitystring */ void CL_AddBrushModel (const eventRegister_t *self, struct dbuffer *msg) { le_t *le; int entnum, modelnum1, levelflags, speed, dir; entity_type_t type; const cBspModel_t *model; int angle; vec3_t origin, angles; NET_ReadFormat(msg, self->formatString, &type, &entnum, &modelnum1, &levelflags, &origin, &angles, &speed, &angle, &dir); if (type != ET_BREAKABLE && type != ET_DOOR && type != ET_ROTATING && type != ET_DOOR_SLIDING && type != ET_TRIGGER_RESCUE && type != ET_TRIGGER_NEXTMAP) Com_Error(ERR_DROP, "Invalid le announced via EV_ADD_BRUSH_MODEL type: %i\n", type); else if (modelnum1 > MAX_MODELS || modelnum1 < 1) Com_Error(ERR_DROP, "Invalid le modelnum1 announced via EV_ADD_BRUSH_MODEL\n"); /* check if the ent is already visible */ le = LE_Get(entnum); if (le) Com_Error(ERR_DROP, "le announced a second time - le for entnum %i (type: %i) already exists (via EV_ADD_BRUSH_MODEL)\n", entnum, type); le = LE_Add(entnum); assert(le); le->rotationSpeed = speed / 100.0f; le->slidingSpeed = speed; le->angle = angle; le->dir = dir; le->type = type; le->modelnum1 = modelnum1; le->levelflags = levelflags; le->addFunc = LE_BrushModelAction; LE_SetThink(le, LET_BrushModel); /* The origin and angles are REQUIRED for doors to work! */ VectorCopy(origin, le->origin); /* store the initial position - needed for sliding doors */ VectorCopy(le->origin, le->oldOrigin); VectorCopy(angles, le->angles); Com_sprintf(le->inlineModelName, sizeof(le->inlineModelName), "*%i", le->modelnum1); model = LE_GetClipModel(le); le->model1 = R_FindModel(le->inlineModelName); if (!le->model1) Com_Error(ERR_DROP, "CL_AddBrushModel: Could not register inline model %i", le->modelnum1); /* Transfer model mins and maxs to entity */ VectorCopy(model->mins, le->mins); VectorCopy(model->maxs, le->maxs); VectorSubtract(le->maxs, le->mins, le->size); VecToPos(le->origin, le->pos); /* to allow tracing against this le */ if (!LE_IsNotSolid(le)) { /* This is to help the entity collision code out */ /* Copy entity origin and angles to model*/ CM_SetInlineModelOrientation(cl.mapTiles, le->inlineModelName, le->origin, le->angles); le->contents = CONTENTS_SOLID; CL_RecalcRouting(le); } }
/** * @brief Called whenever an entity disappears from view * @sa CL_EntAppear */ void CL_EntPerish (const eventRegister_t* self, dbuffer* msg) { int entnum; int type; NET_ReadFormat(msg, self->formatString, &entnum, &type); le_t* le = LE_Get(entnum); if (!le) LE_NotFoundWithTypeError(entnum, type); le_t* actor = nullptr; switch (le->type) { case ET_ITEM: cls.i.emptyContainer(&le->inv, CID_FLOOR); /* search owners (there can be many, some of them dead) */ while ((actor = LE_GetNextInUse(actor))) { if ((actor->type == ET_ACTOR || actor->type == ET_ACTOR2x2) && VectorCompare(actor->pos, le->pos)) { Com_DPrintf(DEBUG_CLIENT, "CL_EntPerish: le of type ET_ITEM hidden\n"); actor->resetFloor(); } } break; case ET_ACTOR: case ET_ACTOR2x2: if (!cls.isOurRound() && le->team != TEAM_CIVILIAN) LE_CenterView(le); cls.i.destroyInventory(&le->inv); if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } /* Clear anim data to prevent actor "jumping" to new animation when it reappears, or worse animation issues. */ OBJZERO(le->as); break; #ifdef DEBUG case ET_ACTORHIDDEN: Com_DPrintf(DEBUG_CLIENT, "CL_EntPerish: It should not happen that we perish a hidden actor\n"); return; #endif case ET_PARTICLE: if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } else { Com_Printf("CL_EntPerish: Particle is nullptr for entnum %i!\n", entnum); } break; case ET_BREAKABLE: case ET_DOOR: case ET_DOOR_SLIDING: break; default: break; } le->flags |= LE_INVISIBLE; /* decrease the count of spotted aliens (also stunned) */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); Cvar_SetValue("mn_numaliensspotted", cl.numEnemiesSpotted); }
/** * @brief Calculates the time the event should get executed. If two events return the same time, * they are going to be executed in the order the were parsed. * @param[in] eType The event type * @param[in,out] msg The message buffer that can be modified to get the event time * @param[in] dt Delta time in msec since the last event was parsed */ int CL_GetEventTime (const event_t eType, struct dbuffer *msg, const int dt) { const eventRegister_t *eventData = CL_GetEvent(eType); #ifdef OLDEVENTTIME /* the time the event should be executed. This value is used to sort the * event chain to determine which event must be executed at first. This * value also ensures, that the events are executed in the correct * order. E.g. @c impactTime is used to delay some events in case the * projectile needs some time to reach its target. */ int eventTime; if (eType == EV_RESET) { parsedDeath = qfalse; nextTime = 0; shootTime = 0; impactTime = 0; } else if (eType == EV_ACTOR_DIE) parsedDeath = qtrue; /* get event time */ if (nextTime < cl.time) nextTime = cl.time; if (impactTime < cl.time) impactTime = cl.time; if (eType == EV_ACTOR_DIE || eType == EV_MODEL_EXPLODE) eventTime = impactTime; else if (eType == EV_ACTOR_SHOOT || eType == EV_ACTOR_SHOOT_HIDDEN) eventTime = shootTime; else if (eType == EV_RESULTS) eventTime = nextTime + 1400; else eventTime = nextTime; if (eType == EV_ENT_APPEAR || eType == EV_INV_ADD || eType == EV_PARTICLE_APPEAR || eType == EV_PARTICLE_SPAWN) { if (parsedDeath) { /* drop items after death (caused by impact) */ eventTime = impactTime + 400; /* EV_INV_ADD messages are the last events sent after a death */ if (eType == EV_INV_ADD) parsedDeath = qfalse; } else if (impactTime > cl.time) { /* item thrown on the ground */ eventTime = impactTime + 75; } } /* calculate time interval before the next event */ switch (eType) { case EV_ACTOR_APPEAR: if (cl.actTeam != cls.team) nextTime += 600; break; case EV_INV_RELOAD: /* let the reload sound play */ nextTime += 600; break; case EV_ACTOR_START_SHOOT: nextTime += 300; shootTime = nextTime; break; case EV_ACTOR_SHOOT_HIDDEN: { int first; int objIdx; const objDef_t *obj; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fireDefIndex; NET_ReadFormat(msg, eventData->formatString, &first, &objIdx, &weapFdsIdx, &fireDefIndex); obj = INVSH_GetItemByIDX(objIdx); if (first) { nextTime += 500; impactTime = shootTime = nextTime; } else { const fireDef_t *fd = FIRESH_GetFiredef(obj, weapFdsIdx, fireDefIndex); /* impact right away - we don't see it at all * bouncing is not needed here, too (we still don't see it) */ impactTime = shootTime; nextTime = shootTime + 1400; if (fd->delayBetweenShots > 0.0) shootTime += 1000 / fd->delayBetweenShots; } parsedDeath = qfalse; } break; case EV_ACTOR_MOVE: { le_t *le; int number, i; int time = 0; int pathLength; byte crouchingState; pos3_t pos, oldPos; number = NET_ReadShort(msg); /* get le */ le = LE_Get(number); if (!le) LE_NotFoundError(number); pathLength = NET_ReadByte(msg); /* Also skip the final position */ NET_ReadByte(msg); NET_ReadByte(msg); NET_ReadByte(msg); VectorCopy(le->pos, pos); crouchingState = LE_IsCrouched(le) ? 1 : 0; for (i = 0; i < pathLength; i++) { const dvec_t dvec = NET_ReadShort(msg); const byte dir = getDVdir(dvec); VectorCopy(pos, oldPos); PosAddDV(pos, crouchingState, dvec); time += LE_ActorGetStepTime(le, pos, oldPos, dir, NET_ReadShort(msg)); NET_ReadShort(msg); } nextTime += time + 400; } break; case EV_ACTOR_SHOOT: { const fireDef_t *fd; int flags, dummy; int objIdx, surfaceFlags; objDef_t *obj; int weap_fds_idx, fd_idx; shoot_types_t shootType; vec3_t muzzle, impact; /* read data */ NET_ReadFormat(msg, eventData->formatString, &dummy, &dummy, &dummy, &objIdx, &weap_fds_idx, &fd_idx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &dummy); obj = INVSH_GetItemByIDX(objIdx); fd = FIRESH_GetFiredef(obj, weap_fds_idx, fd_idx); if (!(flags & SF_BOUNCED)) { /* shooting */ if (fd->speed > 0.0 && !CL_OutsideMap(impact, UNIT_SIZE * 10)) { impactTime = shootTime + 1000 * VectorDist(muzzle, impact) / fd->speed; } else { impactTime = shootTime; } if (cl.actTeam != cls.team) nextTime = impactTime + 1400; else nextTime = impactTime + 400; if (fd->delayBetweenShots > 0.0) shootTime += 1000 / fd->delayBetweenShots; } else { /* only a bounced shot */ eventTime = impactTime; if (fd->speed > 0.0) { impactTime += 1000 * VectorDist(muzzle, impact) / fd->speed; nextTime = impactTime; } } parsedDeath = qfalse; } break; case EV_ACTOR_THROW: nextTime += NET_ReadShort(msg); impactTime = shootTime = nextTime; parsedDeath = qfalse; break; default: break; } Com_DPrintf(DEBUG_EVENTSYS, "%s => eventTime: %i, nextTime: %i, impactTime: %i, shootTime: %i\n", eventData->name, eventTime, nextTime, impactTime, shootTime); return eventTime; #else if (!eventData->timeCallback) return cl.time; return eventData->timeCallback(eventData, msg, dt); #endif }