예제 #1
0
/**
 * @brief Interpolates the camera movement from the given start point to the given end point
 * @sa CL_CameraMove
 * @sa CL_ViewCenterAtGridPosition
 * @param[in] from The grid position to start the camera movement from
 * @param[in] target The grid position to move the camera to
 */
void CL_CameraRoute (const pos3_t from, const pos3_t target)
{
	if (!cl_centerview->integer)
		return;

	/* initialize the camera route variables */
	PosToVec(from, routeFrom);
	PosToVec(target, routeDelta);
	VectorSubtract(routeDelta, routeFrom, routeDelta);
	routeDelta[2] = 0;
	routeDist = VectorLength(routeDelta);
	VectorNormalize(routeDelta);

	/* center the camera on the route starting position */
	VectorCopy(routeFrom, cl.cam.origin);
	/* set the world level to the z axis value of the camera target
	 * the camera lerp will do a smooth translate from the old level
	 * to the new one */
	Cvar_SetValue("cl_worldlevel", target[2]);

	VectorClear(cl.cam.speed);
	cameraRoute = true;

	CL_BlockBattlescapeEvents(true);
}
예제 #2
0
/**
 * @param[in] mapTiles List of tiles the current (RMA-)map is composed of
 * @param[in] start trace start vector
 * @param[in] end trace end vector
 * @param[in] box The box we move through the world
 * @param[in] levelmask Selects which submodels get scanned.
 * @param[in] brushmask brushes the trace should stop at (see MASK_*)
 * @param[in] brushreject brushes the trace should ignore (see MASK_*)
 * @brief Traces all submodels in all tiles.  Used by ufo and ufo_ded.
 */
trace_t CM_CompleteBoxTrace (mapTiles_t *mapTiles, const vec3_t start, const vec3_t end, const AABB &box, int levelmask, int brushmask, int brushreject)
{
	trace_t newtr, tr;
	int tile, i;
	vec3_t smin, smax, emin, emax, wpmins, wpmaxs;
	const vec3_t offset = {UNIT_SIZE / 2, UNIT_SIZE / 2, UNIT_HEIGHT / 2};

	OBJZERO(tr);
	tr.fraction = 2.0f;

	/* Prep the mins and maxs */
	for (i = 0; i < 3; i++) {
		smin[i] = start[i] + std::min(box.mins[i], box.maxs[i]);
		smax[i] = start[i] + std::max(box.mins[i], box.maxs[i]);
		emin[i] = end[i] + std::min(box.mins[i], box.maxs[i]);
		emax[i] = end[i] + std::max(box.mins[i], box.maxs[i]);
	}

	/* trace against all loaded map tiles */
	for (tile = 0; tile < mapTiles->numTiles; tile++) {
		mapTile_t *myTile = &mapTiles->mapTiles[tile];
		PosToVec(myTile->wpMins, wpmins);
		VectorSubtract(wpmins, offset, wpmins);
		PosToVec(myTile->wpMaxs, wpmaxs);
		VectorAdd(wpmaxs, offset, wpmaxs);
		/* If the trace is completely outside of the tile, then skip it. */
		if (smax[0] < wpmins[0] && emax[0] < wpmins[0])
			continue;
		if (smax[1] < wpmins[1] && emax[1] < wpmins[1])
			continue;
		if (smax[2] < wpmins[2] && emax[2] < wpmins[2])
			continue;
		if (smin[0] > wpmaxs[0] && emin[0] > wpmaxs[0])
			continue;
		if (smin[1] > wpmaxs[1] && emin[1] > wpmaxs[1])
			continue;
		if (smin[2] > wpmaxs[2] && emin[2] > wpmaxs[2])
			continue;
		newtr = TR_TileBoxTrace(myTile, start, end, box, levelmask, brushmask, brushreject);
		newtr.mapTile = tile;

		/* memorize the trace with the minimal fraction */
		if (newtr.fraction == 0.0)
			return newtr;
		if (newtr.fraction < tr.fraction)
			tr = newtr;
	}
	return tr;
}
예제 #3
0
/**
 * @param[in] mapTiles List of tiles the current (RMA-)map is composed of
 * @param[in] trLine The line to trace
 * @param[in] box The box we move through the world
 * @param[in] levelmask Selects which submodels get scanned.
 * @param[in] brushmask brushes the trace should stop at (see MASK_*)
 * @param[in] brushreject brushes the trace should ignore (see MASK_*)
 * @brief Traces all submodels in all tiles.  Used by ufo and ufo_ded.
 */
