1 
2 #include "nx.h"
3 #include "map.h"
4 #include "map.fdh"
5 #include "libretro_shared.h"
6 #include "../extract-auto/cachefiles.h"
7 
8 stMap map;
9 
10 extern MapRecord stages[MAX_STAGES];
11 int num_stages;
12 
13 #define MAX_BACKDROPS			32
14 NXSurface *backdrop[MAX_BACKDROPS];
15 
16 // for FindObject--finding NPC's by ID2
17 Object *ID2Lookup[65536];
18 
19 uint8_t tilecode[MAX_TILES];			// tile codes for every tile in current tileset
20 uint32_t tileattr[MAX_TILES];			// tile attribute bits for every tile in current tileset
21 uint32_t tilekey[MAX_TILES] = {0, 0, 64, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 39, 48, 103, 34, 32, 33, 32, 32, 32, 32, 32, 33, 33, 33, 33, 544, 544, 544, 544, 544, 544, 544, 544, 32, 32, 32, 32, 32, 32, 32, 32, 160, 39, 176, 32, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 672, 672, 672, 672, 672, 672, 672, 672, 32, 32, 32, 32, 32, 32, 32, 32, 256, 256, 256, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 416, 416, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // mapping from tile codes -> tile attributes
22 
23 
24 // load stage "stage_no", this entails loading the map (pxm), enemies (pxe), tileset (pbm),
25 // tile attributes (pxa), and script (tsc).
load_stage(int stage_no)26 bool load_stage(int stage_no)
27 {
28 char stage[MAXPATHLEN];
29 char fname[MAXPATHLEN];
30 
31 char slash;
32 #ifdef _WIN32
33 slash = '\\';
34 #else
35 slash = '/';
36 #endif
37 
38 	NX_LOG(" >> Entering stage %d: '%s'.\n", stage_no, stages[stage_no].stagename);
39 	game.curmap = stage_no;		// do it now so onspawn events will have it
40 
41 	if (Tileset::Load(stages[stage_no].tileset))
42 		return 1;
43 
44 	// get the base name of the stage without extension
45 	const char *mapname = stages[stage_no].filename;
46 	if (!strcmp(mapname, "lounge")) mapname = "Lounge";
47 	snprintf(stage, sizeof(stage), "%s%c%s", stage_dir, slash, mapname);
48 
49 	snprintf(fname, sizeof(fname), "%s.pxm", stage);
50 	if (load_map(fname)) return 1;
51 
52 	snprintf(fname, sizeof(fname), "%s%c%s.pxa", stage_dir, slash, tileset_names[stages[stage_no].tileset]);
53 	if (load_tileattr(fname)) return 1;
54 
55 	snprintf(fname, sizeof(fname), "%s.pxe", stage);
56 	if (load_entities(fname)) return 1;
57 
58 	snprintf(fname, sizeof(fname), "%s.tsc", stage);
59 	if (tsc_load(fname, SP_MAP) == -1) return 1;
60 
61 	map_set_backdrop(stages[stage_no].bg_no);
62 	map.scrolltype = stages[stage_no].scroll_type;
63 	map.motionpos = 0;
64 
65    //hack to show nice backdrop in menu, like nicalis
66    stages[0].bg_no=9;
67    stages[0].scroll_type=BK_FASTLEFT_LAYERS;
68 
69 	return 0;
70 }
71 
72 /*
73 void c------------------------------() {}
74 */
75 
76 // load a PXM map
load_map(const char * fname)77 bool load_map(const char *fname)
78 {
79 	CFILE *fp;
80 	int x, y;
81 
82    NX_LOG("load_map: %s\n", fname);
83 
84 	fp = copen(fname, "rb");
85 	if (!fp)
86 	{
87 		NX_ERR("load_map: no such file: '%s'\n", fname);
88 		return 1;
89 	}
90 
91 	if (!cverifystring(fp, "PXM"))
92 	{
93 		NX_ERR("load_map: invalid map format: '%s'\n", fname);
94 		return 1;
95 	}
96 
97 	memset(&map, 0, sizeof(map));
98 
99 	cgetc(fp);
100 	map.xsize = cgeti(fp);
101 	map.ysize = cgeti(fp);
102 
103 	if (map.xsize > MAP_MAXSIZEX || map.ysize > MAP_MAXSIZEY)
104 	{
105 		NX_ERR("load_map: map is too large -- size %dx%d but max is %dx%d\n", map.xsize, map.ysize, MAP_MAXSIZEX, MAP_MAXSIZEY);
106 		cclose(fp);
107 		return 1;
108 	}
109 	else
110 	{
111 		NX_LOG("load_map: level size %dx%d\n", map.xsize, map.ysize);
112 	}
113 
114 	for(y=0;y<map.ysize;y++)
115 	for(x=0;x<map.xsize;x++)
116 	{
117 		map.tiles[x][y] = cgetc(fp);
118 	}
119 
120 	cclose(fp);
121 
122 	map.maxxscroll = (((map.xsize * TILE_W) - SCREEN_WIDTH) - 8) << CSF;
123 	map.maxyscroll = (((map.ysize * TILE_H) - SCREEN_HEIGHT) - 8) << CSF;
124 
125 	NX_LOG("load_map: '%s' loaded OK! - %dx%d\n", fname, map.xsize, map.ysize);
126 	return 0;
127 }
128 
129 
130 // load a PXE (entity list for a map)
load_entities(const char * fname)131 bool load_entities(const char *fname)
132 {
133 CFILE *fp;
134 int i;
135 int nEntities;
136 
137 	// gotta destroy all objects before creating new ones
138 	Objects::DestroyAll(false);
139 	FloatText::ResetAll();
140 
141 	NX_LOG("load_entities: reading in %s\n", fname);
142 	// now we can load in the new objects
143 	fp = copen(fname, "rb");
144 	if (!fp)
145 	{
146 		NX_ERR("load_entities: no such file: '%s'\n", fname);
147 		return 1;
148 	}
149 
150 	if (!cverifystring(fp, "PXE"))
151 	{
152 		NX_ERR("load_entities: not a PXE: '%s'\n", fname);
153 		return 1;
154 	}
155 
156 	cgetc(fp);
157 	nEntities = cgetl(fp);
158 
159 	for(i=0;i<nEntities;i++)
160 	{
161 		int x = cgeti(fp);
162 		int y = cgeti(fp);
163 		int id1 = cgeti(fp);
164 		int id2 = cgeti(fp);
165 		int type = cgeti(fp);
166 		int flags = cgeti(fp);
167 
168 		int dir = (flags & FLAG_FACES_RIGHT) ? RIGHT : LEFT;
169 
170 		//lprintf(" %d:   [%d, %d]\t id1=%d\t id2=%d   Type %d   flags %04x\n", i, x, y, id1, id2, type, flags);
171 
172 		// most maps have apparently garbage entities--invisible do-nothing objects??
173 		// i dunno but no point in spawning those...
174 		if (type || id1 || id2 || flags)
175 		{
176 			bool addobject = false;
177 
178 			// check if object is dependent on a flag being set/not set
179 			if (flags & FLAG_APPEAR_ON_FLAGID)
180 			{
181 				if (game.flags[id1])
182 					addobject = true;
183 			}
184 			else if (flags & FLAG_DISAPPEAR_ON_FLAGID)
185 			{
186 				if (!game.flags[id1])
187 					addobject = true;
188 			}
189 			else
190 				addobject = true;
191 
192 			if (addobject)
193 			{
194 				// hack for chests (can we do this elsewhere?)
195 				if (type == OBJ_CHEST_OPEN) y++;
196             // hack for skydragon in Fall end cinematic
197 				if (type == OBJ_SKY_DRAGON && id2 == 230) y++;
198 
199 				Object *o = CreateObject((x * TILE_W) << CSF, \
200 										 (y * TILE_H) << CSF, type,
201 										 0, 0, dir, NULL, CF_NO_SPAWN_EVENT);
202 
203 				o->id1 = id1;
204 				o->id2 = id2;
205 				o->flags |= flags;
206 
207 				ID2Lookup[o->id2] = o;
208 
209 				// now that it's all set up, execute OnSpawn,
210 				// since we didn't do it in CreateObject.
211 				o->OnSpawn();
212 			}
213 		}
214 	}
215 
216 	//NX_LOG("load_entities: loaded %d objects\n", nEntities);
217 	cclose(fp);
218 	return 0;
219 }
220 
221 // loads a pxa (tileattr) file
load_tileattr(const char * fname)222 bool load_tileattr(const char *fname)
223 {
224 CFILE *fp;
225 int i;
226 unsigned char tc;
227 
228 	map.nmotiontiles = 0;
229 
230 	NX_LOG("load_pxa: reading in %s\n", fname);
231 	fp = copen(fname, "rb");
232 	if (!fp)
233 	{
234 		NX_ERR("load_pxa: no such file: '%s'\n", fname);
235 		return 1;
236 	}
237 
238 	for(i=0;i<256;i++)
239 	{
240 		tc = cgetc(fp);
241 		tilecode[i] = tc;
242 		tileattr[i] = tilekey[tc];
243 		//NX_LOG("Tile %02x   TC %02x    Attr %08x   tilekey[%02x] = %08x\n", i, tc, tileattr[i], tc, tilekey[tc]);
244 
245 		//FIXME: Destroyable star tiles not showing up right now
246 		if (tc == 0x43)	// destroyable block - have to replace graphics
247 		{
248 			CopySpriteToTile(SPR_DESTROYABLE, i, 0, 0);
249 		}
250 
251 		// add water currents to animation list
252 		if (tileattr[i] & TA_CURRENT)
253 		{
254 			map.motiontiles[map.nmotiontiles].tileno = i;
255 			map.motiontiles[map.nmotiontiles].dir = CVTDir(tc & 3);
256 			map.motiontiles[map.nmotiontiles].sprite = SPR_WATER_CURRENT;
257 
258 			map.nmotiontiles++;
259 			NX_LOG("Added tile %02x to animation list, tc=%02x\n", i, tc);
260 		}
261 	}
262 
263 	cclose(fp);
264 	return 0;
265 }
266 
load_stages(void)267 bool load_stages(void)
268 {
269 	num_stages = MAX_STAGES;
270 
271 	return 0;
272 }
273 
274 
initmapfirsttime(void)275 bool initmapfirsttime(void)
276 {
277         char fname[1024];
278 	FILE *fp;
279 	int i;
280 
281 	retro_create_path_string(fname, sizeof(fname), g_dir, "tilekey.dat");
282 
283 	NX_LOG("initmapfirsttime: loading %s.\n", fname);
284 	if (!(fp = fopen(fname, "rb")))
285 	{
286 		NX_LOG("%s is missing, using default\n", fname);
287 	}
288    else
289    {
290       for(i=0;i<256;i++)
291          tilekey[i] = fgetl(fp);
292 
293       fclose(fp);
294    }
295 	return load_stages();
296 }
297 
initmap(void)298 void initmap(void)
299 {
300 	map_focus(NULL);
301 	map.parscroll_x = map.parscroll_y = 0;
302 }
303 
304 /*
305 void c------------------------------() {}
306 */
307 
308 // backdrop_no 	- backdrop # to switch to
map_set_backdrop(int backdrop_no)309 void map_set_backdrop(int backdrop_no)
310 {
311 	if (!LoadBackdropIfNeeded(backdrop_no))
312 		map.backdrop = backdrop_no;
313 }
314 
315 
map_draw_backdrop(void)316 void map_draw_backdrop(void)
317 {
318 int x, y;
319 
320 	if (!backdrop[map.backdrop])
321 	{
322 		LoadBackdropIfNeeded(map.backdrop);
323 		if (!backdrop[map.backdrop])
324 			return;
325 	}
326 
327 	switch(map.scrolltype)
328 	{
329 		case BK_FIXED:
330 			map.parscroll_x = 0;
331 			map.parscroll_y = 0;
332 		break;
333 
334 		case BK_FOLLOWFG:
335 			map.parscroll_x = (map.displayed_xscroll >> CSF);
336 			map.parscroll_y = (map.displayed_yscroll >> CSF);
337 		break;
338 
339 		case BK_PARALLAX:
340 			map.parscroll_y = (map.displayed_yscroll >> CSF) / 2;
341 			map.parscroll_x = (map.displayed_xscroll >> CSF) / 2;
342 		break;
343 
344 		case BK_FASTLEFT:		// Ironhead
345 			map.parscroll_x += 6;
346 			map.parscroll_y = 0;
347 		break;
348 
349 		case BK_FASTLEFT_LAYERS:
350 		case BK_FASTLEFT_LAYERS_NOFALLLEFT:
351 		{
352 			DrawFastLeftLayered();
353 			return;
354 		}
355 		break;
356 
357 		case BK_HIDE:
358 		case BK_HIDE2:
359 		case BK_HIDE3:
360 		{
361 			if (game.curmap == STAGE_KINGS)		// intro cutscene
362 				ClearScreen(BLACK);
363 			else
364 				ClearScreen(DK_BLUE);
365 		}
366 		return;
367 
368 		default:
369 			map.parscroll_x = map.parscroll_y = 0;
370 			NX_ERR("map_draw_backdrop: unhandled map scrolling type %d\n", map.scrolltype);
371 		break;
372 	}
373 
374 	map.parscroll_x %= backdrop[map.backdrop]->Width();
375 	map.parscroll_y %= backdrop[map.backdrop]->Height();
376 	int w = backdrop[map.backdrop]->Width();
377 	int h = backdrop[map.backdrop]->Height();
378 
379 	for(y=0;y<SCREEN_HEIGHT+map.parscroll_y; y+=h)
380 	{
381 		for(x=0;x<SCREEN_WIDTH+map.parscroll_x; x+=w)
382 		{
383 			DrawSurface(backdrop[map.backdrop], x - map.parscroll_x, y - map.parscroll_y);
384 		}
385 	}
386 }
387 
388 // blit OSide's BK_FASTLEFT_LAYERS
DrawFastLeftLayered(void)389 static void DrawFastLeftLayered(void)
390 {
391 static const int layer_ys[] = { 80, 122, 145, 176, 240 };
392 static const int move_spd[] = { 0,    1,   2,   4,   8 };
393 const int nlayers = sizeof(layer_ys) / sizeof(layer_ys[0]);
394 int y1, y2;
395 int i, x;
396 
397 	const int W = backdrop[map.backdrop]->Width();
398 
399 	if (--map.parscroll_x <= -(W*2))
400 		map.parscroll_x = 0;
401 
402 	y1 = x = 0;
403 	for(i=0;i<nlayers;i++)
404 	{
405 		y2 = layer_ys[i];
406 
407 		if (i)	// not the static moon layer?
408 		{
409 			x = (map.parscroll_x * move_spd[i]) >> 1;
410 			x %= W;
411 		}
412 
413 		BlitPatternAcross(backdrop[map.backdrop], x, y1, y1, (y2-y1)+1);
414 		y1 = (y2 + 1);
415 	}
416 }
417 
418 
419 // loads a backdrop into memory, if it hasn't already been loaded
LoadBackdropIfNeeded(int backdrop_no)420 static bool LoadBackdropIfNeeded(int backdrop_no)
421 {
422 char fname[MAXPATHLEN];
423 char slash;
424 #ifdef _WIN32
425 slash = '\\';
426 #else
427 slash = '/';
428 #endif
429 	// load backdrop now if it hasn't already been loaded
430 	if (!backdrop[backdrop_no])
431 	{
432 		// use chromakey (transparency) on bkwater, all others don't
433 		bool use_chromakey = (backdrop_no == 8);
434 
435 		snprintf(fname, sizeof(fname), "%s%c%s.pbm", data_dir, slash, backdrop_names[backdrop_no]);
436 
437 		backdrop[backdrop_no] = NXSurface::FromFile(fname, use_chromakey);
438 		if (!backdrop[backdrop_no])
439 		{
440 			NX_ERR("Failed to load backdrop '%s'\n", fname);
441 			return 1;
442 		}
443 	}
444 
445 	return 0;
446 }
447 
map_flush_graphics()448 void map_flush_graphics()
449 {
450 int i;
451 
452 	for(i=0;i<MAX_BACKDROPS;i++)
453 	{
454 		delete backdrop[i];
455 		backdrop[i] = NULL;
456 	}
457 
458 	// re-copy star files
459 	for(i=0;i<256;i++)
460 	{
461 		if (tilecode[i] == 0x43)
462 		{
463 			CopySpriteToTile(SPR_DESTROYABLE, i, 0, 0);
464 		}
465 	}
466 }
467 
468 
469 /*
470 void c------------------------------() {}
471 */
472 
473 // draw rising/falling water from eg Almond etc
map_drawwaterlevel(void)474 void map_drawwaterlevel(void)
475 {
476 // water_sfc: 16 tall at 0
477 // just under: 16 tall at 32
478 // main tile: 32 tall at 16 (yes, overlapping)
479 int water_x, water_y;
480 
481 	if (!map.waterlevelobject)
482 		return;
483 
484 	water_x = -(map.displayed_xscroll >> CSF);
485 	water_x %= SCREEN_WIDTH;
486 
487 	water_y = (map.waterlevelobject->y >> CSF) - (map.displayed_yscroll >> CSF);
488 
489 	// draw the surface and just under the surface
490 	BlitPatternAcross(backdrop[map.backdrop], water_x, water_y, 0, 16);
491 	water_y += 16;
492 
493 	BlitPatternAcross(backdrop[map.backdrop], water_x, water_y, 32, 16);
494 	water_y += 16;
495 
496 	// draw the rest of the pattern all the way down
497 	while(water_y < (SCREEN_HEIGHT-1))
498 	{
499 		BlitPatternAcross(backdrop[map.backdrop], water_x, water_y, 16, 32);
500 		water_y += 32;
501 	}
502 }
503 
504 
505 // draw the map.
506 // 	if foreground = TA_FOREGROUND, draws the foreground tile layer.
507 //  if foreground = 0, draws backdrop and background tiles.
map_draw(uint8_t foreground)508 void map_draw(uint8_t foreground)
509 {
510 int x, y;
511 int mapx, mapy;
512 int blit_x, blit_y, blit_x_start;
513 int scroll_x, scroll_y;
514 
515 	scroll_x = (map.displayed_xscroll >> CSF);
516 	scroll_y = (map.displayed_yscroll >> CSF);
517 
518 	mapx = (scroll_x / TILE_W);
519 	mapy = (scroll_y / TILE_H);
520 
521 	blit_y = -(scroll_y % TILE_H);
522 	blit_x_start = -(scroll_x % TILE_W);
523 
524 	// MAP_DRAW_EXTRA_Y etc is 1 if resolution is changed to
525 	// something not a multiple of TILE_H.
526 	for(y=0; y <= (SCREEN_HEIGHT / TILE_H)+MAP_DRAW_EXTRA_Y; y++)
527 	{
528 		blit_x = blit_x_start;
529 
530 		for(x=0; x <= (SCREEN_WIDTH / TILE_W)+MAP_DRAW_EXTRA_X; x++)
531 		{
532 			int t = map.tiles[mapx+x][mapy+y];
533 			if ((tileattr[t] & TA_FOREGROUND) == foreground)
534 				draw_tile(blit_x, blit_y, t);
535 
536 			blit_x += TILE_W;
537 		}
538 
539 		blit_y += TILE_H;
540 	}
541 }
542 
543 
544 /*
545 void c------------------------------() {}
546 */
547 
548 // map scrolling code
scroll_normal(void)549 void scroll_normal(void)
550 {
551 const int scroll_adj_rate = (0x2000 / map.scrollspeed);
552 
553 	// how many pixels to let player stray from the center of the screen
554 	// before we start scrolling. high numbers let him reach closer to the edges,
555 	// low numbers keep him real close to the center.
556 	#define P_VARY_FROM_CENTER			(64 << CSF)
557 
558 	if (player->dir == LEFT)
559 	{
560 		map.scrollcenter_x -= scroll_adj_rate;
561 		if (map.scrollcenter_x < -P_VARY_FROM_CENTER)
562 			map.scrollcenter_x = -P_VARY_FROM_CENTER;
563 	}
564 	else
565 	{
566 		map.scrollcenter_x += scroll_adj_rate;
567 		if (map.scrollcenter_x > P_VARY_FROM_CENTER)
568 			map.scrollcenter_x = P_VARY_FROM_CENTER;
569 	}
570 
571 	// compute where the map "wants" to be
572 	map.target_x = (player->CenterX() + map.scrollcenter_x) - ((SCREEN_WIDTH / 2) << CSF);
573 
574 	// Y scrolling
575 	if (player->lookscroll == UP)
576 	{
577 		map.scrollcenter_y -= scroll_adj_rate;
578 		if (map.scrollcenter_y < -P_VARY_FROM_CENTER) map.scrollcenter_y = -P_VARY_FROM_CENTER;
579 	}
580 	else if (player->lookscroll == DOWN)
581 	{
582 		map.scrollcenter_y += scroll_adj_rate;
583 		if (map.scrollcenter_y > P_VARY_FROM_CENTER) map.scrollcenter_y = P_VARY_FROM_CENTER;
584 	}
585 	else
586 	{
587 		if (map.scrollcenter_y <= -scroll_adj_rate)
588 		{
589 			map.scrollcenter_y += scroll_adj_rate;
590 		}
591 		else if (map.scrollcenter_y >= scroll_adj_rate)
592 		{
593 			map.scrollcenter_y -= scroll_adj_rate;
594 		}
595 	}
596 
597 	map.target_y = (player->CenterY() + map.scrollcenter_y) - ((SCREEN_HEIGHT / 2) << CSF);
598 }
599 
map_scroll_do(void)600 void map_scroll_do(void)
601 {
602 	bool doing_normal_scroll = false;
603 
604 	if (!map.scroll_locked)
605 	{
606 		if (map.focus.has_target)
607 		{	// FON command
608 			// this check makes it so if we <FON on an object which
609 			// gets destroyed, the scroll stays locked at the last known
610 			// position of the object.
611 			if (map.focus.target)
612 			{
613 				Object *t = map.focus.target;
614 
615 				// Generally we want to focus on the center of the object, not it's UL corner.
616 				// But a few objects (Cage in mimiga village) have offset drawpoints
617 				// that affect the positioning of the scene. If the object has a drawpoint,
618 				// we'll assume it's in an appropriate position, otherwise, we'll try to find
619 				// the center ourselves.
620 				if (sprites[t->sprite].frame[t->frame].dir[t->dir].drawpoint.equ(0, 0))
621 				{
622 					map.target_x = map.focus.target->CenterX() - ((SCREEN_WIDTH / 2) << CSF);
623 					map.target_y = map.focus.target->CenterY() - ((SCREEN_HEIGHT / 2) << CSF);
624 				}
625 				else
626 				{
627 					map.target_x = map.focus.target->x - ((SCREEN_WIDTH / 2) << CSF);
628 					map.target_y = map.focus.target->y - ((SCREEN_HEIGHT / 2) << CSF);
629 				}
630 			}
631 		}
632 		else
633 		{
634 			if (!player->hide)
635 			{
636 				scroll_normal();
637 
638             doing_normal_scroll = true;
639 			}
640 		}
641 	}
642 
643 	map.real_xscroll += (map.target_x - map.real_xscroll) / map.scrollspeed;
644 	map.real_yscroll += (map.target_y - map.real_yscroll) / map.scrollspeed;
645 
646 	map.displayed_xscroll = (map.real_xscroll + map.phase_adj);
647 	map.displayed_yscroll = map.real_yscroll;	// we don't compensate on Y, because player falls > 2 pixels per frame
648 
649 	if (doing_normal_scroll)
650 	{
651 		run_phase_compensator();
652 	}
653 	else
654 	{
655 		map.phase_adj -= MAP_PHASE_ADJ_SPEED;
656 		if (map.phase_adj < 0) map.phase_adj = 0;
657 	}
658 
659 	map_sanitycheck();
660 
661 	// do quaketime after sanity check so quake works in
662 	// small levels like Shack.
663 	if (game.quaketime)
664 	{
665 		if (!map.scroll_locked)
666 		{
667 			int pushx, pushy;
668 
669 			if (game.megaquaketime)		// Ballos fight
670 			{
671 				game.megaquaketime--;
672 				pushx = random(-5, 5) << CSF;
673 				pushy = random(-3, 3) << CSF;
674 			}
675 			else
676 			{
677 				pushx = random(-1, 1) << CSF;
678 				pushy = random(-1, 1) << CSF;
679 			}
680 
681 			map.real_xscroll += pushx;
682 			map.real_yscroll += pushy;
683 			map.displayed_xscroll += pushx;
684 			map.displayed_yscroll += pushy;
685 		}
686 		else
687 		{
688 			// quake after IronH battle...special case cause we don't
689 			// want to show the walls of the arena.
690 			int pushy = random(-0x500, 0x500);
691 
692 			map.real_yscroll += pushy;
693 			if (map.real_yscroll < 0) map.real_yscroll = 0;
694 			if (map.real_yscroll > (15 << CSF)) map.real_yscroll = (15 << CSF);
695 
696 			map.displayed_yscroll += pushy;
697 			if (map.displayed_yscroll < 0) map.displayed_yscroll = 0;
698 			if (map.displayed_yscroll > (15 << CSF)) map.displayed_yscroll = (15 << CSF);
699 		}
700 
701 		game.quaketime--;
702 	}
703 }
704 
705 // this attempts to prevent jitter most visible when the player is walking on a
706 // long straight stretch. the jitter occurs because map.xscroll and player->x
707 // tend to be out-of-phase, and thus cross over pixel boundaries at different times.
708 // what we do here is try to tweak/fudge the displayed xscroll value by up to 512 subpixels
709 // (1 real pixel), so that it crosses pixel boundaries on exactly the same frame as
710 // the player does.
run_phase_compensator(void)711 void run_phase_compensator(void)
712 {
713 	int displayed_phase_offs = (map.displayed_xscroll - player->x) % 512;
714 
715 	if (displayed_phase_offs != 0)
716 	{
717 		int phase_offs = abs(map.real_xscroll - player->x) % 512;
718 		//debug("%d", phase_offs);
719 
720 		// move phase_adj towards phase_offs; phase_offs is how far
721 		// out of sync we are with the player and so once we reach it
722 		// we will compensating exactly.
723 		if (map.phase_adj < phase_offs)
724 		{
725 			map.phase_adj += MAP_PHASE_ADJ_SPEED;
726 			if (map.phase_adj > phase_offs)
727 				map.phase_adj = phase_offs;
728 		}
729 		else
730 		{
731 			map.phase_adj -= MAP_PHASE_ADJ_SPEED;
732 			if (map.phase_adj < phase_offs)
733 				map.phase_adj = phase_offs;
734 		}
735 	}
736 }
737 
738 /*
739 void c------------------------------() {}
740 */
741 
742 
743 // scroll position sanity checking
map_sanitycheck(void)744 void map_sanitycheck(void)
745 {
746 	#define MAP_BORDER_AMT		(8<<CSF)
747 	if (map.real_xscroll < MAP_BORDER_AMT) map.real_xscroll = MAP_BORDER_AMT;
748 	if (map.real_yscroll < MAP_BORDER_AMT) map.real_yscroll = MAP_BORDER_AMT;
749 	if (map.real_xscroll > map.maxxscroll) map.real_xscroll = map.maxxscroll;
750 	if (map.real_yscroll > map.maxyscroll) map.real_yscroll = map.maxyscroll;
751 
752 	if (map.displayed_xscroll < MAP_BORDER_AMT) map.displayed_xscroll = MAP_BORDER_AMT;
753 	if (map.displayed_yscroll < MAP_BORDER_AMT) map.displayed_yscroll = MAP_BORDER_AMT;
754 	if (map.displayed_xscroll > map.maxxscroll) map.displayed_xscroll = map.maxxscroll;
755 	if (map.displayed_yscroll > map.maxyscroll) map.displayed_yscroll = map.maxyscroll;
756 }
757 
758 
map_scroll_jump(int x,int y)759 void map_scroll_jump(int x, int y)
760 {
761 	map.target_x = x - ((SCREEN_WIDTH / 2) << CSF);
762 	map.target_y = y - ((SCREEN_HEIGHT / 2) << CSF);
763 	map.real_xscroll = map.target_x;
764 	map.real_yscroll = map.target_y;
765 
766 	map.displayed_xscroll = map.real_xscroll;
767 	map.displayed_yscroll = map.real_yscroll;
768 	map.phase_adj = 0;
769 
770 	map.scrollcenter_x = map.scrollcenter_y = 0;
771 	map_sanitycheck();
772 }
773 
774 // lock the scroll in it's current position. the target position will not change,
775 // however if the scroll is moved off the target (really only a quake could do this)
776 // the map will still seek it's old position.
map_scroll_lock(bool lockstate)777 void map_scroll_lock(bool lockstate)
778 {
779 	map.scroll_locked = lockstate;
780 	if (lockstate)
781 	{	// why do we do this?
782 		map.real_xscroll = map.target_x;
783 		map.real_yscroll = map.target_y;
784 	}
785 }
786 
787 // set the map focus and scroll speed.
788 // if o is specified, focuses on that object.
789 // if o is NULL, focuses on the player.
map_focus(Object * o,int spd)790 void map_focus(Object *o, int spd)
791 {
792 	map.focus.target = o;
793 	map.focus.has_target = (o != NULL);
794 
795 	map.scrollspeed = spd;
796 	map.scroll_locked = false;
797 }
798 
799 /*
800 void c------------------------------() {}
801 */
802 
803 // change tile at x,y into newtile while optionally spawning smoke clouds and boomflash
map_ChangeTileWithSmoke(int x,int y,int newtile,int nclouds,bool boomflash,Object * push_behind)804 void map_ChangeTileWithSmoke(int x, int y, int newtile, int nclouds, bool boomflash, Object *push_behind)
805 {
806 	if (x < 0 || y < 0 || x >= map.xsize || y >= map.ysize)
807 		return;
808 
809 	map.tiles[x][y] = newtile;
810 
811 	int xa = ((x * TILE_W) + (TILE_W / 2)) << CSF;
812 	int ya = ((y * TILE_H) + (TILE_H / 2)) << CSF;
813 	SmokeXY(xa, ya, nclouds, TILE_W/2, TILE_H/2, push_behind);
814 
815 	if (boomflash)
816 		effect(xa, ya, EFFECT_BOOMFLASH);
817 }
818 
819 
820 
map_get_stage_name(int mapno)821 const char *map_get_stage_name(int mapno)
822 {
823 	if (mapno == STAGE_KINGS)
824 		return "";//Studio Pixel Presents";
825 
826 	return stages[mapno].stagename;
827 }
828 
829 // show map name for "ticks" ticks
map_show_map_name()830 void map_show_map_name()
831 {
832 	game.mapname_x = (SCREEN_WIDTH / 2) - (GetFontWidth(map_get_stage_name(game.curmap), 0) / 2);
833 	game.showmapnametime = 120;
834 }
835 
map_draw_map_name(void)836 void map_draw_map_name(void)
837 {
838 	if (game.showmapnametime)
839 	{
840 		font_draw(game.mapname_x, 84, map_get_stage_name(game.curmap), 0, &bluefont); // Workaround for avoiding diacritics not showing on map location names
841 		game.showmapnametime--;
842 	}
843 }
844 
845 
846 // animate all motion tiles
AnimateMotionTiles(void)847 void AnimateMotionTiles(void)
848 {
849 int i;
850 int x_off, y_off;
851 
852 	for(i=0;i<map.nmotiontiles;i++)
853 	{
854 		switch(map.motiontiles[i].dir)
855 		{
856 			case LEFT: y_off = 0; x_off = map.motionpos; break;
857 			case RIGHT: y_off = 0; x_off = (TILE_W - map.motionpos); break;
858 
859 			case UP: x_off = 0; y_off = map.motionpos; break;
860 			case DOWN: x_off = 0; y_off = (TILE_H - map.motionpos); break;
861 
862 			default: x_off = y_off = 0; break;
863 		}
864 
865 		CopySpriteToTile(map.motiontiles[i].sprite, map.motiontiles[i].tileno, x_off, y_off);
866 	}
867 
868 	map.motionpos += 2;
869 	if (map.motionpos >= TILE_W) map.motionpos = 0;
870 }
871 
872 
873 // attempts to find an object with id2 matching the given value else returns NULL
FindObjectByID2(int id2)874 Object *FindObjectByID2(int id2)
875 {
876 	Object *result = ID2Lookup[id2];
877 
878 	if (!result)
879 		NX_ERR("FindObjectByID2: no such object %04d\n", id2);
880 
881 	return result;
882 }
883 
884