Example #1
0
/*
 * @brief Sets the kick value based on recent events such as falling. Firing of
 * weapons may also set the kick value, and we factor that in here as well.
 */
static void G_ClientKickAngles(g_edict_t *ent) {
	int16_t *kick_angles = ent->client->ps.pm_state.kick_angles;

	vec3_t kick;
	UnpackAngles(kick_angles, kick);

	// add in any event-based feedback

	switch (ent->s.event) {
		case EV_CLIENT_LAND:
			kick[PITCH] += 2.5;
			break;
		case EV_CLIENT_FALL:
			kick[PITCH] += 5.0;
			break;
		case EV_CLIENT_FALL_FAR:
			kick[PITCH] += 10.0;
			break;
		default:
			break;
	}

	// and any velocity-based feedback

	vec_t forward = DotProduct(ent->locals.velocity, ent->client->locals.forward);
	kick[PITCH] += forward / 450.0;

	vec_t right = DotProduct(ent->locals.velocity, ent->client->locals.right);
	kick[ROLL] += right / 350.0;

	// now interpolate the kick angles towards neutral over time

	vec_t delta = VectorLength(kick);

	if (!delta) // no kick, we're done
		return;

	// we recover from kick at a rate based on the kick itself

	delta = 0.5 + delta * delta * gi.frame_seconds;

	int32_t i;
	for (i = 0; i < 3; i++) {

		// clear angles smaller than our delta to avoid oscillations
		if (fabs(kick[i]) <= delta) {
			kick[i] = 0.0;
		} else if (kick[i] > 0.0) {
			kick[i] -= delta;
		} else {
			kick[i] += delta;
		}

	}

	PackAngles(kick, kick_angles);
}
Example #2
0
/*
 * @brief Adds view kick in the specified direction to the specified client.
 */
void G_ClientDamageKick(g_entity_t *ent, const vec3_t dir, const vec_t kick) {
	vec3_t old_kick_angles, kick_angles;

	UnpackAngles(ent->client->ps.pm_state.kick_angles, old_kick_angles);

	VectorClear(kick_angles);
	kick_angles[PITCH] = DotProduct(dir, ent->client->locals.forward) * kick * KICK_SCALE;
	kick_angles[ROLL] = DotProduct(dir, ent->client->locals.right) * kick * KICK_SCALE;

	//gi.Print("kicked %s from %s at %1.2f\n", vtos(kick_angles), vtos(dir), kick);
	VectorAdd(old_kick_angles, kick_angles, kick_angles);
	PackAngles(kick_angles, ent->client->ps.pm_state.kick_angles);
}
Example #3
0
/*
 * @brief Run recent movement commands through the player movement code
 * locally, storing the resulting origin and angles so that they may be
 * interpolated to by Cl_UpdateView.
 */
void Cg_PredictMovement(const GList *cmds) {
	pm_move_t pm;

	// copy current state to into the move
	memset(&pm, 0, sizeof(pm));
	pm.s = cgi.client->frame.ps.pm_state;

	pm.ground_entity = cgi.client->predicted_state.ground_entity;

	pm.PointContents = cgi.PointContents;
	pm.Trace = Cg_PredictMovement_Trace;

	pm.Debug = Cg_PredictMovement_Debug;

	const GList *e = cmds;

	// run frames
	while (e) {
		const cl_cmd_t *cmd = (cl_cmd_t *) e->data;

		if (cmd->cmd.msec) {

			pm.cmd = cmd->cmd;
			Pm_Move(&pm);

			// for each movement, check for stair interaction and interpolate
			if (pm.s.flags & PMF_ON_STAIRS) {
				cgi.client->predicted_state.step_time = cmd->time;
				cgi.client->predicted_state.step_interval = 120 * (fabs(pm.step) / 16.0);
				cgi.client->predicted_state.step = pm.step;
			}

			// save for debug checking
			const uint32_t frame = (intptr_t) (cmd - cgi.client->cmds);
			VectorCopy(pm.s.origin, cgi.client->predicted_state.origins[frame]);
		}

		e = e->next;
	}

	// copy results out for rendering
	UnpackVector(pm.s.origin, cgi.client->predicted_state.origin);

	UnpackVector(pm.s.view_offset, cgi.client->predicted_state.view_offset);
	UnpackAngles(pm.cmd.angles, cgi.client->predicted_state.view_angles);

	cgi.client->predicted_state.ground_entity = pm.ground_entity;
}
Example #4
0
/*
 * @brief The angles are typically fetched from input, after factoring in client-side
 * prediction, unless the client is watching a demo or chase camera.
 */