trace_t CM_CompleteBoxTrace (mapTiles_t* mapTiles, const Line& trLine, const AABB& box, int levelmask, int brushmask, int brushreject)
{
	int tile;
	vec3_t smin, smax, emin, emax, wpmins, wpmaxs;
	const vec3_t offset = {UNIT_SIZE / 2, UNIT_SIZE / 2, UNIT_HEIGHT / 2};

	trace_t tr;
	tr.fraction = 2.0f;
	VectorCopy(trLine.stop, tr.endpos);	/* optimistically set the endpos just in case we are outside of ALL the tiles, so TR_TileBoxTrace won't do it */

	/* Prep the mins and maxs */
	for (int i = 0; i < 3; i++) {
		smin[i] = trLine.start[i] + std::min(box.mins[i], box.maxs[i]);
		smax[i] = trLine.start[i] + std::max(box.mins[i], box.maxs[i]);
		emin[i] = trLine.stop[i] + std::min(box.mins[i], box.maxs[i]);
		emax[i] = trLine.stop[i] + std::max(box.mins[i], box.maxs[i]);
	}

	/* trace against all loaded map tiles */
	for (tile = 0; tile < mapTiles->numTiles; tile++) {
		MapTile& myTile = mapTiles->mapTiles[tile];
		PosToVec(myTile.wpMins, wpmins);
		VectorSubtract(wpmins, offset, wpmins);
		PosToVec(myTile.wpMaxs, wpmaxs);
		VectorAdd(wpmaxs, offset, wpmaxs);
		/* If the trace is completely outside of the tile, then skip it. */
		if (smax[0] < wpmins[0] && emax[0] < wpmins[0])
			continue;
		if (smax[1] < wpmins[1] && emax[1] < wpmins[1])
			continue;
		if (smax[2] < wpmins[2] && emax[2] < wpmins[2])
			continue;
		if (smin[0] > wpmaxs[0] && emin[0] > wpmaxs[0])
			continue;
		if (smin[1] > wpmaxs[1] && emin[1] > wpmaxs[1])
			continue;
		if (smin[2] > wpmaxs[2] && emin[2] > wpmaxs[2])
			continue;
		trace_t newtr = TR_TileBoxTrace(&myTile, trLine.start, trLine.stop, box, levelmask, brushmask, brushreject);
		newtr.mapTile = tile;

		/* memorize the trace with the minimal fraction */
		if (newtr.fraction == 0.0)
			return newtr;
		if (newtr.fraction < tr.fraction)
			tr = newtr;
	}
	return tr;
}
예제 #4
0
파일: cl_view.cpp 프로젝트: cigo/ufoai
/**
 * @brief Centers the camera on a given grid field
 * @sa CL_CameraMove
 * @sa LE_CenterView
 * @sa CL_CameraRoute
 */
