Exemple #1
0
/**
 * @sa CL_InvDel
 * @sa G_SendInventory
 * @sa EV_INV_ADD
 */
void CL_InvAdd (const eventRegister_t* self, dbuffer* msg)
{
	const int number = NET_ReadShort(msg);
	le_t* le = LE_Get(number);
	int nr = NET_ReadShort(msg);

	if (!le)
		LE_NotFoundError(number);

	le->flags &= ~LE_REMOVE_NEXT_FRAME;

	for (; nr-- > 0;) {
		Item item;
		containerIndex_t container;
		int x, y;
		CL_NetReceiveItem(msg, &item, &container, &x, &y);

		if (LE_IsItem(le)) {
			if (container != CID_FLOOR)
				Com_Error(ERR_DROP, "InvAdd for ET_ITEM but target container is not the floor but %i", container);
		} else if (INVDEF(container)->temp) {
			Com_Error(ERR_DROP, "InvAdd for %i to temp container %i", le->type, container);
		}

		if (cls.i.addToInventory(&le->inv, &item, INVDEF(container), x, y, item.getAmount()) == nullptr)
			Com_Error(ERR_DROP, "InvAdd failed - could not add %i item(s) of %s to container %i",
					item.getAmount(), item.def()->id, container);

		if (container == CID_RIGHT)
			le->right = item.def()->idx;
		else if (container == CID_LEFT)
			le->left = item.def()->idx;
		else if (container == CID_HEADGEAR)
			le->headgear = item.def()->idx;
	}

	switch (le->type) {
	case ET_ACTOR:
	case ET_ACTOR2x2:
		if (LE_IsSelected(le))
			Cmd_ExecuteString("hud_updateactorload");
		LE_SetThink(le, LET_StartIdle);
		break;
	case ET_ITEM:
		LE_PlaceItem(le);
		break;
	default:
		break;
	}
}
/**
 * @brief Parses the actor stats that comes from the netchannel
 * @sa CL_ParseEvent
 * @sa G_SendStats
 */
void CL_ActorStats (const eventRegister_t *self, dbuffer *msg)
{
	le_t *le;
	int entnum, oldTUs = 0;

	entnum = NET_ReadShort(msg);
	le = LE_Get(entnum);
	if (!le)
		LE_NotFoundError(entnum);

	switch (le->type) {
	case ET_ACTORHIDDEN:
	case ET_ACTOR:
	case ET_ACTOR2x2:
		break;
	default:
		Com_Printf("CL_ActorStats: LE (%i) not an actor (type: %i)\n", entnum, le->type);
		return;
	}

	if (LE_IsSelected(le))
		oldTUs = le->TU;

	NET_ReadFormat(msg, self->formatString, &le->TU, &le->HP, &le->STUN, &le->morale);

	if (le->TU > le->maxTU)
		le->maxTU = le->TU;
	if (le->HP > le->maxHP)
		le->maxHP = le->HP;
	if (le->morale > le->maxMorale)
		le->maxMorale = le->morale;

	/* if selActor's timeunits have changed, update movelength */
	if (le->TU != oldTUs && LE_IsSelected(le))
		CL_ActorResetMoveLength(le);
}
Exemple #3
0
/**
 * @brief Ends the move of an actor
 */
