Пример #1
0
/**
 * @todo Use this function to enable footsteps over network
 * @note Only play the water sounds if actor is not in STATE_CROUCHED mode
 * we can assume, that moving slower and more carefully would also not produce
 * the water sounds
 */
void G_PhysicsStep (edict_t *ent)
{
	/**
	 * @todo don't play foot step sounds for flying units.
	 */
	if (ent->moveinfo.currentStep < ent->moveinfo.steps) {
		const int contentFlags = ent->contentFlags;
		const vismask_t visflags = ent->moveinfo.visflags[ent->moveinfo.currentStep];
		/* Send the sound effect to everyone how's not seeing the actor */
		if (!G_IsCrouched(ent)) {
			if (contentFlags & CONTENTS_WATER) {
				if (ent->moveinfo.contentFlags[ent->moveinfo.currentStep] & CONTENTS_WATER) {
					/* looks like we already are in the water */
					/* send water moving sound */
					G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_under");
				} else {
					/* send water entering sound */
					G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_in");
				}
			} else if (ent->contentFlags & CONTENTS_WATER) {
				/* send water leaving sound */
				G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_out");
			} else {
				trace_t trace;
				vec3_t from, to;

				VectorCopy(ent->origin, to);
				VectorCopy(ent->origin, from);
				/* we should really hit the ground with this */
				to[2] -= UNIT_HEIGHT;

				trace = G_Trace(from, to, NULL, MASK_SOLID);
				if (trace.surface) {
					const char *snd = gi.GetFootstepSound(trace.surface->name);
					if (snd)
						G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, snd);
				}
			}
		}

		/* and now save the new contents */
		ent->contentFlags = ent->moveinfo.contentFlags[ent->moveinfo.currentStep];
		ent->moveinfo.currentStep++;

		/* Immediately re-think */
		ent->nextthink = (level.framenum + 3) * SERVER_FRAME_SECONDS;
	} else {
		ent->moveinfo.currentStep = 0;
		ent->moveinfo.steps = 0;
		ent->think = NULL;
	}
}
Пример #2
0
static bool Destroy_Breakable (edict_t *self)
{
	vec3_t origin;
	const char *model = self->model;

	VectorCenterFromMinsMaxs(self->absmin, self->absmax, origin);

	/* the HP value is used to decide whether this was a triggered call or a
	 * call during a fight - a triggered call will be handled differently in
	 * terms of timing and the related particle effects in the client code */
	if (self->HP == 0)
		G_EventModelExplodeTriggered(self);
	else
		G_EventModelExplode(self);

	if (self->particle)
		G_SpawnParticle(origin, self->spawnflags, self->particle);

	switch (self->material) {
	case MAT_GLASS:
		G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakglass+");
		break;
	case MAT_METAL:
		G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakmetal+");
		break;
	case MAT_ELECTRICAL:
		G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakelectric+");
		break;
	case MAT_WOOD:
		G_EventSpawnSound(PM_ALL, false, self, origin, "misc/breakwood+");
		break;
	case MAT_MAX:
		break;
	}

	G_TouchEdicts(self, 10.0f);

	/* destroy the door trigger */
	if (self->child)
		G_FreeEdict(self->child);

	/* now we can destroy the edict completely */
	G_FreeEdict(self);

	AABB oldAABB(vec3_origin, vec3_origin);
	gi.GetInlineModelAABB(model, oldAABB);
	GridBox rerouteOldBox(oldAABB);
	G_RecalcRouting(model, rerouteOldBox);

	return true;
}
Пример #3
0
/**
 * @brief Hurt trigger
 * @sa SP_trigger_hurt
 */
bool Touch_HurtTrigger (Edict* self, Edict* activator)
{
	/* Dead actors should really not be able to trigger this - they can't be hurt anymore anyway */
	if (!G_IsLivingActor(activator))
		return false;

	/* If no damage is dealt don't count it as triggered */
	const int damage = G_ApplyProtection(activator, self->dmgtype, self->dmg);
	if (damage == 0)
		return false;

	const bool stunEl = (self->dmgtype == gi.csi->damStunElectro);
	const bool stunGas = (self->dmgtype == gi.csi->damStunGas);
	const bool shock = (self->dmgtype == gi.csi->damShock);
	const bool isRobot = activator->chr.teamDef->robot;
	Actor* actor = makeActor(activator);

	if (stunEl || (stunGas && !isRobot)) {
		actor->addStun(damage);
	} else if (shock) {
		/** @todo Handle dazed via trigger_hurt */
	} else {
		G_TakeDamage(actor, damage);
	}
	/* Play hurt sound unless this is shock damage -- it doesn't do anything
	 * because we don't actually handle it above yet */
	if (!shock) {
		const teamDef_t* teamDef = activator->chr.teamDef;
		const int gender = activator->chr.gender;
		const char* sound = teamDef->getActorSound(gender, SND_HURT);
		G_EventSpawnSound(G_PlayerToPM(activator->getPlayer()), *activator, nullptr, sound);
	}

	G_CheckDeathOrKnockout(actor, nullptr, nullptr, damage);

	return true;
}
Пример #4
0
/**
 * @brief Opens/closes a door
 * @note Use function for func_door
 * @todo Check if the door can be opened or closed - there should not be
 * anything in the way (e.g. an actor)
 */
