/** * @brief Let an entity appear - like an item on the ground that just got visible * @sa EV_ENT_APPEAR * @sa CL_EntPerish * @sa CL_AddEdict */ void CL_EntAppear (const eventRegister_t* self, dbuffer* msg) { int entnum; entity_type_t type; pos3_t pos; NET_ReadFormat(msg, self->formatString, &entnum, &type, &pos); /* check if the ent is already visible */ le_t* le = LE_Get(entnum); if (!le) { le = LE_Add(entnum); } else { Com_DPrintf(DEBUG_CLIENT, "CL_EntAppear: Entity appearing already visible... overwriting the old one\n"); le->inuse = true; } le->type = type; /* the default is invisible - another event will follow which spawns not * only the le, but also the particle. The visibility is set there, too */ if (le->type == ET_PARTICLE) LE_SetInvisible(le); VectorCopy(pos, le->pos); Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin); }
/** * @brief Adds server side edicts to the client for displaying them * @sa EV_ADD_EDICT * @sa CL_EntAppear */ void CL_AddEdict (const eventRegister_t *self, struct dbuffer * msg) { le_t *le; int entnum; entity_type_t type; vec3_t mins, maxs; NET_ReadFormat(msg, self->formatString, &entnum, &type, &mins, &maxs); /* use an offset to ensure that we don't conflict with any other solid edict that is already spawned */ le = LE_Get(entnum + MAX_EDICTS); if (!le) { le = LE_Add(entnum + MAX_EDICTS); } else { Com_DPrintf(DEBUG_CLIENT, "CL_AddEdict: Entity appearing already visible... overwriting the old one\n"); le->inuse = qtrue; } VectorCopy(mins, le->mins); VectorCopy(maxs, le->maxs); le->addFunc = CL_AddEdictFunc; le->type = type; Com_DPrintf(DEBUG_CLIENT, "CL_AddEdict: entnum: %i - type: %i\n", entnum, type); }
/** * @brief Adds an hidden actor to the list of le's * @sa CL_ActorAppear * @note Actor is invisible until CL_ActorAppear (EV_ACTOR_APPEAR) was triggered * @note EV_ACTOR_ADD * @sa G_SendInvisible */ void CL_ActorAdd (const eventRegister_t* self, dbuffer* msg) { /* check if the actor is already visible */ const int entnum = NET_ReadShort(msg); le_t* le = LE_Get(entnum); if (le) { Com_Printf("CL_ActorAdd: Actor with number %i already exists\n", entnum); NET_SkipFormat(msg, self->formatString); return; } le = LE_Add(entnum); /* get the info */ int teamDefID; NET_ReadFormat(msg, self->formatString, &le->team, &teamDefID, &le->gender, &le->pnum, &le->pos, &le->state, &le->fieldSize); if (teamDefID < 0 || teamDefID > csi.numTeamDefs) Com_Printf("CL_ActorAdd: Invalid teamDef index\n"); else le->teamDef = &csi.teamDef[teamDefID]; /*Com_Printf("CL_ActorAdd: Add number: %i\n", entnum);*/ le->type = ET_ACTORHIDDEN; Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin); le->flags = LE_INVISIBLE; }
/** * @brief Adds ambient sounds from misc_sound entities * @sa CL_SpawnParseEntitystring */ void LE_AddAmbientSound (const char* sound, const vec3_t origin, int levelflags, float volume, float attenuation) { if (strstr(sound, "sound/")) sound += 6; int sampleIdx = S_LoadSampleIdx(sound); if (!sampleIdx) return; le_t* le = LE_Add(0); if (!le) { Com_Printf("Could not add ambient sound entity\n"); return; } le->type = ET_SOUND; le->sampleIdx = sampleIdx; VectorCopy(origin, le->origin); LE_SetInvisible(le); le->levelflags = levelflags; le->attenuation = attenuation; if (volume < 0.0f || volume > 1.0f) { le->volume = SND_VOLUME_DEFAULT; Com_Printf("Invalid volume for local entity given - only values between 0.0 and 1.0 are valid\n"); } else { le->volume = volume; } Com_DPrintf(DEBUG_SOUND, "Add ambient sound '%s' with volume %f\n", sound, volume); }
/** * @brief Adds a camera edicts to the client for displaying them * @sa EV_CAMERA_APPEAR */ void CL_CameraAppear (const eventRegister_t *self, dbuffer *msg) { int entnum; int team; int levelflags; int dir; int rotate; vec3_t origin; camera_type_t cameraType; NET_ReadFormat(msg, self->formatString, &entnum, &origin, &team, &dir, &cameraType, &levelflags, &rotate); le_t *le = LE_Get(entnum); if (!le) { le = LE_Add(entnum); } else { le->inuse = true; } VectorCopy(origin, le->origin); le->type = ET_CAMERA; le->team = team; le->angles[YAW] = directionAngles[le->angle]; le->flags |= LE_CHECK_LEVELFLAGS; le->levelflags = levelflags; le->model1 = R_FindModel(va("objects/cameras/camera%i", cameraType)); if (rotate) { const char *rotateAnim = "rotate"; R_AnimChange(&le->as, le->model1, rotateAnim); } Com_DPrintf(DEBUG_CLIENT, "CL_CameraAppear: entnum: %i\n", entnum); }
/** * @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); }
/** * @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); } }
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); }