static void Cl_UpdateAngles(const player_state_t *from, const player_state_t *to) {
	vec3_t old_angles, new_angles, angles;

	// start with the predicted angles, or interpolate the server states
	if (Cl_UsePrediction()) {
		VectorCopy(cl.predicted_state.view_angles, r_view.angles);
	} else {
		UnpackAngles(from->pm_state.view_angles, old_angles);
		UnpackAngles(to->pm_state.view_angles, new_angles);

		AngleLerp(old_angles, new_angles, cl.lerp, r_view.angles);
	}

	// add in the kick angles
	UnpackAngles(from->pm_state.kick_angles, old_angles);
	UnpackAngles(to->pm_state.kick_angles, new_angles);

	AngleLerp(old_angles, new_angles, cl.lerp, angles);
	VectorAdd(r_view.angles, angles, r_view.angles);

	// and lastly the delta angles
	UnpackAngles(from->pm_state.delta_angles, old_angles);
	UnpackAngles(to->pm_state.delta_angles, new_angles);

	VectorCopy(new_angles, angles);

	// check for small delta angles, and interpolate them
	if (!VectorCompare(old_angles, new_angles)) {
		int32_t i;

		for (i = 0; i < 3; i++) {
			const vec_t delta = fabs(new_angles[i] - old_angles[i]);
			if (delta > 5.0 && delta < 355.0) {
				break;
			}
		}

		if (i == 3) {
			AngleLerp(old_angles, new_angles, cl.lerp, angles);
		}
	}

	VectorAdd(r_view.angles, angles, r_view.angles);

	if (cl.frame.ps.pm_state.type == PM_DEAD) { // look only on x axis
		r_view.angles[0] = 0.0;
		r_view.angles[2] = 45.0;
	}

	// and finally set the view directional vectors
	AngleVectors(r_view.angles, r_view.forward, r_view.right, r_view.up);
}
Example #5
0
/*
 * @brief The angles are typically fetched from input, after factoring in client-side
 * prediction, unless the client is watching a demo or chase camera.
 */
static void Cl_UpdateAngles(const player_state_t *ps, const player_state_t *ops) {
	vec3_t old_angles, new_angles, angles;

	// start with the predicted angles, or interpolate the server states
	if (Cl_UsePrediction()) {
		VectorCopy(cl.predicted_state.view_angles, r_view.angles);
	} else {
		UnpackAngles(ops->pm_state.view_angles, old_angles);
		UnpackAngles(ps->pm_state.view_angles, new_angles);

		AngleLerp(old_angles, new_angles, cl.lerp, r_view.angles);
	}

	// add in the kick angles
	UnpackAngles(ops->pm_state.kick_angles, old_angles);
	UnpackAngles(ps->pm_state.kick_angles, new_angles);

	AngleLerp(old_angles, new_angles, cl.lerp, angles);
	VectorAdd(r_view.angles, angles, r_view.angles);

	// and lastly the delta angles
	UnpackAngles(ops->pm_state.delta_angles, old_angles);
	UnpackAngles(ps->pm_state.delta_angles, new_angles);

	VectorCopy(new_angles, angles);

	// check for small delta angles, and interpolate them
	if (!VectorCompare(new_angles, new_angles)) {

		VectorSubtract(old_angles, new_angles, angles);
		vec_t f = VectorLength(angles);

		if (f < 15.0) {
			AngleLerp(old_angles, new_angles, cl.lerp, angles);
		}
	}

	VectorAdd(r_view.angles, angles, r_view.angles);

	ClampAngles(r_view.angles);

	if (cl.frame.ps.pm_state.type == PM_DEAD) { // look only on x axis
		r_view.angles[0] = 0.0;
		r_view.angles[2] = 45.0;
	}

	// and finally set the view directional vectors
	AngleVectors(r_view.angles, r_view.forward, r_view.right, r_view.up);
}
Example #6
0
/**
 * @brief Checks for client side prediction errors. These will occur under normal gameplay
 * conditions if the client is pushed by another entity on the server (projectile, platform, etc.).
 */