static bool Door_Use (edict_t *door, edict_t *activator)
{
    if (door->doorState == STATE_CLOSED) {
        door->doorState = STATE_OPENED;

        /* change rotation/origin and relink */
        if (door->type == ET_DOOR) {
            if (door->dir & DOOR_OPEN_REVERSE)
                door->angles[door->dir & 3] -= DOOR_ROTATION_ANGLE;
            else
                door->angles[door->dir & 3] += DOOR_ROTATION_ANGLE;
        } else if (door->type == ET_DOOR_SLIDING) {
            Door_SlidingUse(door);
        }
        gi.LinkEdict(door);

        /* maybe the server called this because the door starts opened */
        if (G_MatchIsRunning()) {
            /* let everybody know, that the door opens */
            G_EventDoorOpen(door);
            if (door->noise[0] != '\0')
                G_EventSpawnSound(PM_ALL, false, door, door->origin, door->noise);
        }
    } else if (door->doorState == STATE_OPENED) {
        door->doorState = STATE_CLOSED;

        /* change rotation and relink */
        if (door->type == ET_DOOR) {
            if (door->dir & DOOR_OPEN_REVERSE)
                door->angles[door->dir & 3] += DOOR_ROTATION_ANGLE;
            else
                door->angles[door->dir & 3] -= DOOR_ROTATION_ANGLE;
        } else if (door->type == ET_DOOR_SLIDING) {
            Door_SlidingUse(door);
        }
        gi.LinkEdict(door);

        /* closed is the standard, opened is handled above - we need an active
         * team here already */
        if (G_MatchIsRunning()) {
            /* let everybody know, that the door closes */
            G_EventDoorClose(door);
            if (door->noise[0] != '\0')
                G_EventSpawnSound(PM_ALL, false, door, door->origin, door->noise);
        }
    } else
        return false;

    /* Update model orientation */
    gi.SetInlineModelOrientation(door->model, door->origin, door->angles);
    Com_DPrintf(DEBUG_GAME, "Server processed door movement.\n");

    /* Update path finding table */
    G_RecalcRouting(door->model);

    if (activator && G_IsLivingActor(activator)) {
        /* Check if the player appears/perishes, seen from other teams. */
        G_CheckVis(activator, true);

        /* Calc new vis for the activator. */
        G_CheckVisTeamAll(activator->team, false, activator);
    }

    return true;
}
Пример #5
0
/**
 * @brief Opens/closes a door
 * @note Use function for func_door
 * @todo Check if the door can be opened or closed - there should not be
 * anything in the way (e.g. an actor)
 */