void LE_DoEndPathMove (le_t* le)
{
	/* Verify the current position */
	if (!VectorCompare(le->pos, le->newPos))
		Com_Error(ERR_DROP, "LE_DoEndPathMove: Actor movement is out of sync: %i:%i:%i should be %i:%i:%i (step %i of %i) (team %i)",
				le->pos[0], le->pos[1], le->pos[2], le->newPos[0], le->newPos[1], le->newPos[2], le->pathPos, le->pathLength, le->team);

	CL_ActorConditionalMoveCalc(le);
	/* if the moving actor was not the selected one, */
	/* recalc the pathing table for the selected one, too. */
	if (!LE_IsSelected(le)) {
		CL_ActorConditionalMoveCalc(selActor);
	}

	LE_LinkFloorContainer(le);

	LE_SetThink(le, LET_StartIdle);
	LE_ExecuteThink(le);
	LE_Unlock(le);
}
Exemple #4
0
static void UI_RadarNodeDrawActor (const le_t* le, const vec3_t pos)
{
	vec2_t coords[4];
	vec2_t vertices[4];
	int i;
	const float size = 10;
	const int tileSize = 28;
	int tilePos = 4;
	const image_t* image;
	vec4_t color;
	const float pov = directionAngles[le->angle] * torad + M_PI;

	image = UI_LoadImage("ui/radar");
	if (image == nullptr)
		return;

	/* draw FOV */
	if (!LE_IsDead(le)) {
		vertices[0][0] = - size * 4;
		vertices[0][1] = + 0;
		vertices[1][0] = + size * 4;
		vertices[1][1] = + 0;
		vertices[2][0] = + size * 4;
		vertices[2][1] = - size * 4;
		vertices[3][0] = - size * 4;
		vertices[3][1] = - size * 4;
		coords[0][0] = (7) / 128.0f;
		coords[0][1] = (37 + 63) / 128.0f;
		coords[1][0] = (7 + 114) / 128.0f;
		coords[1][1] = (37 + 63) / 128.0f;
		coords[2][0] = (7 + 114) / 128.0f;
		coords[2][1] = (37) / 128.0f;
		coords[3][0] = (7) / 128.0f;
		coords[3][1] = (37) / 128.0f;

		/* affine transformation */
		for (i = 0; i < 4; i++) {
			const float dx = vertices[i][0];
			const float dy = vertices[i][1];
			vertices[i][0] = pos[0] + dx * sin(pov) + dy * cos(pov);
			vertices[i][1] = pos[1] + dx * cos(pov) - dy * sin(pov);
		}

		UI_RadarNodeGetActorColor(le, color);
		if (LE_IsSelected(le)) {
			color[3] *= 0.75;
		} else {
			color[3] = 0.1f;
		}
		UI_RadarNodeDrawArrays(color, coords, vertices, image);
	}

	if (LE_IsDead(le))
		tilePos = 4;
	else if (LE_IsSelected(le))
		tilePos = 66;
	else
		tilePos = 36;

	/* a 0,0 centered square */
	vertices[0][0] = - size;
	vertices[0][1] = + size;
	vertices[1][0] = + size;
	vertices[1][1] = + size;
	vertices[2][0] = + size;
	vertices[2][1] = - size;
	vertices[3][0] = - size;
	vertices[3][1] = - size;
	coords[0][0] = (tilePos) / 128.0f;
	coords[0][1] = (5 + tileSize) / 128.0f;
	coords[1][0] = (tilePos + tileSize) / 128.0f;
	coords[1][1] = (5 + tileSize) / 128.0f;
	coords[2][0] = (tilePos + tileSize) / 128.0f;
	coords[2][1] = (5) / 128.0f;
	coords[3][0] = (tilePos) / 128.0f;
	coords[3][1] = (5) / 128.0f;

	/* affine transformation */
	for (i = 0; i < 4; i++) {
		const float dx = vertices[i][0];
		const float dy = vertices[i][1];
		vertices[i][0] = pos[0] + dx * sin(pov) + dy * cos(pov);
		vertices[i][1] = pos[1] + dx * cos(pov) - dy * sin(pov);
	}

	UI_RadarNodeGetActorColor(le, color);
	UI_RadarNodeDrawArrays(color, coords, vertices, image);
}
Exemple #5
0
/**
 * @brief Calculates chance to hit if the actor has a fire mode activated.
 * @param[in] actor The local entity of the actor to calculate the hit probability for.
 * @todo The hit probability should work somewhat differently for splash damage weapons.
 * Since splash damage weapons can deal damage even when they don't directly hit an actor,
 * the hit probability should be defined as the predicted percentage of the maximum splash
 * damage of the firemode, assuming the projectile explodes at the desired location. This
 * means that a percentage should be displayed for EVERY actor in the predicted blast
 * radius. This will likely require specialized code.
 */