void CL_ViewCenterAtGridPosition (const pos3_t pos)
{
	vec3_t vec;

	PosToVec(pos, vec);
	VectorCopy(vec, cl.cam.origin);
	Cvar_SetValue("cl_worldlevel", pos[2]);
}
예제 #5
0
static void LE_PlayFootStepSound (le_t* le)
{
	if (Q_strvalid(le->teamDef->footstepSound)) {
		S_LoadAndPlaySample(le->teamDef->footstepSound, le->origin, SOUND_ATTN_NORM, SND_VOLUME_FOOTSTEPS);
		return;
	}
	/* walking in water will not play the normal footstep sounds */
	if (!le->pathContents[le->pathPos]) {
		vec3_t from, to;

		/* prepare trace vectors */
		PosToVec(le->pos, from);
		VectorCopy(from, to);
		/* we should really hit the ground with this */
		to[2] -= UNIT_HEIGHT;

		const trace_t trace = CL_Trace(Line(from, to), AABB::EMPTY, nullptr, nullptr, MASK_SOLID, cl_worldlevel->integer);
		if (trace.surface)
			LE_PlaySoundFileAndParticleForSurface(le, trace.surface->name);
	} else
		LE_PlaySoundFileForContents(le, le->pathContents[le->pathPos]);
}
예제 #6
0
/**
 * @brief Plays step sounds and draw particles for different terrain types
 * @param[in] le The local entity to play the sound and draw the particle for
 * @param[in] textureName The name of the texture the actor is standing on
 * @sa LET_PathMove
 */