static bool Door_Use (edict_t *door, edict_t *activator)
{
	int opening = 1;

	if (door->doorState == STATE_CLOSED) {
		door->doorState = STATE_OPENED;
		opening = 1;
	} else if (door->doorState == STATE_OPENED) {
		door->doorState = STATE_CLOSED;
		opening = -1;
	} else
		return false;

	/* remember the old location */
	AABB oldAABB;
	gi.GetInlineModelAABB(door->model, oldAABB);
	GridBox rerouteOldBox(oldAABB);

	/* change rotation and relink */
	if (door->type == ET_DOOR) {
		if (door->dir & DOOR_OPEN_REVERSE)
			opening *= -1;
		door->angles[door->dir & 3] += DOOR_ROTATION_ANGLE * opening;
	} else if (door->type == ET_DOOR_SLIDING) {
		Door_SlidingUse(door);
	}
	gi.LinkEdict(door);

	/* maybe the server called this because the door starts opened */
	if (G_MatchIsRunning()) {
		/* let everybody know, that the door moves */
		if (door->doorState == STATE_OPENED)
			G_EventDoorOpen(door);
		else
			G_EventDoorClose(door);
		if (door->noise[0] != '\0') {
			const playermask_t playerMask = G_GetClosePlayerMask(door->origin, UNIT_SIZE * 10);
			G_EventSpawnSound(playerMask, false, door, door->origin, door->noise);
		}
	}

	/* Update model orientation */
	gi.SetInlineModelOrientation(door->model, door->origin, door->angles);
	AABB newAabb;
	gi.GetInlineModelAABB(door->model, newAabb);
	GridBox rerouteNewBox(newAabb);
	Com_DPrintf(DEBUG_GAME, "Server processed door movement.\n");

	/* Update path finding table for the new location of the model */
	G_RecalcRouting(door->model, rerouteOldBox);							/* Update path finding table */
	G_RecalcRouting(door->model, rerouteNewBox);

	if (activator && G_IsLivingActor(activator)) {
		/* Check if the player appears/perishes, seen from other teams. */
		G_CheckVis(activator);

		/* Calc new vis for the activator. */
		G_CheckVisTeamAll(activator->team, 0, activator);
	}

	return true;
}
Пример #6
0
/**
 * @brief Deals splash damage to a target and its surroundings.
 * @param[in] ent The shooting actor
 * @param[in] fd The fire definition that defines what type of damage is dealt and how big the splash radius is.
 * @param[in] impact The impact vector where the grenade is exploding
 * @param[in,out] mock pseudo shooting - only for calculating mock values - nullptr for real shots
 * @param[in] tr The trace where the grenade hits something (or not)
 */
static void G_SplashDamage (Actor* ent, const fireDef_t* fd, vec3_t impact, shot_mock_t* mock, const trace_t* tr)
{
	assert(fd->splrad > 0.0f);

	const bool shock = (fd->obj->dmgtype == gi.csi->damShock);

	Edict* check = nullptr;
	while ((check = G_EdictsGetNextInUse(check))) {
		/* If we use a blinding weapon we skip the target if it's looking
		 * away from the impact location. */
		if (shock && !G_FrustumVis(check, impact))
			continue;

		const bool isActor = G_IsLivingActor(check);
		vec3_t center;
		if (G_IsBrushModel(check) && G_IsBreakable(check))
			check->absBox.getCenter(center);
		else if (isActor || G_IsBreakable(check))
			VectorCopy(check->origin, center);
		else
			continue;

		/* check for distance */
		float dist = VectorDist(impact, center);
		dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0.0f;
		if (dist > fd->splrad)
			continue;

		if (fd->irgoggles) {
			if (isActor) {
				/* check whether this actor (check) is in the field of view of the 'shooter' (ent) */
				if (G_FrustumVis(ent, check->origin)) {
					if (!mock) {
						vec3_t eyeEnt;
						G_ActorGetEyeVector(ent, eyeEnt);
						if (!G_SmokeVis(eyeEnt, check)) {
							const unsigned int playerMask = G_TeamToPM(ent->getTeam()) ^ G_VisToPM(check->visflags);
							G_AppearPerishEvent(playerMask, true, *check, ent);
							G_VisFlagsAdd(*check, G_PMToVis(playerMask));
						}
					}
				}
			}
			continue;
		}

		/* check for walls */
		if (isActor && G_TestLine(impact, check->origin))
			continue;

		/* do damage */
		const int damage = shock ? 0 : fd->spldmg[0] * (1.0f - dist / fd->splrad);

		if (mock)
			mock->allow_self = true;
		/* Send hurt sounds for actors, but only if they'll recieve damage from this attack */
		if (G_Damage(check, fd, damage, ent, mock, nullptr) && isActor
				&& (G_ApplyProtection(check, fd->dmgweight,  damage) > 0) && !shock) {
			const teamDef_t* teamDef = check->chr.teamDef;
			const int gender = check->chr.gender;
			const char* sound = teamDef->getActorSound(gender, SND_HURT);
			G_EventSpawnSound(G_VisToPM(check->visflags), *check, nullptr, sound);
		}
		if (mock)
			mock->allow_self = false;
	}

	/** @todo splash might also hit other surfaces and the trace doesn't handle that */
	if (tr && G_FireAffectedSurface(tr->surface, fd)) {
		/* move a little away from the impact vector */
		VectorMA(impact, 1, tr->plane.normal, impact);
		G_SpawnParticle(impact, tr->contentFlags >> 8, "burning");
	}