int CL_GetHitProbability (const le_t* actor)
{
	assert(actor);
	assert(actor->fd);

	pos3_t toPos;
	if (IS_MODE_FIRE_PENDING(actor->actorMode))
		VectorCopy(actor->mousePendPos, toPos);
	else
		VectorCopy(mousePos, toPos);

	/** @todo use LE_FindRadius */
	const le_t* le = LE_GetFromPos(toPos);
	if (!le)
		return 0;

	/* Target is not visible */
	if  (LE_IsInvisible(le))
		return 0;

	/* or suicide attempted */
	if (LE_IsSelected(le) && !FIRESH_IsMedikit(le->fd))
		return 0;

	vec3_t shooter;
	VectorCopy(actor->origin, shooter);
	vec3_t target;
	VectorCopy(le->origin, target);

	/* Calculate HitZone: */
	const int distx = fabs(shooter[0] - target[0]);
	const int disty = fabs(shooter[1] - target[1]);
	const float distance = sqrtf(distx * distx + disty * disty);
	float pseudosin;
	if (distx > disty)
		pseudosin = distance / distx;
	else
		pseudosin = distance / disty;
	float width = 2 * PLAYER_WIDTH * pseudosin;
	float height = LE_IsCrouched(le) ? PLAYER_CROUCHING_HEIGHT : PLAYER_STANDING_HEIGHT;

	const character_t* chr = CL_ActorGetChr(actor);
	if (!chr)
		Com_Error(ERR_DROP, "No character given for local entity");

	const float acc = GET_ACC(chr->score.skills[ABILITY_ACCURACY],
			actor->fd->weaponSkill ? chr->score.skills[actor->fd->weaponSkill] : 0.0, CL_ActorInjuryModifier(actor, MODIFIER_ACCURACY));

	const float crouch = (LE_IsCrouched(actor) && actor->fd->crouch) ? actor->fd->crouch : 1.0;

	const float commonfactor = crouch * torad * distance;
	const float stdevupdown = (actor->fd->spread[0] * (WEAPON_BALANCE + SKILL_BALANCE * acc)) * commonfactor;
	const float stdevleftright = (actor->fd->spread[1] * (WEAPON_BALANCE + SKILL_BALANCE * acc)) * commonfactor;
	const float hitchance = (stdevupdown > LOOKUP_EPSILON ? CL_LookupErrorFunction(height * 0.3536f / stdevupdown) : 1.0f)
			  * (stdevleftright > LOOKUP_EPSILON ? CL_LookupErrorFunction(width * 0.3536f / stdevleftright) : 1.0f);
	/* 0.3536=sqrt(2)/4 */

	/* Calculate cover: */
	int n = 0;
	height = height / 18;
	width = width / 18;
	target[2] -= UNIT_HEIGHT / 2;
	target[2] += height * 9;
	float perpX = disty / distance * width;
	float perpY = 0 - distx / distance * width;

	target[0] += perpX;
	perpX *= 2;
	target[1] += perpY;
	perpY *= 2;
	target[2] += 6 * height;
	if (!CL_TestLine(shooter, target, TL_FLAG_NONE))
		n++;
	target[0] += perpX;
	target[1] += perpY;
	target[2] -= 6 * height;
	if (!CL_TestLine(shooter, target, TL_FLAG_NONE))
		n++;
	target[0] += perpX;
	target[1] += perpY;
	target[2] += 4 * height;
	if (!CL_TestLine(shooter, target, TL_FLAG_NONE))
		n++;
	target[2] += 4 * height;
	if (!CL_TestLine(shooter, target, TL_FLAG_NONE))
		n++;
	target[0] -= perpX * 3;
	target[1] -= perpY * 3;
	target[2] -= 12 * height;
	if (!CL_TestLine(shooter, target, TL_FLAG_NONE))
		n++;
	target[0] -= perpX;
	target[1] -= perpY;
	target[2] += 6 * height;
	if (!CL_TestLine(shooter, target, TL_FLAG_NONE))
		n++;
	target[0] -= perpX;
	target[1] -= perpY;
	target[2] -= 4 * height;
	if (!CL_TestLine(shooter, target, TL_FLAG_NONE))
		n++;
	target[0] -= perpX;
	target[1] -= perpY;
	target[2] += 10 * height;
	if (!CL_TestLine(shooter, target, TL_FLAG_NONE))
		n++;

	return 100 * (hitchance * (0.125) * n);
}
/**
 * @brief Shoot with weapon.
 * @sa CL_ActorShoot
 * @sa CL_ActorShootHidden
 * @todo Improve detection of left- or right animation.
 * @sa EV_ACTOR_SHOOT
 */