static void LE_PlaySoundFileAndParticleForSurface (le_t* le, const char* textureName)
{
	const terrainType_t* t = Com_GetTerrainType(textureName);
	if (!t)
		return;

	/* origin might not be up-to-date here - but pos should be */
	vec3_t origin;
	PosToVec(le->pos, origin);

	/** @todo use the Grid_Fall method (ACTOR_SIZE_NORMAL) to ensure, that the particle is
	 * drawn at the ground (if needed - maybe the origin is already ground aligned)*/
	if (t->particle) {
		/* check whether actor is visible */
		if (!LE_IsStunned(le) && LE_IsLivingAndVisibleActor(le))
			CL_ParticleSpawn(t->particle, 0, origin);
	}
	if (t->footstepSound) {
		Com_DPrintf(DEBUG_SOUND, "LE_PlaySoundFileAndParticleForSurface: volume %.2f\n", t->footstepVolume);
		S_LoadAndPlaySample(t->footstepSound, origin, SOUND_ATTN_STATIC, t->footstepVolume);
	}
}
예제 #7
0
	/**
	 * @param[in] l Routing lump ... (routing data lump from bsp file)
	 * @param[in] sX The x position on the world plane (grid position) - values from -(PATHFINDING_WIDTH/2) up to PATHFINDING_WIDTH/2 are allowed
	 * @param[in] sY The y position on the world plane (grid position) - values from -(PATHFINDING_WIDTH/2) up to PATHFINDING_WIDTH/2 are allowed
	 * @param[in] sZ The height level on the world plane (grid position) - values from 0 - PATHFINDING_HEIGHT are allowed
	 * @sa CM_AddMapTile
	 * @todo TEST z-level routing
	 */
	static void CMod_LoadRouting (RoutingLump& routingLump, const std::string& name, const lump_t * l,
			byte* cModelBase, int sX, int sY, int sZ)
	{
		static routing_t tempMap[ACTOR_MAX_SIZE];
		static routing_t clMap[ACTOR_MAX_SIZE];
		static mapTile_t curTile;
		byte *source;
		int length;
		int x, y, z, size, dir;
		int minX, minY, minZ;
		int maxX, maxY, maxZ;
		unsigned int i;
		double start, end;
		const int targetLength = sizeof(curTile.wpMins) + sizeof(curTile.wpMaxs) + sizeof(tempMap);

		start = time(NULL);

		if (!GUINT32_TO_LE(l->filelen)) {
			g_error("CMod_LoadRouting: Map has NO routing lump");
			return;
		}

		source = cModelBase + GUINT32_TO_LE(l->fileofs);

		i = CMod_DeCompressRouting(&source, (byte*) curTile.wpMins);
		length = i;
		i = CMod_DeCompressRouting(&source, (byte*) curTile.wpMaxs);
		length += i;
		i = CMod_DeCompressRouting(&source, (byte*) tempMap);
		length += i;

		if (length != targetLength) {
			g_error("CMod_LoadRouting: Map has BAD routing lump; expected %i got %i", targetLength, length);
			return;
		}

		/* endian swap possibly necessary */
		for (i = 0; i < 3; i++) {
			curTile.wpMins[i] = GUINT32_TO_LE(curTile.wpMins[i]);
			curTile.wpMaxs[i] = GUINT32_TO_LE(curTile.wpMaxs[i]);
		}

		g_debug("wpMins:(%i, %i, %i) wpMaxs:(%i, %i, %i)\n", curTile.wpMins[0], curTile.wpMins[1], curTile.wpMins[2],
				curTile.wpMaxs[0], curTile.wpMaxs[1], curTile.wpMaxs[2]);

		/* Things that need to be done:
		 * The floor, ceiling, and route data can be copied over from the map.
		 * All data must be regenerated for cells with overlapping content or where new
		 * model data is adjacent to a cell with existing model data. */

		/* Copy the routing information into our master table */
		minX = std::max(curTile.wpMins[0], 0);
		minY = std::max(curTile.wpMins[1], 0);
		minZ = std::max(curTile.wpMins[2], 0);
		maxX = std::min(curTile.wpMaxs[0], PATHFINDING_WIDTH - 1);
		maxY = std::min(curTile.wpMaxs[1], PATHFINDING_WIDTH - 1);
		maxZ = std::min(curTile.wpMaxs[2], PATHFINDING_HEIGHT - 1);

		/** @todo check whether we need this copy code */
		for (size = 0; size < ACTOR_MAX_SIZE; size++)
			/* Adjust starting x and y by size to catch large actor cell overlap. */
			for (y = minY - size; y <= maxY; y++)
				for (x = minX - size; x <= maxX; x++) {
					/* Just incase x or y start negative. */
					if (x < 0 || y < 0)
						continue;
					for (z = minZ; z <= maxZ; z++) {
						clMap[size].floor[z][y][x] = tempMap[size].floor[z - sZ][y - sY][x - sX];
						clMap[size].ceil[z][y][x] = tempMap[size].ceil[z - sZ][y - sY][x - sX];
						for (dir = 0; dir < CORE_DIRECTIONS; dir++) {
							clMap[size].route[z][y][x][dir] = tempMap[size].route[z - sZ][y - sY][x - sX][dir];
							clMap[size].stepup[z][y][x][dir] = tempMap[size].stepup[z - sZ][y - sY][x - sX][dir];
						}
					}
					/* Update the reroute table */
					/*if (!reroute[size][y][x]) {
					 reroute[size][y][x] = numTiles + 1;
					 } else {
					 reroute[size][y][x] = ROUTING_NOT_REACHABLE;
					 }*/
				}

		g_message("Done copying data.\n");
		for (size = 0; size < 1; size++)
			for (x = minX; x <= maxX; x++)
				for (y = minY; y <= maxY; y++)
					for (z = minZ; z <= maxZ; z++) {
						pos3_t pos = { x, y, z };
						vec3_t vect;
						PosToVec(pos,vect);
						/**@todo add other data to constructor: accessibility + connection states */
						RoutingLumpEntry entry = RoutingLumpEntry(Vector3(vect), (z + 1));
						FillRoutingLumpEntry(entry, clMap, pos);
						/**@todo perhaps there is a better way than creating a const object for adding */
						const RoutingLumpEntry toAdd = RoutingLumpEntry(entry);
						routingLump.add(toAdd);
					}
		end = time(NULL);
		g_message("Loaded routing for tile %s in %5.1fs\n", name.c_str(), end - start);
	}
예제 #8
0
/**
 * @brief This test cycles through the list of map definitions found in the maps.ufo script
 * and tries to find surfaces to stand on with no sound assigned to them.
 *
 * This test takes too long to be run every time testall is run. So it's set up almost like a game:
 * After 5 maps (the first of them is fully checked) with missing sounds, the test stops.
 * If we manage to 'clean' one of those 5 maps, the next map will show up in the next run.
 */
