TEST_F(ParticleTest, FloodParticles) { for (int i = 0; i < MAX_PTLS; i++) { ASSERT_TRUE(nullptr != CL_ParticleSpawn("fire", 0xFF, vec3_origin)); } CL_ParticleRun(); ASSERT_TRUE(nullptr == CL_ParticleSpawn("fire", 0xFF, vec3_origin)); }
/** * @brief Called every frame and checks whether a timed particle should be spawned */ static void CL_ParticleRunTimed (void) { int i; const size_t length = lengthof(timedParticles); for (i = 0; i < length; i++) { timedParticle_t *tp = &timedParticles[i]; if (!tp->parent || !tp->parent->inuse) continue; if (tp->n >= tp->max) continue; if (CL_Milliseconds() - tp->lastTime < tp->dt) continue; { ptl_t *p; if (!tp->n) { /* first spawn? - then copy the parent values. We have to * do this here and now earlier because projectile particles * get these values set after spawn. */ VectorCopy(tp->parent->s, tp->s); VectorCopy(tp->parent->v, tp->v); VectorCopy(tp->parent->a, tp->a); } tp->n++; tp->lastTime = CL_Milliseconds(); p = CL_ParticleSpawn(tp->ptl, tp->levelFlags, tp->s, tp->v, tp->a); if (p && tp->children) { p->next = tp->parent->children; p->parent = tp->parent; tp->parent->children = p; } } } }
static void CL_RunMapParticles (void) { mapParticle_t *mp; ptl_t *ptl; int i; for (i = 0, mp = mapParticles; i < cl.numMapParticles; i++, mp++) if (mp->nextTime && cl.time >= mp->nextTime) { /* spawn a new particle */ ptl = CL_ParticleSpawn(mp->ptl, mp->levelflags, mp->origin); if (!ptl) { Com_Printf(S_COLOR_YELLOW "Could not spawn particle '%s'\n", mp->ptl); mp->nextTime = 0; continue; } /* init the particle */ CL_ParseMapParticle(ptl, mp->info, false); CL_ParticleFunction(ptl, ptl->ctrl->init); CL_ParseMapParticle(ptl, mp->info, true); /* prepare next spawning */ if (Vector2NotEmpty(mp->wait)) mp->nextTime += mp->wait[0] + mp->wait[1] * frand(); else mp->nextTime = 0; } }
/** * @note Think function */ static void LET_Projectile (le_t* le) { if (cl.time >= le->endTime) { vec3_t impact; VectorCopy(le->origin, impact); CL_ParticleFree(le->ptl); /* don't run the think function again */ le->inuse = false; if (Q_strvalid(le->ref1)) { VectorCopy(le->ptl->s, impact); le->ptl = CL_ParticleSpawn(le->ref1, 0, impact, bytedirs[le->angle]); VecToAngles(bytedirs[le->state], le->ptl->angles); } if (Q_strvalid(le->ref2)) { S_LoadAndPlaySample(le->ref2, impact, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (le->ref3) { /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */ /** @todo Special particles for stun attack (mind you that there is * electrical and gas/chemical stunning)? */ if (le->fd->obj->dmgtype != csi.damStunGas) LE_ActorBodyHit(le->ref3, impact, le->angle); CL_ActorPlaySound(le->ref3, SND_HURT); } } else if (CL_OutsideMap(le->ptl->s, UNIT_SIZE * 10)) { le->endTime = cl.time; CL_ParticleFree(le->ptl); /* don't run the think function again */ le->inuse = false; } }
/** * @brief Spawns particle effects for a hit actor. * @param[in] le The actor to spawn the particles for. * @param[in] impact The impact location (where the particles are spawned). * @param[in] normal The index of the normal vector of the particles (think: impact angle). */ static void LE_ActorBodyHit (const le_t* le, const vec3_t impact, int normal) { if (le->teamDef) { /* Spawn "hit_particle" if defined in teamDef. */ if (le->teamDef->hitParticle[0] != '\0') CL_ParticleSpawn(le->teamDef->hitParticle, 0, impact, bytedirs[normal]); } }
/** * @brief Let a particle spawn for the client * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg holds the network data * @sa CL_ParticleSpawn * @sa EV_PARTICLE_SPAWN */ void CL_ParticleSpawnEvent (const eventRegister_t* self, dbuffer* msg) { char particle[MAX_VAR]; int levelflags; vec3_t s, v, a; /* read data */ NET_ReadFormat(msg, self->formatString, &levelflags, &s, &v, &a, particle, sizeof(particle)); CL_ParticleSpawn(particle, levelflags, s, v, a); }
/** * @brief Spawns a debug marker particle in the world */ static void PTL_DebugSpawnMarker_f (void) { vec3_t worldOrigin; if (Cmd_Argc() < 4) { Com_Printf("Usage: %s <x> <y> <z>\n", Cmd_Argv(0)); return; } worldOrigin[0] = atof(Cmd_Argv(1)); worldOrigin[1] = atof(Cmd_Argv(2)); worldOrigin[2] = atof(Cmd_Argv(3)); CL_ParticleSpawn("debug_marker", 0, worldOrigin); }
/** * @brief draw a simple 'spotted' line from a spotter to the spotted */ static void CL_DrawLineOfSight (const le_t *watcher, const le_t *target) { ptl_t *ptl; vec3_t eyes; if (!watcher || !target) return; /* start is the watchers origin */ VectorCopy(watcher->origin, eyes); if (LE_IsCrouched(watcher)) eyes[2] += EYE_HT_CROUCH; else eyes[2] += EYE_HT_STAND; ptl = CL_ParticleSpawn("fadeTracer", 0, eyes, target->origin); if (LE_IsCivilian(target)) VectorSet(ptl->color, 0.2, 0.2, 1); }
/** * @param[in] fd The grenade fire definition * @param[in] flags bitmask: SF_BODY, SF_IMPACT, SF_BOUNCING, SF_BOUNCED * @param[in] muzzle starting/location vector * @param[in] v0 velocity vector * @param[in] dt delta seconds * @param[in] leVictim The actor the grenade is thrown at (not yet supported) */ void LE_AddGrenade (const fireDef_t* fd, int flags, const vec3_t muzzle, const vec3_t v0, int dt, le_t* leVictim) { /* add le */ le_t* le = LE_Add(0); if (!le) return; LE_SetInvisible(le); /* bind particle */ vec3_t accel; VectorSet(accel, 0, 0, -GRAVITY); le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle, v0, accel); if (!le->ptl) { le->inuse = false; return; } /* particle properties */ VectorSet(le->ptl->angles, 360 * crand(), 360 * crand(), 360 * crand()); VectorSet(le->ptl->omega, 500 * crand(), 500 * crand(), 500 * crand()); /* think function */ if (flags & SF_BODY) { le->ref1 = fd->hitBody; le->ref2 = fd->hitBodySound; le->ref3 = leVictim; } else if ((flags & SF_IMPACT) || (fd->splrad && !fd->bounce)) { le->ref1 = fd->impact; le->ref2 = fd->impactSound; } else { le->ref1 = nullptr; if (flags & SF_BOUNCING) le->ref2 = fd->bounceSound; } le->endTime = cl.time + dt; /* direction - bytedirs index (0,0,1) */ le->angle = 5; le->fd = fd; LE_SetThink(le, LET_Projectile); LE_ExecuteThink(le); }
/** * @brief Let a particle appear for the client * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg holds the network data * @sa CL_ParticleSpawn * @sa EV_PARTICLE_APPEAR */ void CL_ParticleAppear (const eventRegister_t* self, dbuffer* msg) { char particle[MAX_VAR]; int entnum, levelflags; vec3_t origin; /* read data */ NET_ReadFormat(msg, self->formatString, &entnum, &levelflags, origin, particle, sizeof(particle)); le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); /* particles don't have a model to add to the scene - we mark them as invisible and * only render the particle */ LE_SetInvisible(le); le->levelflags = levelflags; le->particleID = Mem_PoolStrDup(particle, cl_genericPool, 0); le->ptl = CL_ParticleSpawn(le->particleID, le->levelflags, origin); if (!le->ptl) Com_Printf("Could not spawn particle: '%s'\n", le->particleID); }
/** * @brief Plays step sounds and draw particles for different terrain types * @param[in] le The local entity to play the sound and draw the particle for * @param[in] textureName The name of the texture the actor is standing on * @sa LET_PathMove */ static void LE_PlaySoundFileAndParticleForSurface (le_t* le, const char* textureName) { const terrainType_t* t = Com_GetTerrainType(textureName); if (!t) return; /* origin might not be up-to-date here - but pos should be */ vec3_t origin; PosToVec(le->pos, origin); /** @todo use the Grid_Fall method (ACTOR_SIZE_NORMAL) to ensure, that the particle is * drawn at the ground (if needed - maybe the origin is already ground aligned)*/ if (t->particle) { /* check whether actor is visible */ if (!LE_IsStunned(le) && LE_IsLivingAndVisibleActor(le)) CL_ParticleSpawn(t->particle, 0, origin); } if (t->footstepSound) { Com_DPrintf(DEBUG_SOUND, "LE_PlaySoundFileAndParticleForSurface: volume %.2f\n", t->footstepVolume); S_LoadAndPlaySample(t->footstepSound, origin, SOUND_ATTN_STATIC, t->footstepVolume); } }
static void CL_ParticleFunction (ptl_t *p, ptlCmd_t *cmd) { int stackIdx; ptrdiff_t e; int type; int i, j, n; void *cmdData; float arg; ptl_t *pnew; /* test for null cmd */ if (!cmd) return; /* run until finding PC_END */ for (stackIdx = 0, e = 0; cmd->cmd != PC_END; cmd++) { if (cmd->ref > RSTACK) cmdData = CL_ParticleCommandGetDataLocation(p, cmd); else { if (!stackIdx) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); /* pop an element off the stack */ e = (byte *) stackPtr[--stackIdx] - cmdStack; i = RSTACK - cmd->ref; if (!i) { /* normal stack reference */ cmdData = stackPtr[stackIdx]; cmd->type = stackType[stackIdx]; } else { /* stack reference to element of vector */ if ((1 << stackType[stackIdx]) & V_VECS) { cmd->type = V_FLOAT; cmdData = (float *) stackPtr[stackIdx] + (i - 1); } else { Com_Error(ERR_DROP, "CL_ParticleFunction: can't get components of a non-vector type (particle %s)", p->ctrl->name); } } } switch (cmd->cmd) { case PC_PUSH: /* check for stack overflow */ if (stackIdx >= MAX_STACK_DEPTH) Com_Error(ERR_DROP, "CL_ParticleFunction: stack overflow"); /* store the value in the stack */ stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; e += Com_SetValue(stackPtr[stackIdx++], cmdData, (valueTypes_t)cmd->type, 0, 0); break; case PC_POP: case PC_KPOP: /* check for stack underflow */ if (stackIdx == 0) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); /* get pics and models */ if (offsetof(ptl_t, pic) == -cmd->ref) { if (stackType[--stackIdx] != V_STRING) Com_Error(ERR_DROP, "Bad type '%s' for pic (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); p->pic = CL_ParticleGetArt((char *) stackPtr[stackIdx], p->frame, ART_PIC); e = (byte *) stackPtr[stackIdx] - cmdStack; break; } if (offsetof(ptl_t, model) == -cmd->ref) { if (stackType[--stackIdx] != V_STRING) Com_Error(ERR_DROP, "Bad type '%s' for model (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); p->model = CL_ParticleGetArt((char *) stackPtr[stackIdx], p->frame, ART_MODEL); e = (byte *) stackPtr[stackIdx] - cmdStack; break; } if (offsetof(ptl_t, program) == -cmd->ref) { if (stackType[--stackIdx] != V_STRING) Com_Error(ERR_DROP, "Bad type '%s' for program (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); p->program = R_LoadProgram((char *) stackPtr[stackIdx], R_InitParticleProgram, R_UseParticleProgram); if (p->program) p->program->userdata = p; e = (byte *) stackPtr[stackIdx] - cmdStack; break; } /* get different data */ if (cmd->cmd == PC_POP) e -= Com_SetValue(cmdData, stackPtr[--stackIdx], (valueTypes_t)cmd->type, 0, 0); else Com_SetValue(cmdData, stackPtr[stackIdx - 1], (valueTypes_t)cmd->type, 0, 0); break; case PC_ADD: case PC_SUB: /* check for stack underflow */ if (stackIdx == 0) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); type = stackType[stackIdx - 1]; if (!((1 << type) & V_VECS)) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for add (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); /* float based vector addition */ if (type != cmd->type) Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for add/sub (particle %s)", p->ctrl->name); n = type - V_FLOAT + 1; for (i = 0; i < n; i++) { if (cmd->cmd == PC_SUB) arg = -(*((float *) cmdData + i)); else arg = *((float *) cmdData + i); *((float *) stackPtr[stackIdx - 1] + i) += arg; } break; case PC_MUL: case PC_DIV: /* check for stack underflow */ if (stackIdx == 0) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); type = stackType[stackIdx - 1]; if (!((1 << type) & V_VECS)) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for add (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); n = type - V_FLOAT + 1; if (type > V_FLOAT && cmd->type > V_FLOAT) { /* component wise multiplication */ if (type != cmd->type) Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for mul/div (particle %s)", p->ctrl->name); for (i = 0; i < n; i++) { if (cmd->cmd == PC_DIV) arg = 1.0 / (*((float *) cmdData + i)); else arg = *((float *) cmdData + i); *((float *) stackPtr[stackIdx - 1] + i) *= arg; } break; } if (cmd->type > V_FLOAT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad vector dimensions for mul/div (particle %s)", p->ctrl->name); /* scalar multiplication with scalar in second argument */ if (cmd->cmd == PC_DIV) arg = 1.0 / (*(float *) cmdData); else arg = *(float *) cmdData; for (i = 0; i < n; i++) *((float *) stackPtr[stackIdx - 1] + i) *= arg; break; case PC_SIN: if (cmd->type != V_FLOAT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for sin (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI)); e += sizeof(float); break; case PC_COS: if (cmd->type != V_FLOAT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for cos (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI)); e += sizeof(float); break; case PC_TAN: if (cmd->type != V_FLOAT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for tan (particle %s)", vt_names[stackType[stackIdx - 1]], p->ctrl->name); stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; *(float *) stackPtr[stackIdx++] = sin(*(float *) cmdData * (2 * M_PI)); e += sizeof(float); break; case PC_RAND: case PC_CRAND: stackPtr[stackIdx] = &cmdStack[e]; stackType[stackIdx] = cmd->type; n = cmd->type - V_FLOAT + 1; if (cmd->cmd == PC_RAND) for (i = 0; i < n; i++) *((float *) stackPtr[stackIdx] + i) = *((float *) cmdData + i) * frand(); else for (i = 0; i < n; i++) *((float *) stackPtr[stackIdx] + i) = *((float *) cmdData + i) * crand(); e += n * sizeof(float); stackIdx++; break; case PC_V2: case PC_V3: case PC_V4: n = cmd->cmd - PC_V2 + 2; j = 0; if (stackIdx < n) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); for (i = 0; i < n; i++) { if (!((1 << stackType[--stackIdx]) & V_VECS)) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' for vector creation (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name); j += stackType[stackIdx] - V_FLOAT + 1; } if (j > 4) Com_Error(ERR_DROP, "CL_ParticleFunction: created vector with dim > 4 (particle %s)", p->ctrl->name); stackType[stackIdx++] = V_FLOAT + j - 1; break; case PC_KILL: CL_ParticleFree(p); return; case PC_SPAWN: pnew = CL_ParticleSpawn((const char *) cmdData, p->levelFlags, p->s, p->v, p->a); if (!pnew) Com_Printf("PC_SPAWN: Could not spawn child particle for '%s' (%s)\n", p->ctrl->name, (const char *) cmdData); break; case PC_TNSPAWN: /* check for stack underflow */ if (stackIdx < 2) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); /* pop elements off the stack */ /* amount of timed particles */ type = stackType[--stackIdx]; if (type != V_INT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for tnspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name); n = *(int *) stackPtr[stackIdx]; /* delta time */ type = stackType[--stackIdx]; if (type != V_INT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for tnspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name); i = *(int *) stackPtr[stackIdx]; /** @todo make the children boolean configurable */ CL_ParticleSpawnTimed((const char *) cmdData, p, true, i, n); e -= 2 * sizeof(int); break; case PC_NSPAWN: /* check for stack underflow */ if (stackIdx == 0) Com_Error(ERR_DROP, "CL_ParticleFunction: stack underflow"); type = stackType[--stackIdx]; if (type != V_INT) Com_Error(ERR_DROP, "CL_ParticleFunction: bad type '%s' int required for nspawn (particle %s)", vt_names[stackType[stackIdx]], p->ctrl->name); n = *(int *) stackPtr[stackIdx]; e -= sizeof(int); for (i = 0; i < n; i++) { pnew = CL_ParticleSpawn((const char *) cmdData, p->levelFlags, p->s, p->v, p->a); if (!pnew) Com_Printf("PC_NSPAWN: Could not spawn child particle for '%s'\n", p->ctrl->name); } break; case PC_CHILD: pnew = CL_ParticleSpawn((const char *)cmdData, p->levelFlags, p->s, p->v, p->a); if (pnew) { pnew->next = p->children; pnew->parent = p; p->children = pnew; } else { Com_Printf("PC_CHILD: Could not spawn child particle for '%s'\n", p->ctrl->name); } break; default: Com_Error(ERR_DROP, "CL_ParticleFunction: unknown cmd type %i", cmd->type); break; } } }
/** * @brief Draw a model from the battlescape entity list * @sa R_GetEntityLists */ void R_DrawAliasModel (entity_t *e) { mAliasModel_t *mod = &e->model->alias; /* the values are sane here already - see R_GetEntityLists */ const image_t *skin = mod->meshes[e->as.mesh].skins[e->skinnum].skin; int i; float g; vec4_t color = {0.8, 0.8, 0.8, 1.0}; mAliasMesh_t *mesh; /* IR goggles override color for entities that are affected */ if ((refdef.rendererFlags & RDF_IRGOGGLES) && (e->flags & RF_IRGOGGLES)) Vector4Set(e->shell, 1.0, 0.3, 0.3, 1.0); if (e->flags & RF_PULSE) { /* and then adding in a pulse */ const float f = 1.0 + sin((refdef.time + (e->model->alias.meshes[0].num_tris)) * 6.0) * 0.33; VectorScale(color, 1.0 + f, color); } g = 0.0; /* find brightest component */ for (i = 0; i < 3; i++) { if (color[i] > g) /* keep it */ g = color[i]; } /* scale it back to 1.0 */ if (g > 1.0) VectorScale(color, 1.0 / g, color); R_Color(color); assert(skin->texnum > 0); R_BindTexture(skin->texnum); R_EnableGlowMap(skin->glowmap); R_UpdateLightList(e); R_EnableModelLights(e->lights, e->numLights, e->inShadow, true); /** @todo this breaks the encapsulation - don't call CL_* functions from within the renderer code */ if (r_debug_lights->integer) { for (i = 0; i < e->numLights && i < r_dynamic_lights->integer; i++) CL_ParticleSpawn("lightTracerDebug", 0, e->transform.matrix + 12, e->lights[i]->origin); } if (skin->normalmap) R_EnableBumpmap(skin->normalmap); if (skin->specularmap) R_EnableSpecularMap(skin->specularmap, true); if (skin->roughnessmap) R_EnableRoughnessMap(skin->roughnessmap, true); glPushMatrix(); glMultMatrixf(e->transform.matrix); if (VectorNotEmpty(e->scale)) glScalef(e->scale[0], e->scale[1], e->scale[2]); mesh = R_DrawAliasModelBuffer(e); if (r_state.specularmap_enabled) R_EnableSpecularMap(NULL, false); if (r_state.roughnessmap_enabled) R_EnableRoughnessMap(NULL, false); R_EnableModelLights(NULL, 0, false, false); R_EnableGlowMap(NULL); if (r_state.active_normalmap) R_EnableBumpmap(NULL); R_DrawMeshShadow(e, mesh); if (mod->num_frames == 1) R_ResetArraysAfterStaticMeshRender(); glPopMatrix(); /* show model bounding box */ if (r_showbox->integer) R_DrawBoundingBox(mod->frames[e->as.frame].mins, mod->frames[e->as.frame].maxs); R_Color(NULL); }
void LE_AddProjectile (const fireDef_t* fd, int flags, const vec3_t muzzle, const vec3_t impact, int normal, le_t* leVictim) { /* add le */ le_t* le = LE_Add(0); if (!le) return; LE_SetInvisible(le); /* bind particle */ le->ptl = CL_ParticleSpawn(fd->projectile, 0, muzzle); if (!le->ptl) { le->inuse = false; return; } /* calculate parameters */ vec3_t delta; VectorSubtract(impact, muzzle, delta); const float dist = VectorLength(delta); VecToAngles(delta, le->ptl->angles); /* direction - bytedirs index */ le->angle = normal; le->fd = fd; /* infinite speed projectile? */ if (!fd->speed) { le->inuse = false; le->ptl->size[0] = dist; VectorMA(muzzle, 0.5, delta, le->ptl->s); if ((flags & (SF_IMPACT | SF_BODY)) || (fd->splrad && !fd->bounce)) { ptl_t* ptl = nullptr; const float* dir = bytedirs[le->angle]; if (flags & SF_BODY) { if (fd->hitBodySound != nullptr) { S_LoadAndPlaySample(fd->hitBodySound, le->origin, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (fd->hitBody != nullptr) ptl = CL_ParticleSpawn(fd->hitBody, 0, impact, dir); /* Spawn blood particles (if defined) if actor(-body) was hit. Even if actor is dead. */ /** @todo Special particles for stun attack (mind you that there is * electrical and gas/chemical stunning)? */ if (leVictim) { if (fd->obj->dmgtype != csi.damStunGas) LE_ActorBodyHit(leVictim, impact, le->angle); if (fd->damage[0] >= 0) CL_ActorPlaySound(leVictim, SND_HURT); } } else { if (fd->impactSound != nullptr) { S_LoadAndPlaySample(fd->impactSound, le->origin, le->fd->impactAttenuation, SND_VOLUME_WEAPONS); } if (fd->impact != nullptr) ptl = CL_ParticleSpawn(fd->impact, 0, impact, dir); } if (ptl) VecToAngles(dir, ptl->angles); } return; } /* particle properties */ VectorScale(delta, fd->speed / dist, le->ptl->v); le->endTime = cl.time + 1000 * dist / fd->speed; /* think function */ if (flags & SF_BODY) { le->ref1 = fd->hitBody; le->ref2 = fd->hitBodySound; le->ref3 = leVictim; } else if ((flags & SF_IMPACT) || (fd->splrad && !fd->bounce)) { le->ref1 = fd->impact; le->ref2 = fd->impactSound; } else { le->ref1 = nullptr; if (flags & SF_BOUNCING) le->ref2 = fd->bounceSound; } LE_SetThink(le, LET_Projectile); LE_ExecuteThink(le); }
/** * @sa CL_ViewRender * @sa CL_AddUGV * @sa CL_AddActor */ void LE_AddToScene (void) { for (int i = 0; i < cl.numLEs; i++) { le_t& le = cl.LEs[i]; if (le.flags & LE_REMOVE_NEXT_FRAME) { le.inuse = false; le.flags &= ~LE_REMOVE_NEXT_FRAME; } if (le.inuse && !LE_IsInvisible(&le)) { if (le.flags & LE_CHECK_LEVELFLAGS) { if (!((1 << cl_worldlevel->integer) & le.levelflags)) continue; } else if (le.flags & LE_ALWAYS_VISIBLE) { /* show them always */ } else if (le.pos[2] > cl_worldlevel->integer) continue; entity_t ent(RF_NONE); ent.alpha = le.alpha; VectorCopy(le.angles, ent.angles); ent.model = le.model1; ent.skinnum = le.bodySkin; ent.lighting = &le.lighting; switch (le.contents) { /* Only breakables do not use their origin; func_doors and func_rotating do!!! * But none of them have animations. */ case CONTENTS_SOLID: case CONTENTS_DETAIL: /* they use mins/maxs */ break; default: /* set entity values */ R_EntitySetOrigin(&ent, le.origin); VectorCopy(le.origin, ent.oldorigin); /* store animation values */ ent.as = le.as; break; } if (LE_IsOriginBrush(&le)) { ent.isOriginBrushModel = true; R_EntitySetOrigin(&ent, le.origin); VectorCopy(le.origin, ent.oldorigin); } if (LE_IsSelected(&le) && le.clientAction != nullptr) { const le_t* action = le.clientAction; if (action->inuse && action->type > ET_NULL && action->type < ET_MAX) LE_AddEdictHighlight(action); } /* call add function */ /* if it returns false, don't draw */ if (le.addFunc) if (!le.addFunc(&le, &ent)) continue; /* add it to the scene */ R_AddEntity(&ent); if (cl_le_debug->integer) CL_ParticleSpawn("cross", 0, le.origin); } } }