void CL_ActorDoShoot (const eventRegister_t* self, dbuffer* msg)
{
	vec3_t muzzle, impact;
	int flags, normal, shooterEntnum, victimEntnum;
	int objIdx;
	int first;
	weaponFireDefIndex_t weapFdsIdx;
	fireDefIndex_t fdIdx;
	int surfaceFlags;
	shoot_types_t shootType;

	/* read data */
	NET_ReadFormat(msg, self->formatString, &shooterEntnum, &victimEntnum, &first, &objIdx, &weapFdsIdx, &fdIdx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &normal);

	le_t* leVictim;
	if (victimEntnum != SKIP_LOCAL_ENTITY) {
		leVictim = LE_Get(victimEntnum);
		if (!leVictim)
			LE_NotFoundError(victimEntnum);
	} else {
		leVictim = nullptr;
	}

	/* get shooter le */
	le_t* leShooter = LE_Get(shooterEntnum);

	/* get the fire def */
	const objDef_t* obj = INVSH_GetItemByIDX(objIdx);
	const fireDef_t* fd = FIRESH_GetFiredef(obj, weapFdsIdx, fdIdx);

	CL_ActorGetMuzzle(leShooter, muzzle, shootType);

	/* add effect le */
	LE_AddProjectile(fd, flags, muzzle, impact, normal, leVictim);

	/* start the sound */
	if ((first || !fd->soundOnce) && fd->fireSound != nullptr && !(flags & SF_BOUNCED))
		S_LoadAndPlaySample(fd->fireSound, muzzle, fd->fireAttenuation, SND_VOLUME_WEAPONS);

	/* do actor related stuff */
	if (!leShooter)
		return; /* maybe hidden or inuse is false? */

	if (!LE_IsActor(leShooter))
		Com_Error(ERR_DROP, "Can't shoot, LE not an actor (type: %i)", leShooter->type);

	/* no animations for hidden actors */
	if (leShooter->type == ET_ACTORHIDDEN)
		return;

	if (LE_IsDead(leShooter)) {
		Com_DPrintf(DEBUG_CLIENT, "Can't shoot, actor dead or stunned.\n");
		return;
	}

	/* Animate - we have to check if it is right or left weapon usage. */
	if (IS_SHOT_RIGHT(shootType)) {
		R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->right, leShooter->left, leShooter->state));
		R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->right, leShooter->left, leShooter->state));
	} else if (IS_SHOT_LEFT(shootType)) {
		R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->left, leShooter->right, leShooter->state));
		R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->left, leShooter->right, leShooter->state));
	} else if (IS_SHOT_HEADGEAR(shootType)) {
		if (fd->irgoggles) {
			leShooter->state |= RF_IRGOGGLESSHOT;
			if (LE_IsSelected(leShooter))
				refdef.rendererFlags |= RDF_IRGOGGLES;
		}
	} else {
		/* no animation for headgear (yet) */
		Com_Error(ERR_DROP, "CL_ActorDoShoot: Invalid shootType given (entnum: %i, shootType: %i).\n", shootType, shooterEntnum);
	}
}
Exemple #7
0
/**
 * @brief Adds an UGV to the render entities.
 * @param[in] le The local entity the UGV should be created from
 * @param[out] ent
 * @sa CL_AddActor
 */
bool CL_AddUGV (le_t * le, entity_t * ent)
{
	entity_t add;

	if (!LE_IsDead(le)) {
		/* add weapon */
		if (le->left != NONE) {
			OBJZERO(add);

			add.model = cls.modelPool[le->left];

			add.tagent = R_GetFreeEntity() + 2 + (le->right != NONE);
			add.tagname = "tag_lweapon";

			R_AddEntity(&add);
		}

		/* add weapon */
		if (le->right != NONE) {
			OBJZERO(add);

			add.alpha = le->alpha;
			add.model = cls.modelPool[le->right];

			add.tagent = R_GetFreeEntity() + 2;
			add.tagname = "tag_rweapon";

			R_AddEntity(&add);
		}
	}

	/* add head */
	OBJZERO(add);

	add.alpha = le->alpha;
	add.model = le->model2;
	add.skinnum = le->bodySkin;

	/** @todo */
	add.tagent = R_GetFreeEntity() + 1;
	add.tagname = "tag_head";

	R_AddEntity(&add);

	/* add actor special effects */
	ent->flags |= RF_SHADOW;
	ent->flags |= RF_ACTOR;

	if (!LE_IsDead(le)) {
		if (LE_IsSelected(le))
			ent->flags |= RF_SELECTED;
		if (le->team == cls.team) {
			if (le->pnum == cl.pnum)
				ent->flags |= RF_MEMBER;
			if (le->pnum != cl.pnum)
				ent->flags |= RF_ALLIED;
		}
		if (le->team == TEAM_CIVILIAN)
			ent->flags |= RF_NEUTRAL;
	}

	return true;
}
Exemple #8
0
/**
 * @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);
		}
	}
}