TEST_F(FootStepTest, DISABLED_MapDefsFootSteps)
{
	const char* filterId = TEST_GetStringProperty("mapdef-id");
	const mapDef_t* md;
	int mapCount = 0;				// the number of maps read
	int badMapCount = 0;
	const int skipCount = 20;		// to skip the first n mapDefs
	const int badMapCountMax = 25;	// # of maps with missing sounds before this test stops
	const int mapCountMax = 150;	// should of cause be higher than skip + bad
	const int texCountMax = 30;
	char texNames[texCountMax][MAX_QPATH];
	bool done = false;

	OBJZERO(texNames);
	ASSERT_TRUE(csi.numMDs > 0);

	MapDef_Foreach(md) {
		if (md->mapTheme[0] == '.')
			continue;
		if (filterId && !Q_streq(filterId, md->id))
			continue;

		mapCount++;
		if (mapCount <= skipCount)
			continue;
		/* use a known seed to reproduce an error */
		unsigned int seed;
		if (TEST_ExistsProperty("mapdef-seed")) {
			seed = TEST_GetLongProperty("mapdef-seed");
		} else {
			seed = (unsigned int) time(nullptr);
		}
		srand(seed);

		int count = 0;
		Com_Printf("testMapDefsFootSteps: Mapdef %s (seed %u)\n", md->id, seed);

		const char* asmName = (const char*)LIST_GetByIdx(md->params, 0);
		SV_Map(true, md->mapTheme, asmName);

		/* now that we have loaded the map, check all cells for walkable places */
		GridBox mBox(sv->mapData.mapBox);	// test ALL the cells
#if !FOOTSTEPS_FULL
		if (mapCount >= skipCount + 4) {	// after the first 4 maps, reduce the testing area
			const pos3_t center = {148, 128, 0};
			mBox.set(center, center);		// the box on the map we're testing
			mBox.expandXY(10);				// just test a few cells around the center of the map
			mBox.maxs[2] = 2;				// and 3 levels high
		}
#endif
		mBox.clipToMaxBoundaries();

		for (int x = mBox.getMinX(); x <= mBox.getMaxX() && !done; x++) {
			for (int y = mBox.getMinY(); y <= mBox.getMaxY() && !done; y++) {
				for (int z = mBox.getMinZ(); z <= mBox.getMaxZ(); z++) {
					const int floor = sv->mapData.routing.getFloor(1, x, y, z);
					if (floor < 0)						// if we have a floor in that cell
						continue;
					const AABB noBox(vec3_origin, vec3_origin);	// we're doing a point-trace
					const pos3_t cellPos = {(pos_t)x, (pos_t)y, (pos_t)z};			// the cell in question
					vec3_t from, to;
					PosToVec(cellPos, from);			// the center of the cell
					VectorCopy(from, to);				// also base for the endpoint of the trace
					from[2] -= UNIT_HEIGHT / 2;			// bottom of the cell
					from[2] += (floor + 2) * QUANT;		// add the height of the floor plus 2 QUANTS
					to[2] -= 2 * UNIT_HEIGHT;			// we should really hit the ground with this
					const trace_t trace = SV_Trace(Line(from, to), noBox, nullptr, MASK_SOLID);
					if (!trace.surface)
						continue;

					const char* snd = SV_GetFootstepSound(trace.surface->name);
					if (snd)
						continue;

					for (int i = 0; i < texCountMax; ++i) {
						if (!texNames[i][0]) {	// found a free slot ?
							Q_strncpyz(texNames[i], trace.surface->name, sizeof(texNames[i]));
							count++;
							break;
						}
						if (Q_streq(trace.surface->name, texNames[i]))	// already there ?
							break;
					}
					if (count > texCountMax) {
						done = true;
						break;	// the z-loop
					}
				}
			}
		}
		if (!texNames[0][0]) {
			Com_Printf("In map %s, asm %s: Nothing detected\n", md->mapTheme, asmName);
		} else {
			++badMapCount;
			for (int i = 0; i < texCountMax; ++i) {
				if (texNames[i][0]) {
					Com_Printf("In map %s, asm %s: No sound for: %s\n", md->mapTheme, asmName, texNames[i]);
				}
			}
		}
		OBJZERO(texNames);
		SV_ShutdownGameProgs();

		if (done || mapCount >= mapCountMax || badMapCount >= badMapCountMax)
			break;
	}
}