REDRIVER2/src_rebuild/Game/C/overmap.c
2022-01-30 11:59:22 +03:00

1968 lines
36 KiB
C

#include "driver2.h"
#include "overmap.h"
#include "texture.h"
#include "cars.h"
#include "system.h"
#include "mission.h"
#include "mdraw.h"
#include "players.h"
#include "overlay.h"
#include "debris.h"
#include "map.h"
#include "convert.h"
#include "pres.h"
#include "cop_ai.h"
#include "camera.h"
#include "felony.h"
#include "pad.h"
#include "ASM/rnc_2.h"
struct MAPTEX
{
short u, w, v, h;
};
struct OVERMAP
{
int x_offset, y_offset;
int width, height;
u_char toptile;
u_char dummy;
int scale;
};
OVERMAP overlaidmaps[4] =
{
{ 197, 318, 384, 672, 252, 0x99, 2145 },
{ 229, 85, 544, 352, 187, 0x88, 2048 },
{ 68, 457, 288, 672, 189, 0xBB, 1911 },
{ 159, 207, 416, 576, 252, 0x44, 2048 }
};
SXYPAIR MapSegmentPos[16];
XYPAIR NVertex[4] = {
{ -2, 3 },
{ -2, -3 },
{ 2, 3 },
{ 2, -3 }
};
XYPAIR north[4] = {
{ 0, 20 },
{ 0, 20 },
{ 0, 20 },
{ 0, 20 }
};
static short big_north[] =
{
2048, 2048, 2048, 2048
};
VECTOR player_position = { 0, 0, 0, 0 };
MATRIX map_matrix;
char* MapBitMaps;
static char MapBuffer[520];
static unsigned short MapClut;
static RECT16 MapRect;
static int tilehnum = 0;
static char maptile[4][4];
static char tile_size = 32;
static int old_x_mod = 0;
static int old_y_mod = 0;
static int x_map = 0;
static int y_map = 0;
static int map_x_offset = 0;
static int map_z_offset = 0;
int map_x_shift = 0;
int map_z_shift = 0;
static unsigned short MapTPage = 0;
static int gUseRotatedMap = 0;
int gMapXOffset = 249;
int gMapYOffset = 181;
void WorldToOverheadMapPositions(VECTOR * pGlobalPosition, VECTOR * pOverheadMapPosition, int count, char inputRelative, int outputRelative);
void WorldToMultiplayerMap(VECTOR * in, VECTOR * out);
void WorldToFullscreenMap(VECTOR * in, VECTOR * out);
void WorldToFullscreenMap2(VECTOR * in, VECTOR * out);
// [D] [T]
void DrawTargetBlip(VECTOR *pos, u_char r, u_char g, u_char b, int flags)
{
int ysize;
POLY_FT4 *poly;
VECTOR vec;
int map_minX, map_maxX;
int map_minY, map_maxY;
map_minX = gMapXOffset;
map_maxX = gMapXOffset + MAP_SIZE_W;
map_minY = gMapYOffset;
map_maxY = gMapYOffset + MAP_SIZE_H;
if (flags & 0x20)
{
WorldToMultiplayerMap(pos, &vec);
vec.vx += gMapXOffset;
vec.vz += 96;
}
else if (flags & 0x8)
{
vec.vx = pos->vx;
vec.vz = pos->vz;
}
else if (flags & 0x1)
{
WorldToOverheadMapPositions(pos, &vec, 1, 0, 0);
if (vec.vx <= map_minX || vec.vx >= map_maxX || vec.vz <= map_minY || vec.vz >= map_maxY)
{
return;
}
}
else
{
WorldToFullscreenMap2(pos, &vec);
}
if ((flags & 1) == 0)
{
vec.vx += map_x_offset;
vec.vz += map_z_offset;
}
if (flags & 0x10)
ysize = OverlayFlashValue / 2;
else if (flags & 0x2)
ysize = 3;
else
ysize = 4;
poly = (POLY_FT4 *)current->primptr;
setPolyFT4(poly);
setSemiTrans(poly, 1);
poly->x0 = vec.vx - ysize;
poly->y0 = vec.vz - ysize;
poly->x1 = vec.vx + ysize;
poly->y1 = vec.vz - ysize;
poly->x2 = vec.vx - ysize;
poly->y2 = vec.vz + ysize;
poly->x3 = vec.vx + ysize;
poly->y3 = vec.vz + ysize;
poly->r0 = r;
poly->g0 = g;
poly->b0 = b;
*(ushort*)&poly->u0 = *(ushort*)&light_texture.coords.u0;
*(ushort*)&poly->u1 = *(ushort*)&light_texture.coords.u1;
*(ushort*)&poly->u2 = *(ushort*)&light_texture.coords.u2;
*(ushort*)&poly->u3 = *(ushort*)&light_texture.coords.u3;
if (flags & 0x2)
poly->tpage = light_texture.tpageid | 0x40;
else
poly->tpage = light_texture.tpageid | 0x20;
poly->clut = light_texture.clutid;
if (flags & 0x4)
{
// fullscreen map mode
DrawPrim(poly);
}
else
{
addPrim(current->ot + 1, poly);
current->primptr += sizeof(POLY_FT4);
}
}
// [D] [T]
void DrawTargetArrow(VECTOR *pos, int flags)
{
int dy;
int dx;
POLY_G3 *poly;
VECTOR vec;
VECTOR vec2;
if (flags & 0x8)
{
vec.vx = pos->vx;
vec.vz = pos->vz;
}
else if (flags & 0x1)
{
WorldToOverheadMapPositions(pos, &vec, 1, 0, 0);
}
else
{
WorldToFullscreenMap2(pos, &vec);
}
if (flags & 0x1)
{
WorldToOverheadMapPositions((VECTOR *)player, &vec2, 1, '\0', 0);
}
else
{
vec.vx = vec.vx + map_x_offset;
vec.vz = vec.vz + map_z_offset;
vec2.vx = map_x_offset + 160;
vec2.vz = map_z_offset + 128;
}
// target arrow perpendicular
dx = (vec2.vx - vec.vx) / 8;
dy = (vec2.vz - vec.vz) / 8;
poly = (POLY_G3 *)current->primptr;
setPolyG3(poly);
setSemiTrans(poly, 1);
poly->r0 = poly->r1 = 24;
poly->g0 = poly->g1 = 24;
poly->b0 = poly->b1 = 24;
poly->r2 = 64;
poly->g2 = 64;
poly->b2 = 64;
poly->x0 = vec2.vx + dy;
poly->y0 = vec2.vz - dx;
poly->x1 = vec2.vx - dy;
poly->y1 = vec2.vz + dx;
poly->x2 = vec.vx;
poly->y2 = vec.vz;
POLY_FT3* null = (POLY_FT3*)(poly + 1);
setPolyFT3(null);
setSemiTrans(null, 1);
null->x0 = -1;
null->y0 = -1;
null->x1 = -1;
null->y1 = -1;
null->x2 = -1;
null->y2 = -1;
null->tpage = 0x40;
if (flags & 0x4)
{
// fullscreen map
DrawPrim(null);
DrawPrim(poly);
}
else
{
addPrim(current->ot + 1, poly);
addPrim(current->ot + 1, null);
current->primptr += sizeof(POLY_G3) + sizeof(POLY_FT3);
}
}
// [D] [T]
void DrawPlayerDot(VECTOR *pos, short rot, u_char r, u_char g, u_char b, int flags)
{
int sn;
int cs;
POLY_F3 *poly;
VECTOR opos[3];
VECTOR vec;
if (flags & 0x20)
{
WorldToMultiplayerMap(pos, &vec);
vec.vx += gMapXOffset;
vec.vz += 96;
}
else
{
if (flags & 0x8)
{
vec.vx = pos->vx;
vec.vz = pos->vz;
}
else if (flags & 0x1)
{
WorldToOverheadMapPositions(pos, &vec, 1, 0, 0);
if (vec.vx - 233U > 94)
return;
if (vec.vz < 174 || vec.vz > 250)
return;
}
else
{
WorldToFullscreenMap2(pos, &vec);
}
}
if ((flags & 0x1) == 0)
{
vec.vx += map_x_offset;
vec.vz += map_z_offset;
}
sn = RSIN(rot);
cs = RCOS(rot);
opos[2].vx = vec.vx;
opos[2].vz = vec.vz;
opos[0].vx = vec.vx + FIXEDH(sn * -3);
opos[0].vz = vec.vz + FIXEDH(cs * -3);
opos[1].vx = vec.vx + FIXEDH(sn * 3 + cs * -2);
opos[1].vz = vec.vz + FIXEDH(cs * 3 + sn * 2);
opos[2].vx = vec.vx + FIXEDH(cs * 2 + sn * 3);
opos[2].vz = vec.vz + FIXEDH(sn * -2 + cs * 3);
poly = (POLY_F3 *)current->primptr;
setPolyF3(poly);
poly->r0 = r;
poly->g0 = g;
poly->b0 = b;
poly->x0 = opos[0].vx;
poly->y0 = opos[0].vz;
poly->x1 = opos[1].vx;
poly->y1 = opos[1].vz;
poly->x2 = opos[2].vx;
poly->y2 = opos[2].vz;
if (flags & 0x4)
{
DrawPrim(poly);
}
else
{
addPrim(current->ot + 1, poly);
current->primptr += sizeof(POLY_F3);
}
}
// [D] [T]
void ProcessOverlayLump(char *lump_ptr, int lump_size)
{
int i;
TEXTURE_DETAILS info;
GetTextureDetails("OVERHEAD", &info);
MapTPage = info.tpageid;
MapClut = GetClut(mapclutpos.x, mapclutpos.y);
MapRect.w = 64;
MapRect.h = 256;
MapRect.x = (MapTPage & 15) << 6;
MapRect.y = (MapTPage & 16) << 4;
// place map segments inside rectangle
for (i = 0; i < 16; i++)
{
MapSegmentPos[i].x = info.coords.u0 + (i & 3) * 32;
MapSegmentPos[i].y = info.coords.v0 + (i / 4) * 32;
}
D_MALLOC_BEGIN()
MapBitMaps = D_MALLOC(lump_size);
memcpy((u_char*)MapBitMaps, (u_char*)lump_ptr, lump_size);
D_MALLOC_END();
// load CLUT
LoadImage(&mapclutpos, (u_long *)(MapBitMaps + 512));
DrawSync(0);
}
// [D] [T]
void LoadMapTile(int tpage, int x, int y)
{
int idx, temp, count;
RECT16 MapSegment;
MapSegment.w = 8;
MapSegment.h = 32;
MapSegment.x = MapRect.x + (MapSegmentPos[tpage].x >> 2);
MapSegment.y = MapRect.y + MapSegmentPos[tpage].y;
idx = x + y * tilehnum;
temp = x * 32;
if (idx > -1 && idx < overlaidmaps[GameLevel].toptile &&
temp > -1 && (temp < overlaidmaps[GameLevel].width))
{
UnpackRNC(MapBitMaps + *((ushort*)MapBitMaps + idx), MapBuffer);
}
else
{
for (count = 0; count < 512; count++)
MapBuffer[count++] = overlaidmaps[GameLevel].dummy;
}
#ifdef PSX
DrawSync(0);
LoadImage(&MapSegment, (u_long*)MapBuffer);
DrawSync(0);
#else
LoadImage(&MapSegment, (u_long*)MapBuffer);
#endif
}
// [D] [T]
void SetMapPos(void)
{
int scale;
scale = overlaidmaps[GameLevel].scale;
x_map = overlaidmaps[GameLevel].x_offset + player[0].pos[0] / scale + 1;
y_map = overlaidmaps[GameLevel].y_offset - player[0].pos[2] / scale + 1;
}
// [D] [T]
void draw_box(int yPos, int h)
{
LINE_F4* linef4 = (LINE_F4*)current->primptr;
setLineF4(linef4);
linef4->r0 = 0;
linef4->g0 = 0;
linef4->b0 = 128;
linef4->x0 = gMapXOffset;
linef4->y0 = yPos;
linef4->x1 = gMapXOffset + MAP_SIZE_W;
linef4->x2 = gMapXOffset + MAP_SIZE_W;
linef4->y1 = yPos;
linef4->x3 = gMapXOffset;
linef4->y2 = yPos + h;
linef4->y3 = yPos + h;
addPrim(current->ot, linef4);
current->primptr += sizeof(LINE_F4);
LINE_F2* linef2 = (LINE_F2*)current->primptr;
setLineF2(linef2);
linef2->r0 = 0;
linef2->g0 = 0;
linef2->b0 = 128;
linef2->x0 = gMapXOffset;
linef2->y0 = yPos;
linef2->x1 = gMapXOffset;
linef2->y1 = yPos + h;
addPrim(current->ot, linef2);
current->primptr += sizeof(LINE_F2);
}
// [D] [T]
void DrawN(VECTOR *pScreenPosition, int direct)
{
int i;
LINE_F2 *linef2;
XYPAIR *pPoint;
XYPAIR lastPoint;
pPoint = NVertex;
i = 0;
lastPoint.x = NVertex[0].x + pScreenPosition->vx;
lastPoint.y = NVertex[0].y + pScreenPosition->vz;
do {
pPoint++;
linef2 = (LINE_F2 *)current->primptr;
setLineF2(linef2);
linef2->x0 = lastPoint.x;
linef2->y0 = lastPoint.y;
lastPoint.x = pPoint->x + pScreenPosition->vx;
lastPoint.y = pPoint->y + pScreenPosition->vz;
linef2->x1 = lastPoint.x;
linef2->y1 = lastPoint.y;
if (gTimeOfDay == 3)
{
linef2->r0 = 75;
linef2->g0 = 75;
linef2->b0 = 75;
}
else
{
linef2->r0 = 96;
linef2->g0 = 96;
linef2->b0 = 96;
}
setSemiTrans(linef2, 1);
linef2->code = linef2->code | 2;
if (direct == 0)
{
addPrim(current->ot + 1, linef2);
current->primptr += sizeof(LINE_F2);
}
else
{
DrawPrim(linef2);
}
i++;
} while (i < 3);
}
// [D] [T]
void DrawCompass(void)
{
int i;
LINE_G2 *lineg2;
VECTOR *pPosition;
OTTYPE *potz;
VECTOR position[5];
position[1].vx = north[GameLevel].x * overlaidmaps[GameLevel].scale;
position[1].vz = north[GameLevel].y * overlaidmaps[GameLevel].scale;
position[0].vx = position[1].vx * 20 + 8 >> 4;
position[2].vx = position[1].vx + 2 >> 2;
position[0].vz = position[1].vz * 20 + 8 >> 4;
position[2].vz = position[1].vz + 2 >> 2;
position[3].vx = (position[1].vz - position[1].vx) * 171 >> 10;
position[3].vz = (-position[1].vx - position[1].vz) * 171 >> 10;
position[4].vz = -position[3].vx;
position[4].vx = position[3].vz;
WorldToOverheadMapPositions(position, position, 3, 1, 0);
WorldToOverheadMapPositions(position + 3, position + 3, 2, 1, 1);
position[3].vx = position[3].vx + position[1].vx;
position[3].vz = position[3].vz + position[1].vz;
position[4].vx = position[4].vx + position[1].vx;
position[4].vz = position[4].vz + position[1].vz;
potz = current->ot + 1;
pPosition = position + 2;
for (i = 0; i < 3; i++)
{
lineg2 = (LINE_G2 *)current->primptr;
setLineG2(lineg2);
setSemiTrans(lineg2, 1);
lineg2->x0 = position[1].vx;
lineg2->y0 = position[1].vz;
lineg2->x1 = pPosition->vx;
lineg2->y1 = pPosition->vz;
lineg2->r0 = 96;
lineg2->g0 = 96;
lineg2->b0 = 96;
lineg2->r1 = 0;
lineg2->g1 = 0;
lineg2->b1 = 0;
addPrim(potz, lineg2);
current->primptr += sizeof(LINE_G2);
pPosition++;
}
DrawN(position, 0);
TransparencyOn(potz, 0x20);
}
// [D] [T]
void DrawBigCompass(VECTOR *root, int angle)
{
int i;
LINE_G2 *lineg2;
int ang, ang2, sn, cs;
VECTOR *pPosition;
VECTOR position[5];
ang = big_north[GameLevel] + angle & 0xfff;
position[2].vx = root->vx + map_x_offset;
position[2].vy = root->vz + map_z_offset;
sn = RSIN(ang);
cs = RCOS(ang);
position[1].vx = position[2].vx + (sn * 0x19 >> 0xc);
position[0].vx = position[2].vx + (sn * 0xf >> 0xb);
position[1].vy = position[2].vy + (cs * 0x19 >> 0xc);
position[0].vz = position[2].vy + (cs * 0xf >> 0xb);
ang2 = ang - 200 & 0xfff;
sn = RSIN(ang2);
cs = RCOS(ang2);
position[3].vx = position[2].vx + (sn * 5 >> 10);
position[3].vy = position[2].vy + (cs * 5 >> 10);
ang2 = ang + 200 & 0xfff;
sn = RSIN(ang2);
cs = RCOS(ang2);
position[4].vx = position[2].vx + (sn * 5 >> 10);
position[4].vy = position[2].vy + (cs * 5 >> 10);
pPosition = position + 2;
for (i = 0; i < 3; i++)
{
lineg2 = (LINE_G2*)current->primptr;
setLineG2(lineg2);
setSemiTrans(lineg2, 1);
lineg2->x0 = position[1].vx;
lineg2->y0 = position[1].vy;
lineg2->x1 = pPosition->vx;
lineg2->y1 = pPosition->vy;
lineg2->r0 = 96;
lineg2->g0 = 96;
lineg2->b0 = 96;
lineg2->r1 = 0;
lineg2->g1 = 0;
lineg2->b1 = 0;
DrawPrim(lineg2);
pPosition++;
//current->primptr += sizeof(LINE_G2);
}
DrawN(position, 1);
}
// [D] [T]
void CopIndicator(int xpos, int strength)
{
int startH, endH;
int str2;
POLY_F3 *poly;
startH = SCREEN_H;
endH = SCREEN_H - 30;
if (strength > 255)
strength = 255;
poly = (POLY_F3 *)current->primptr;
setPolyF3(poly);
setSemiTrans(poly, 1);
poly->r0 = strength;
str2 = (strength >> 2);
poly->g0 = str2;
poly->b0 = str2;
poly->x0 = xpos - 12;
poly->y0 = startH;
poly->x1 = xpos;
poly->y1 = endH;
poly->x2 = xpos + 12;
poly->y2 = startH;
addPrim(current->ot + 1, poly);
current->primptr += sizeof(POLY_F3);
TransparencyOn(current->ot + 1, 0x20);
poly = (POLY_F3 *)current->primptr;
setPolyF3(poly);
setSemiTrans(poly, 1);
str2 = strength * (strength + 256) >> 9;
poly->r0 = str2;
poly->g0 = str2;
poly->b0 = str2;
poly->x0 = xpos - 12;
poly->y0 = startH;
poly->y1 = endH;
poly->x2 = xpos + 12;
poly->y2 = startH;
poly->x1 = xpos;
addPrim(current->ot + 1, poly);
current->primptr += sizeof(POLY_F3);
TransparencyOn(current->ot + 1, 0x40);
}
// [D] [T]
void DrawSightCone(COP_SIGHT_DATA *pCopSightData, VECTOR *pPosition, int direction, int flags)
{
short temp;
int dir;
VECTOR *pNextVertex;
POLY_G3 *poly;
TILE_1* tile1;
VECTOR *pVertex;
VECTOR vertex[9];
int angle;
int frontViewAngle;
int frontViewDistance;
int surroundViewDistance;
frontViewDistance = pCopSightData->frontViewDistance;
surroundViewDistance = pCopSightData->surroundViewDistance;
frontViewAngle = pCopSightData->frontViewAngle;
vertex[0].vx = pPosition->vx;
vertex[0].vz = pPosition->vz;
pVertex = vertex + 1;
angle = 0;
do {
if (angle > frontViewAngle && angle < 4096 - frontViewAngle)
temp = surroundViewDistance;
else
temp = frontViewDistance;
dir = angle + direction & 0xfff;
angle += 512;
pVertex->vx = vertex[0].vx + FIXEDH(RSIN(dir) * temp);
pVertex->vz = vertex[0].vz + FIXEDH(RCOS(dir) * temp);
pVertex++;
} while (angle < 4096);
WorldToOverheadMapPositions(vertex, vertex, 9, 0, 0);
pVertex = vertex + 1;
while (pVertex < &vertex[9])
{
pNextVertex = pVertex + 1;
if (pNextVertex > vertex + 8)
pNextVertex = vertex + 1;
poly = (POLY_G3 *)current->primptr;
setPolyG3(poly);
setSemiTrans(poly, 1);
poly->x0 = vertex[0].vx;
poly->y0 = vertex[0].vz;
poly->x1 = pVertex->vx;
poly->y1 = pVertex->vz;
poly->x2 = pNextVertex->vx;
poly->y2 = pNextVertex->vz;
poly->r0 = poly->g0 = poly->b0 = 96;
poly->r1 = poly->g1 = poly->b1 = 0;
poly->r2 = poly->g2 = poly->b2 = 0;
addPrim(current->ot + 1, poly);
current->primptr += sizeof(POLY_G3);
pVertex++;
}
// draw center
if(flags & 0x1)
{
tile1 = (TILE_1*)current->primptr;
setTile1(tile1);
tile1->r0 = tile1->g0 = tile1->b0 = (CameraCnt & 7) > 3 ? 255 : 0;
tile1->x0 = vertex[0].vx;
tile1->y0 = vertex[0].vz;
addPrim(current->ot + 1, tile1);
current->primptr += sizeof(TILE_1);
}
TransparencyOn(current->ot + 1, 0x20);
}
// [D] [T]
u_int Long2DDistance(VECTOR *pPoint1, VECTOR *pPoint2)
{
int tempTheta;
int theta;
u_int result;
VECTOR delta;
delta.vx = ABS(pPoint1->vx - pPoint2->vx);
delta.vz = ABS(pPoint1->vz - pPoint2->vz);
theta = ratan2(delta.vz, delta.vx);
if ((theta & 0x7ff) - 512U <= 1024)
{
tempTheta = RSIN(theta);
result = delta.vz;
}
else
{
tempTheta = RCOS(theta);
result = delta.vx;
}
if (result < 0x80000)
result = (result << 12) / tempTheta;
else
result = (result << 9) / tempTheta << 3;
return result;
}
// [D] [T]
void InitMultiplayerMap(void)
{
RECT16 rect;
char filename[32];
if (MissionHeader->region == 0)
return;
// multiplayer maps loaded externally
sprintf(filename, "MAPS\\REG%d.%d", MissionHeader->region, GameLevel);
if (FileExists(filename))
{
Loadfile(filename, MapBitMaps);
rect.x = MapRect.x + MapSegmentPos[0].x / 4;
rect.y = MapRect.y + MapSegmentPos[0].y;
rect.w = 16;
rect.h = 64;
LoadImage(&rect, (u_long *)MapBitMaps);
DrawSync(0);
}
}
// [D] [T]
void InitOverheadMap(void)
{
int c, d;
int tpage;
if (NumPlayers > 1)
gMapYOffset = (SCREEN_H - MAP_SIZE_H) / 2 - 2;// 96;
else
gMapYOffset = SCREEN_H - MAP_SIZE_H - 15; //181;
if (gMultiplayerLevels)
{
InitMultiplayerMap();
return;
}
SetMapPos();
tilehnum = overlaidmaps[GameLevel].width / 32;
tpage = 0;
for (c = 0; c < 4; c++)
{
for (d = 0; d < 4; d++)
{
maptile[d][c] = tpage;
LoadMapTile(tpage, (x_map >> 5) + d, (y_map >> 5) + c);
tpage++;
}
}
old_x_mod = x_map & 0x1f;
old_y_mod = y_map & 0x1f;
}
// [D] [T]
void FlashOverheadMap(int r, int g, int b)
{
TILE *prim;
POLY_FT3 *null;
prim = (TILE *)current->primptr;
setTile(prim);
setSemiTrans(prim, 1);
prim->r0 = r;
prim->g0 = g;
prim->b0 = b;
prim->x0 = gMapXOffset;
prim->y0 = gMapYOffset;
prim->w = MAP_SIZE_W;
prim->h = MAP_SIZE_H;
addPrim(current->ot + 1, prim);
current->primptr += sizeof(TILE);
null = (POLY_FT3 *)current->primptr;
setPolyFT3(null);
null = (POLY_FT3 *)current->primptr;
setPolyFT3(null);
null->x0 = -1;
null->y0 = -1;
null->x1 = -1;
null->y1 = -1;
null->x2 = -1;
null->y2 = -1;
null->tpage = 0x20; // [A] correct me if I'm wrong
addPrim(current->ot + 1, null);
current->primptr += sizeof(POLY_FT3);
}
// [D] [T]
void DrawMultiplayerMap(void)
{
POLY_FT4 *poly;
PLAYER *pl;
int i;
u_char g;
u_char r;
int xPos, yPos;
VECTOR target;
int px, py;
map_x_offset = 0;
map_z_offset = 0;
xPos = gMapXOffset;
yPos = gMapYOffset;
DrawMultiplayerTargets();
r = 255;
g = 0;
for (i = 0; i < NumPlayers; i++)
{
pl = &player[i];
target.vx = pl->pos[0];
target.vz = pl->pos[2];
target.vy = 0;
WorldToMultiplayerMap(&target, &target);
target.vx += xPos;
target.vz += yPos;
DrawPlayerDot(&target, -pl->dir, r, g, 0, 0x8);
r++;
g--;
}
draw_box(yPos, 64);
// draw map image
poly = (POLY_FT4 *)current->primptr;
setPolyFT4(poly);
setSemiTrans(poly, 1);
poly->x0 = xPos;
poly->y0 = yPos;
poly->x1 = xPos + MAP_SIZE_W;
poly->y1 = yPos;
poly->x2 = xPos;
poly->x3 = xPos + MAP_SIZE_W;
poly->y2 = yPos + 64;
poly->y3 = yPos + 64;
px = MapSegmentPos[0].x;
py = MapSegmentPos[0].y;
poly->u0 = px;
poly->v0 = py;
poly->u1 = px + 63;
poly->v1 = py;
poly->u2 = px;
poly->v2 = py + 63;
poly->u3 = px + 63;
poly->v3 = py + 63;
poly->clut = MapClut;
poly->tpage = MapTPage;
if (gTimeOfDay == 3)
r = 50;
else
r = 100;
poly->r0 = poly->g0 = poly->b0 = r;
addPrim(current->ot + 1, poly);
current->primptr += sizeof(POLY_FT4);
}
// [D] [T]
void DrawOverheadMap(void)
{
u_char tmp;
short *playerFelony;
TILE_1 *tile1;
POLY_F4 *sptb;
POLY_FT4 *spt;
POLY_FT3 *null;
CAR_DATA *cp;
DR_AREA *drarea;
int intens;
SVECTOR MapMesh[5][5];
VECTOR MapMeshO[5][5];
MAPTEX MapTex[4];
SVECTOR direction;
RECT16 clipped_size;
VECTOR vec;
long flag;
int i, j;
int tw, th;
int x_mod, y_mod;
int MeshWidth, MeshHeight;
int map_minX, map_maxX;
int map_minY, map_maxY;
static int flashtimer = 0;
static u_char ptab[] = {
255, 240, 170, 120,
80, 55, 38, 23,
13, 10, 0, 0,
0, 0, 0, 0,
};
static u_char ptab2[] = {
255, 255, 240, 170,
120, 80, 55, 38,
23, 13, 10, 0,
0
};
if (MissionHeader->region != 0)
{
DrawMultiplayerMap();
return;
}
if (NumPlayers > 1)
return;
map_minX = gMapXOffset;
map_minY = gMapYOffset;
map_maxX = map_minX + MAP_SIZE_W;
map_maxY = map_minY + MAP_SIZE_H;
VECTOR translate = {
map_minX + MAP_SIZE_W/2,
0,
map_minY + MAP_SIZE_H/2
};
SetMapPos();
draw_box(map_minY, MAP_SIZE_H);
// flash the overhead map
if (player_position_known > 0)
{
if (player[0].playerCarId < 0)
playerFelony = &pedestrianFelony;
else
playerFelony = &car_data[player[0].playerCarId].felonyRating;
if (*playerFelony > FELONY_PURSUIT_MIN_VALUE)
FlashOverheadMap(ptab[CameraCnt & 0xf], 0, ptab[CameraCnt + 8U & 0xf]);
}
else
{
if (player_position_known == -1)
{
if (flashtimer == 0)
{
if (player[0].playerCarId < 0)
playerFelony = &pedestrianFelony;
else
playerFelony = &car_data[player[0].playerCarId].felonyRating;
if (*playerFelony > FELONY_PURSUIT_MIN_VALUE)
flashtimer = 48;
}
}
if (flashtimer)
{
flashtimer--;
intens = -flashtimer;
intens = ptab2[intens + 47 >> 2] + ptab2[intens + 48 >> 2] + ptab2[intens + 49 >> 2] + ptab2[intens + 50 >> 2] >> 2;
FlashOverheadMap(intens, intens, intens);
}
}
// for restoring
drarea = (DR_AREA *)current->primptr;
SetDrawArea(drarea, &current->draw.clip);
addPrim(current->ot + 1, drarea);
current->primptr += sizeof(DR_AREA);
WorldToOverheadMapPositions((VECTOR *)player->pos, &vec, 1, 0, 0);
// draw map center
if (vec.vx > map_minX && vec.vx < map_maxX &&
vec.vz > map_minY && vec.vz < map_maxY)
{
tile1 = (TILE_1 *)current->primptr;
setTile1(tile1);
tile1->r0 = 255;
tile1->g0 = 255;
tile1->b0 = 255;
tile1->x0 = vec.vx;
tile1->y0 = vec.vz;
addPrim(current->ot, tile1);
current->primptr += sizeof(TILE_1);
}
DrawTargetBlip((VECTOR *)player->pos, 64, 64, 64, 3);
DrawCompass();
DrawOverheadTargets();
// draw cop sight
cp = car_data;
do {
if (cp->controlType == CONTROL_TYPE_PURSUER_AI && cp->ai.p.dying == 0 || (cp->controlFlags & CONTROL_FLAG_COP))
DrawSightCone(&copSightData, (VECTOR *)cp->hd.where.t, cp->hd.direction, cp->controlType == CONTROL_TYPE_PURSUER_AI);
cp++;
} while (cp <= &car_data[MAX_CARS]);
x_mod = x_map & 31;
y_mod = y_map & 31;
// next code loads map tiles depending on player position changes
// X axis
if (x_mod < 16 && old_x_mod > 16)
{
// left
for (i = 0; i < 4; i++)
{
tmp = maptile[0][i];
maptile[0][i] = maptile[1][i];
maptile[1][i] = maptile[2][i];
maptile[2][i] = maptile[3][i];
maptile[3][i] = tmp;
LoadMapTile(tmp, (x_map >> 5) + 3, (y_map >> 5) + i);
}
}
if (x_mod > 16 && old_x_mod < 16)
{
// right
for (i = 0; i < 4; i++)
{
tmp = maptile[3][i];
maptile[3][i] = maptile[2][i];
maptile[2][i] = maptile[1][i];
maptile[1][i] = maptile[0][i];
maptile[0][i] = tmp;
LoadMapTile(tmp, (x_map >> 5), (y_map >> 5) + i);
}
}
// Z axis
if (y_mod < 16 && old_y_mod > 16)
{
// down
for (i = 0; i < 4; i++)
{
tmp = maptile[i][0];
maptile[i][0] = maptile[i][1];
maptile[i][1] = maptile[i][2];
maptile[i][2] = maptile[i][3];
maptile[i][3] = tmp;
LoadMapTile(tmp, (x_map >> 5) + i, (y_map >> 5) + 3);
}
}
if (y_mod > 16 && old_y_mod < 16)
{
// up
for (i = 0; i < 4; i++)
{
tmp = maptile[i][3];
maptile[i][3] = maptile[i][2];
maptile[i][2] = maptile[i][1];
maptile[i][1] = maptile[i][0];
maptile[i][0] = tmp;
LoadMapTile(tmp, (x_map >> 5) + i, (y_map >> 5));
}
}
old_x_mod = x_mod;
old_y_mod = y_mod;
// make grid coordinates
for (i = 0; i < 5; i++)
{
MapMesh[0][i].vx = -44;
MapMesh[i][0].vz = -44;
MapMesh[1][i].vx = -x_mod - 16;
MapMesh[i][1].vz = -y_mod - 16;
MapMesh[2][i].vx = -x_mod + 16;
MapMesh[i][2].vz = -y_mod + 16;
MapMesh[3][i].vx = -x_mod + 48;
MapMesh[i][3].vz = -y_mod + 48;
MapMesh[4][i].vx = 44;
MapMesh[i][4].vz = 44;
}
MapTex[1].w = MapTex[2].w = tile_size;
MapTex[1].u = MapTex[2].u = MapTex[3].u = 0;
MapTex[1].h = MapTex[2].h = tile_size;
MapTex[1].v = MapTex[2].v = MapTex[3].v = 0;
MapTex[0].u = 32 - ABS(MapMesh[0][0].vx - MapMesh[1][0].vx);
MapTex[0].w = ABS(MapMesh[0][0].vx - MapMesh[1][0].vx);
MapTex[3].w = ABS(MapMesh[3][0].vx - MapMesh[4][0].vx);
MapTex[0].v = 32 - ABS(MapMesh[0][0].vz - MapMesh[0][1].vz);
MapTex[0].h = ABS(MapMesh[0][0].vz - MapMesh[0][1].vz);
MapTex[3].h = ABS(MapMesh[0][3].vz - MapMesh[0][4].vz);
direction.vx = 0;
direction.vz = 0;
direction.vy = player[0].dir & 0xfff;
InitMatrix(map_matrix);
_RotMatrixY(&map_matrix, player[0].dir & 0xfff);
gte_SetRotMatrix(&map_matrix);
gte_SetTransVector(&translate);
MeshWidth = x_mod ? 4 : 3;
MeshHeight = y_mod ? 4 : 3;
// transform the map mesh
for (i = 0; i <= MeshWidth; i++)
{
for(j = 0; j <= MeshHeight; j++)
{
RotTrans(&MapMesh[i][j], &MapMeshO[i][j], &flag);
}
}
// draw map mesh booooi
for (i = 0; i < MeshHeight; i++)
{
for (j = 0; j < MeshWidth; j++)
{
int tile, px, py;
tile = maptile[j][i];
tw = MapTex[j].w - 1;
th = MapTex[i].h - 1;
#ifndef PSX
// make map fully detailed when filtering is not available
if (!g_cfg_bilinearFiltering)
{
tw += 1;
th += 1;
}
#endif
spt = (POLY_FT4*)current->primptr;
setPolyFT4(spt);
setSemiTrans(spt, 1);
if (gTimeOfDay == 3)
spt->r0 = spt->g0 = spt->b0 = 50;
else
spt->r0 = spt->g0 = spt->b0 = 100;
spt->clut = MapClut;
spt->tpage = MapTPage;
spt->x0 = MapMeshO[j][i].vx;
spt->y0 = MapMeshO[j][i].vz;
spt->x1 = MapMeshO[j+1][i].vx;
spt->y1 = MapMeshO[j+1][i].vz;
spt->x2 = MapMeshO[j][i+1].vx;
spt->y2 = MapMeshO[j][i+1].vz;
spt->x3 = MapMeshO[j+1][i+1].vx;
spt->y3 = MapMeshO[j+1][i+1].vz;
px = MapSegmentPos[tile].x;
py = MapSegmentPos[tile].y;
spt->u0 = MIN(255, MapTex[j].u + px);
spt->v0 = MIN(255, MapTex[i].v + py);
spt->u1 = MIN(255, MapTex[j].u + px + tw);
spt->v1 = MIN(255, MapTex[i].v + py);
spt->u2 = MIN(255, MapTex[j].u + px);
spt->v2 = MIN(255, MapTex[i].v + py + th);
spt->u3 = MIN(255, MapTex[j].u + px + tw);
spt->v3 = MIN(255, MapTex[i].v + py + th);
addPrim(current->ot + 1, spt);
current->primptr += sizeof(POLY_FT4);
}
}
sptb = (POLY_F4 *)current->primptr;
setPolyF4(sptb);
setSemiTrans(sptb, 1);
sptb->r0 = 60;
sptb->g0 = 60;
sptb->b0 = 60;
sptb->y0 = map_minY;
sptb->y1 = map_minY;
sptb->x0 = map_minX;
sptb->x1 = map_maxX;
sptb->x2 = map_minX;
sptb->y2 = map_maxY;
sptb->x3 = map_maxX;
sptb->y3 = map_maxY;
addPrim(current->ot + 1, sptb);
current->primptr += sizeof(POLY_F4);
null = (POLY_FT3 *)current->primptr;
setPolyFT3(null);
null->x0 = -1;
null->y0 = -1;
null->x1 = -1;
null->y1 = -1;
null->x2 = -1;
null->y2 = -1;
null->tpage = 0;
addPrim(current->ot + 1, null);
current->primptr += sizeof(POLY_FT3);
clipped_size.x = map_minX + 1;
clipped_size.w = MAP_SIZE_W - 1;
clipped_size.h = MAP_SIZE_H;
clipped_size.y = current->draw.clip.y + map_minY;// +1;
drarea = (DR_AREA*)current->primptr;
SetDrawArea(drarea, &clipped_size);
addPrim(current->ot + 1, drarea);
current->primptr += sizeof(DR_AREA);
}
// [D] [T]
void SetFullscreenMapMatrix(void)
{
VECTOR translate = { 160, 0, 128 };
int direction;
if (gUseRotatedMap == 0)
direction = 0;
else
direction = player[0].dir & 0xfff;
InitMatrix(map_matrix);
_RotMatrixY(&map_matrix, direction);
gte_SetRotMatrix(&map_matrix);
gte_SetTransVector(&translate);
}
// [D] [T]
void DrawFullscreenMap(void)
{
char str[64];
TILE *polys;
TILE_1 *tile1;
POLY_FT4 *back, *prevback;
POLY_FT3 *null;
int x, y;
int clipped;
int px, py;
SVECTOR mesh[4];
VECTOR meshO[4];
VECTOR target;
VECTOR vec;
long flag;
int width, height;
int ntiles, count;
int tw, th;
// toggle rotated map
if (Pads[0].dirnew & 0x20)
{
map_x_offset = 0;
map_z_offset = 0;
map_x_shift = 0;
map_z_shift = 0;
gUseRotatedMap ^= 1;
}
SetFullscreenMapMatrix();
tw = tile_size - 1;
th = tile_size - 1;
#ifndef PSX
/* It will look funny when enabled
// make map fully detailed when filtering is not available
if (!g_cfg_bilinearFiltering)
{
tw += 1;
th += 1;
}
*/
#else
polys = (TILE*)current->primptr;
setTile(polys);
polys->r0 = 0;
polys->g0 = 0;
polys->b0 = 0;
polys->x0 = 0;
polys->y0 = 0;
polys->w = 320;
polys->h = 256;
DrawPrim(polys);
DrawSync(0);
#endif
width = overlaidmaps[GameLevel].width;
height = overlaidmaps[GameLevel].height;
WorldToFullscreenMap((VECTOR *)player->pos, &player_position);
// do map movement
if (gUseRotatedMap)
{
if (Pads[0].direct & 0x8000)
map_x_offset += 8;
else if (Pads[0].direct & 0x2000)
map_x_offset -= 8;
if (Pads[0].direct & 0x1000)
map_z_offset += 8;
else if (Pads[0].direct & 0x4000)
map_z_offset -= 8;
if (map_x_offset < -128)
map_x_offset = -128;
if (map_x_offset > 128)
map_x_offset = 128;
if (map_z_offset < -128)
map_z_offset = -128;
if (map_z_offset > 128)
map_z_offset = 128;
map_x_shift = 0;
map_z_shift = 0;
}
else
{
if(player_position.vx + 160 <= width)
{
map_x_offset = player_position.vx - 160;
if(map_x_offset > -1)
map_x_offset = 0;
}
else
{
map_x_offset = (player_position.vx + 160) - width;
}
if(player_position.vz + 128 <= height)
{
map_z_offset = player_position.vz - 128;
if (map_z_offset > -1)
map_z_offset = 0;
}
else
{
map_z_offset = (player_position.vz + 128) - height;
}
if (Pads[0].direct & 0x8000)
{
if (player_position.vx - map_x_offset - 160 - map_x_shift > 0)
{
map_x_shift += 8;
}
}
else if(Pads[0].direct & 0x2000)
{
if (player_position.vx - map_x_offset + 160 - map_x_shift < width)
{
map_x_shift -= 8;
}
}
if (Pads[0].direct & 0x1000)
{
if (player_position.vz - map_z_offset - 128 - map_z_shift > 0)
{
map_z_shift += 8;
}
}
else if(Pads[0].direct & 0x4000)
{
if (player_position.vz - map_z_offset + 128 - map_z_shift < height)
{
map_z_shift -= 8;
}
}
map_x_offset += map_x_shift;
map_z_offset += map_z_shift;
}
null = (POLY_FT3 *)current->primptr;
setPolyFT3(null);
null->x0 = -1;
null->y0 = -1;
null->x1 = -1;
null->y1 = -1;
null->x2 = -1;
null->y2 = -1;
null->tpage = MapTPage;
DrawPrim(null);
DrawSync(0);
width >>= 5;
height >>= 5;
#ifndef PSX
RECT16 emuViewport;
PsyX_GetPSXWidescreenMappedViewport(&emuViewport);
prevback = NULL;
#endif
ntiles = 0;
for(x = 0; x < width; x++)
{
for(y = 0; y < height; y++)
{
mesh[0].vy = 0;
mesh[1].vy = 0;
mesh[2].vy = 0;
mesh[3].vy = 0;
mesh[0].vx = x * 32 - player_position.vx;
mesh[1].vx = mesh[0].vx + 32;
mesh[0].vz = y * 32 - player_position.vz;
mesh[2].vz = mesh[0].vz + 32;
mesh[1].vz = mesh[0].vz;
mesh[2].vx = mesh[0].vx;
mesh[3].vx = mesh[1].vx;
mesh[3].vz = mesh[2].vz;
// rotate and clip polygons
clipped = 0;
for (count = 0; count < 4; count++)
{
RotTrans(&mesh[count], &meshO[count], &flag);
meshO[count].vx += map_x_offset;
meshO[count].vz += map_z_offset;
#ifndef PSX
if (meshO[count].vx > emuViewport.w || meshO[count].vz > emuViewport.w ||
meshO[count].vx < emuViewport.x || meshO[count].vz < emuViewport.y)
{
clipped++;
}
#else
if (meshO[count].vx > 320 || meshO[count].vz > 256 ||
meshO[count].vx < 0 || meshO[count].vz < 0)
{
clipped++;
}
#endif
}
if(clipped == 4)
continue;
LoadMapTile(ntiles & 15, x, y);
back = (POLY_FT4 *)current->primptr;
setPolyFT4(back);
back->r0 = back->g0 = back->b0 = 88;
back->x0 = meshO[0].vx;
back->y0 = meshO[0].vz;
back->x1 = meshO[1].vx;
back->y1 = meshO[1].vz;
back->x2 = meshO[2].vx;
back->y2 = meshO[2].vz;
back->x3 = meshO[3].vx;
back->y3 = meshO[3].vz;
px = MapSegmentPos[ntiles & 15].x;
py = MapSegmentPos[ntiles & 15].y;
back->u0 = px;
back->v0 = py;
back->u1 = MIN(255, px + tw);
back->v1 = MIN(255, py);
back->u2 = px;
back->v2 = MIN(255, py + th);
back->u3 = MIN(255, px + tw);
back->v3 = MIN(255, py + th);
back->clut = MapClut;
back->tpage = MapTPage;
#ifdef PSX
DrawPrim(back);
DrawSync(0);
ntiles++;
#else
current->primptr += sizeof(POLY_FT4);
if (!prevback)
prevback = back;
// draw if map tiles are filled or we reached horizontal end
if ((ntiles & 15) == 0 || x == width - 1 || y == height - 1)
{
// upload map to GPU
DrawSync(0);
// draw all polys
while(prevback <= back)
DrawPrim(prevback++);
DrawSync(0);
}
ntiles++;
#endif
}
}
target.vx = 160;
target.vz = 128;
DrawFullscreenTargets();
if (gUseRotatedMap)
DrawBigCompass(&target, player[0].dir);
else
DrawBigCompass(&target, 0);
DrawTargetBlip(&target, 64, 64, 64, 14);
vec.vx = target.vx + map_x_offset;
vec.vz = target.vz + map_z_offset;
tile1 = (TILE_1 *)current->primptr;
setTile1(tile1);
tile1->r0 = -1;
tile1->g0 = -1;
tile1->b0 = -1;
tile1->x0 = vec.vx;
tile1->y0 = vec.vz;
DrawPrim(tile1);
null = (POLY_FT3 *)current->primptr;
setPolyFT3(null);
null->x0 = -1;
null->y0 = -1;
null->x1 = -1;
null->y1 = -1;
null->x2 = -1;
null->y2 = -1;
null->tpage = fonttpage;
DrawPrim(null);
DrawSync(0);
SetTextColour(128, 128, 128);
// print string with special characters representing some images inserted into it
sprintf(str, "\x80 %s \x81 %s \x8a %s", G_LTXT(GTXT_Exit), G_LTXT(GTXT_Rotation), G_LTXT(GTXT_Move));
PrintStringCentred(str, SCREEN_H - 30); // 226
}
// [D] [T]
void DrawCopIndicators(void)
{
int cc, cs;
int dx, dz;
int p, q;
CAR_DATA *cp;
cc = RCOS(player[0].dir);
cs = RSIN(player[0].dir);
cp = car_data;
do {
if (cp->controlType == CONTROL_TYPE_PURSUER_AI && cp->ai.p.dying == 0)
{
dx = cp->hd.where.t[0] - player[0].pos[0];
dz = cp->hd.where.t[2] - player[0].pos[2];
p = FIXEDH(dx * cc - dz * cs) * 3 >> 2;
q = -FIXEDH(dx * cs + dz * cc);
if (ABS(p) < q)
{
CopIndicator((p * 266) / q + 160, 0x3fff0 / ((q >> 3) + 600));
}
}
cp++;
} while (cp <= &car_data[MAX_CARS]);
}
// [D] [T]
void WorldToMultiplayerMap(VECTOR *in, VECTOR *out)
{
int z;
int x;
if (MissionHeader->region == 0)
{
out->vx = 32;
out->vz = 32;
return;
}
x = in->vx - (((MissionHeader->region % regions_across) << 16) - cells_across * MAP_CELL_SIZE/2);
z = in->vz - (((MissionHeader->region / regions_across) << 16) - cells_down * MAP_CELL_SIZE/2);
out->vx = x / MAP_CELL_SIZE;
out->vz = MAP_REGION_SIZE - z / MAP_CELL_SIZE;
}
// [D] [T]
void WorldToOverheadMapPositions(VECTOR *pGlobalPosition, VECTOR *pOverheadMapPosition, int count, char inputRelative,int outputRelative)
{
int scale, cs, sn;
MATRIX tempMatrix;
SVECTOR tempVector;
XZPAIR playerPos;
long flag;
cs = RCOS(player[0].dir);
sn = RSIN(player[0].dir);
tempMatrix.m[0][1] = 0;
tempMatrix.m[1][0] = 0;
tempMatrix.m[1][2] = 0;
tempMatrix.m[2][1] = 0;
tempMatrix.m[0][0] = cs;
tempMatrix.m[2][2] = cs;
tempMatrix.m[2][0] = -sn;
tempMatrix.m[0][2] = sn;
tempMatrix.t[1] = 0;
if (outputRelative == 0)
{
tempMatrix.t[0] = gMapXOffset + MAP_SIZE_W/2;
tempMatrix.t[2] = gMapYOffset + MAP_SIZE_H/2;
}
else
{
tempMatrix.t[0] = 0;
tempMatrix.t[2] = 0;
}
gte_SetRotMatrix(&tempMatrix);
gte_SetTransMatrix(&tempMatrix);
scale = overlaidmaps[GameLevel].scale;
playerPos.x = player[0].pos[0];
playerPos.z = player[0].pos[2];
count--;
while (count >= 0)
{
tempVector.vy = 0;
if (inputRelative)
{
tempVector.vx = pGlobalPosition->vx / scale;
tempVector.vz = -pGlobalPosition->vz / scale;
}
else
{
tempVector.vx = (pGlobalPosition->vx - playerPos.x) / scale;
tempVector.vz = (playerPos.z - pGlobalPosition->vz) / scale;
}
RotTrans(&tempVector, pOverheadMapPosition, &flag);
// might be faster on OG hardware... but not sure.
// pOverheadMapPosition->vx = tempMatrix.t[0] + (cs * tempVector.vx + sn * tempVector.vz >> 12);
// pOverheadMapPosition->vz = tempMatrix.t[2] + (cs * tempVector.vz - sn * tempVector.vx >> 12);
count--;
pGlobalPosition++;
pOverheadMapPosition++;
}
}
// [D] [T]
void WorldToFullscreenMap(VECTOR *in, VECTOR *out)
{
int scale;
scale = overlaidmaps[GameLevel].scale;
out->vy = 0;
out->vx = overlaidmaps[GameLevel].x_offset + in->vx / scale + 49;
out->vz = overlaidmaps[GameLevel].y_offset - (in->vz / scale - 49);
}
// [D] [T]
void WorldToFullscreenMap2(VECTOR *in, VECTOR *out)
{
SVECTOR pos;
long flag;
int scale;
scale = overlaidmaps[GameLevel].scale;
pos.vy = 0;
pos.vx = overlaidmaps[GameLevel].x_offset + (in->vx / scale + 49) - player_position.vx;
pos.vz = overlaidmaps[GameLevel].y_offset - (in->vz / scale - 49) - player_position.vz;
RotTrans(&pos, out, &flag);
}