/**
 * @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);
}
Exemple #2
0
/**
 * @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;
}
Exemple #4
0
/**
 * @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);
}
Exemple #6
0
/**
 * @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);
	}
}
Exemple #9
0
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);
}