void Cl_CheckPredictionError(void) {

	if (!cls.cgame->UsePrediction()) {
		return;
	}

	cl_predicted_state_t *pr = &cl.predicted_state;

	if (cl.delta_frame) {

		// calculate the last cl_cmd_t we sent that the server has processed
		const uint32_t cmd = cls.net_chan.incoming_acknowledged & CMD_MASK;

		// subtract what the server returned with what we had predicted it to be
		VectorSubtract(cl.frame.ps.pm_state.origin, pr->origins[cmd], pr->error);

		// if the error is too large, it was likely a teleport or respawn, so ignore it
		const vec_t len = VectorLength(pr->error);
		if (len > 64.0) {
			Com_Debug(DEBUG_CLIENT, "Clear %s\n", vtos(pr->error));
			VectorClear(pr->error);
		} else if (len > 0.1) {
			Com_Debug(DEBUG_CLIENT, "Error %s\n", vtos(pr->error));
		}

	} else {
		Com_Debug(DEBUG_CLIENT, "No delta\n");

		VectorCopy(cl.frame.ps.pm_state.origin, pr->view.origin);

		UnpackVector(cl.frame.ps.pm_state.view_offset, pr->view.offset);
		UnpackAngles(cl.frame.ps.pm_state.view_angles, pr->view.angles);

		VectorClear(pr->error);
	}
}
Example #7
0
/*
 * @brief Sets the kick value based on recent events such as falling. Firing of
 * weapons may also set the kick value, and we factor that in here as well.
 */
static void G_ClientKickAngles(g_entity_t *ent) {
	uint16_t *kick_angles = ent->client->ps.pm_state.kick_angles;

	// spectators and dead clients receive no kick angles

	if (ent->client->ps.pm_state.type != PM_NORMAL) {
		VectorClear(kick_angles);
		return;
	}

	vec3_t kick;
	UnpackAngles(kick_angles, kick);

	// un-clamp them so that we can work with small signed values near zero

	for (int32_t i = 0; i < 3; i++) {
		if (kick[i] > 180.0)
			kick[i] -= 360.0;
	}

	// add in any event-based feedback

	switch (ent->s.event) {
		case EV_CLIENT_LAND:
			kick[PITCH] += 2.5;
			break;
		case EV_CLIENT_FALL:
			kick[PITCH] += 5.0;
			break;
		case EV_CLIENT_FALL_FAR:
			kick[PITCH] += 10.0;
			break;
		default:
			break;
	}

	// and any velocity-based feedback

	vec_t forward = DotProduct(ent->locals.velocity, ent->client->locals.forward);
	kick[PITCH] += forward / 450.0;

	vec_t right = DotProduct(ent->locals.velocity, ent->client->locals.right);
	kick[ROLL] += right / 400.0;

	// now interpolate the kick angles towards neutral over time

	vec_t delta = VectorLength(kick);

	if (!delta) // no kick, we're done
		return;

	// we recover from kick at a rate based on the kick itself

	delta = 0.5 + delta * delta * gi.frame_seconds;

	for (int32_t i = 0; i < 3; i++) {

		// clear angles smaller than our delta to avoid oscillations
		if (fabs(kick[i]) <= delta) {
			kick[i] = 0.0;
		} else if (kick[i] > 0.0) {
			kick[i] -= delta;
		} else {
			kick[i] += delta;
		}
	}

	PackAngles(kick, kick_angles);
}