NXEngine v1.0.0.6
[NXEngine.git] / map.cpp
blob9dc84627cb2ba7289565bc18852dcd6a59dd0d58
2 #include "nx.h"
3 #include "map.h"
4 #include "map.fdh"
6 stMap map;
8 MapRecord stages[MAX_STAGES];
9 int num_stages;
11 #define MAX_BACKDROPS 32
12 NXSurface *backdrop[MAX_BACKDROPS];
14 // for FindObject--finding NPC's by ID2
15 Object *ID2Lookup[65536];
17 unsigned char tilecode[MAX_TILES]; // tile codes for every tile in current tileset
18 unsigned int tileattr[MAX_TILES]; // tile attribute bits for every tile in current tileset
19 unsigned int tilekey[MAX_TILES]; // mapping from tile codes -> tile attributes
22 // load stage "stage_no", this entails loading the map (pxm), enemies (pxe), tileset (pbm),
23 // tile attributes (pxa), and script (tsc).
24 bool load_stage(int stage_no)
26 char stage[MAXPATHLEN];
27 char fname[MAXPATHLEN];
29 stat(" >> Entering stage %d: '%s'.", stage_no, stages[stage_no].stagename);
30 game.curmap = stage_no; // do it now so onspawn events will have it
32 if (use_palette)
34 palette_reset();
35 Sprites::FlushSheets();
36 map_flush_graphics();
39 if (Tileset::Load(stages[stage_no].tileset))
40 return 1;
42 // get the base name of the stage without extension
43 const char *mapname = stages[stage_no].filename;
44 if (!strcmp(mapname, "lounge")) mapname = "Lounge";
45 sprintf(stage, "%s/%s", stage_dir, mapname);
47 sprintf(fname, "%s.pxm", stage);
48 if (load_map(fname)) return 1;
50 sprintf(fname, "%s/%s.pxa", stage_dir, tileset_names[stages[stage_no].tileset]);
51 if (load_tileattr(fname)) return 1;
53 sprintf(fname, "%s.pxe", stage);
54 if (load_entities(fname)) return 1;
56 sprintf(fname, "%s.tsc", stage);
57 if (tsc_load(fname, SP_MAP) == -1) return 1;
59 map_set_backdrop(stages[stage_no].bg_no);
60 map.scrolltype = stages[stage_no].scroll_type;
61 map.motionpos = 0;
63 return 0;
67 void c------------------------------() {}
70 // load a PXM map
71 bool load_map(const char *fname)
73 FILE *fp;
74 int x, y;
76 fp = fileopen(fname, "rb");
77 if (!fp)
79 staterr("load_map: no such file: '%s'", fname);
80 return 1;
83 if (!fverifystring(fp, "PXM"))
85 staterr("load_map: invalid map format: '%s'", fname);
86 return 1;
89 memset(&map, 0, sizeof(map));
91 fgetc(fp);
92 map.xsize = fgeti(fp);
93 map.ysize = fgeti(fp);
95 if (map.xsize > MAP_MAXSIZEX || map.ysize > MAP_MAXSIZEY)
97 staterr("load_map: map is too large -- size %dx%d but max is %dx%d", map.xsize, map.ysize, MAP_MAXSIZEX, MAP_MAXSIZEY);
98 fclose(fp);
99 return 1;
101 else
103 stat("load_map: level size %dx%d", map.xsize, map.ysize);
106 for(y=0;y<map.ysize;y++)
107 for(x=0;x<map.xsize;x++)
109 map.tiles[x][y] = fgetc(fp);
112 fclose(fp);
114 map.maxxscroll = (((map.xsize * TILE_W) - SCREEN_WIDTH) - 8) << CSF;
115 map.maxyscroll = (((map.ysize * TILE_H) - SCREEN_HEIGHT) - 8) << CSF;
117 stat("load_map: '%s' loaded OK! - %dx%d", fname, map.xsize, map.ysize);
118 return 0;
122 // load a PXE (entity list for a map)
123 bool load_entities(const char *fname)
125 FILE *fp;
126 int i;
127 int nEntities;
129 // gotta destroy all objects before creating new ones
130 Objects::DestroyAll(false);
131 FloatText::ResetAll();
133 stat("load_entities: reading in %s", fname);
134 // now we can load in the new objects
135 fp = fileopen(fname, "rb");
136 if (!fp)
138 staterr("load_entities: no such file: '%s'", fname);
139 return 1;
142 if (!fverifystring(fp, "PXE"))
144 staterr("load_entities: not a PXE: '%s'", fname);
145 return 1;
148 fgetc(fp);
149 nEntities = fgetl(fp);
151 for(i=0;i<nEntities;i++)
153 int x = fgeti(fp);
154 int y = fgeti(fp);
155 int id1 = fgeti(fp);
156 int id2 = fgeti(fp);
157 int type = fgeti(fp);
158 int flags = fgeti(fp);
160 int dir = (flags & FLAG_FACES_RIGHT) ? RIGHT : LEFT;
162 //lprintf(" %d: [%d, %d]\t id1=%d\t id2=%d Type %d flags %04x\n", i, x, y, id1, id2, type, flags);
164 // most maps have apparently garbage entities--invisible do-nothing objects??
165 // i dunno but no point in spawning those...
166 if (type || id1 || id2 || flags)
168 bool addobject = false;
170 // check if object is dependent on a flag being set/not set
171 if (flags & FLAG_APPEAR_ON_FLAGID)
173 if (game.flags[id1])
175 addobject = true;
176 stat(" -- Appearing object %02d (%s) because flag %d is set", id2, DescribeObjectType(type), id1);
179 else if (flags & FLAG_DISAPPEAR_ON_FLAGID)
181 if (!game.flags[id1])
183 addobject = true;
185 else
187 stat(" -- Disappearing object %02d (%s) because flag %d is set", id2, DescribeObjectType(type), id1);
190 else
192 addobject = true;
195 if (addobject)
197 // hack for chests (can we do this elsewhere?)
198 if (type == OBJ_CHEST_OPEN) y++;
200 Object *o = CreateObject((x * TILE_W) << CSF, \
201 (y * TILE_H) << CSF, type,
202 0, 0, dir, NULL, CF_NO_SPAWN_EVENT);
204 o->id1 = id1;
205 o->id2 = id2;
206 o->flags |= flags;
208 ID2Lookup[o->id2] = o;
210 // now that it's all set up, execute OnSpawn,
211 // since we didn't do it in CreateObject.
212 o->OnSpawn();
217 //stat("load_entities: loaded %d objects", nEntities);
218 fclose(fp);
219 return 0;
222 /*const int ta[] =
223 { 0, TA_SOLID, TA_SOLID, TA_SOLID, TA_SOLID,
224 TA_SLOPE_BACK1|TA_FOREGROUND, TA_SLOPE_BACK2|TA_FOREGROUND, TA_SLOPE_FWD1|TA_FOREGROUND, TA_SLOPE_FWD2|TA_FOREGROUND,
225 TA_FOREGROUND, 0,0,0, TA_FOREGROUND,TA_FOREGROUND,TA_FOREGROUND, 0, TA_SOLID, TA_SOLID, TA_FOREGROUND, TA_FOREGROUND,
226 TA_SOLID,TA_SOLID,TA_SOLID,TA_SOLID,TA_FOREGROUND,0,0,0,TA_FOREGROUND,TA_FOREGROUND,TA_FOREGROUND,
227 0,TA_SOLID,TA_FOREGROUND,TA_DESTROYABLE|TA_SOLID,TA_SOLID,TA_FOREGROUND,TA_FOREGROUND,TA_FOREGROUND,TA_FOREGROUND,TA_FOREGROUND,TA_SLOPE_CEIL_BACK1|TA_FOREGROUND,TA_SOLID,TA_SOLID,TA_SLOPE_CEIL_FWD2|TA_FOREGROUND,TA_SLOPE_FWD1|TA_FOREGROUND,TA_SLOPE_FWD2|TA_FOREGROUND,
228 TA_FOREGROUND,TA_FOREGROUND,TA_SLOPE_CEIL_FWD1|TA_FOREGROUND,TA_SLOPE_CEIL_FWD2|TA_FOREGROUND,TA_SLOPE_CEIL_BACK1|TA_FOREGROUND,TA_SLOPE_CEIL_BACK2|TA_FOREGROUND,TA_FOREGROUND,TA_FOREGROUND,TA_FOREGROUND,0,0,TA_SOLID,TA_SOLID,TA_FOREGROUND,TA_SOLID,TA_SOLID,
229 TA_SOLID,TA_SOLID,TA_FOREGROUND|TA_SLOPE_BACK1,TA_SLOPE_BACK2|TA_FOREGROUND,TA_SLOPE_FWD1|TA_FOREGROUND,TA_SLOPE_FWD2|TA_FOREGROUND,TA_SPIKES,TA_SPIKES,TA_SPIKES,TA_SPIKES,0,TA_SOLID,TA_SOLID,0,TA_SOLID,TA_SOLID,
230 0,TA_SOLID,0,TA_SOLID,TA_SOLID,0,0,0,0,0,0,TA_SOLID,TA_SOLID,TA_SOLID,TA_SOLID,TA_SOLID,
231 TA_SOLID,TA_FOREGROUND,TA_FOREGROUND,0,0,0,0,0,0,0,0,0,TA_SOLID,TA_SOLID,TA_SOLID,TA_SOLID
233 memset(tileattr, 0, sizeof(tileattr));
234 memcpy(&tileattr, &ta, sizeof(ta));
237 // loads a pxa (tileattr) file
238 bool load_tileattr(const char *fname)
240 FILE *fp;
241 int i;
242 unsigned char tc;
244 map.nmotiontiles = 0;
246 stat("load_pxa: reading in %s", fname);
247 fp = fileopen(fname, "rb");
248 if (!fp)
250 staterr("load_pxa: no such file: '%s'", fname);
251 return 1;
254 for(i=0;i<256;i++)
256 tc = fgetc(fp);
257 tilecode[i] = tc;
258 tileattr[i] = tilekey[tc];
259 //stat("Tile %02x TC %02x Attr %08x tilekey[%02x] = %08x", i, tc, tileattr[i], tc, tilekey[tc]);
261 if (tc == 0x43) // destroyable block - have to replace graphics
263 CopySpriteToTile(SPR_DESTROYABLE, i, 0, 0);
266 // add water currents to animation list
267 if (tileattr[i] & TA_CURRENT)
269 map.motiontiles[map.nmotiontiles].tileno = i;
270 map.motiontiles[map.nmotiontiles].dir = CVTDir(tc & 3);
271 map.motiontiles[map.nmotiontiles].sprite = SPR_WATER_CURRENT;
273 map.nmotiontiles++;
274 stat("Added tile %02x to animation list, tc=%02x", i, tc);
278 fclose(fp);
279 return 0;
282 bool load_stages(void)
284 FILE *fp;
286 fp = fileopen("stage.dat", "rb");
287 if (!fp)
289 staterr("%s(%d): failed to open stage.dat", __FILE__, __LINE__);
290 num_stages = 0;
291 return 1;
294 num_stages = fgetc(fp);
295 for(int i=0;i<num_stages;i++)
296 fread(&stages[i], sizeof(MapRecord), 1, fp);
298 return 0;
302 bool initmapfirsttime(void)
304 FILE *fp;
305 int i;
307 stat("initmapfirsttime: loading tilekey.dat.");
308 if (!(fp = fileopen("tilekey.dat", "rb")))
310 staterr("tilekey.dat is missing!");
311 return 1;
314 for(i=0;i<256;i++)
315 tilekey[i] = fgetl(fp);
317 fclose(fp);
318 return load_stages();
321 void initmap(void)
323 map_focus(NULL);
324 map.parscroll_x = map.parscroll_y = 0;
328 void c------------------------------() {}
331 // backdrop_no - backdrop # to switch to
332 void map_set_backdrop(int backdrop_no)
334 if (!LoadBackdropIfNeeded(backdrop_no))
335 map.backdrop = backdrop_no;
339 void map_draw_backdrop(void)
341 int x, y;
343 if (!backdrop[map.backdrop])
345 LoadBackdropIfNeeded(map.backdrop);
346 if (!backdrop[map.backdrop])
347 return;
350 switch(map.scrolltype)
352 case BK_FIXED:
353 map.parscroll_x = 0;
354 map.parscroll_y = 0;
355 break;
357 case BK_FOLLOWFG:
358 map.parscroll_x = (map.displayed_xscroll >> CSF);
359 map.parscroll_y = (map.displayed_yscroll >> CSF);
360 break;
362 case BK_PARALLAX:
363 map.parscroll_y = (map.displayed_yscroll >> CSF) / 2;
364 map.parscroll_x = (map.displayed_xscroll >> CSF) / 2;
365 break;
367 case BK_FASTLEFT: // Ironhead
368 map.parscroll_x += 6;
369 map.parscroll_y = 0;
370 break;
372 case BK_FASTLEFT_LAYERS:
373 case BK_FASTLEFT_LAYERS_NOFALLLEFT:
375 DrawFastLeftLayered();
376 return;
378 break;
380 case BK_HIDE:
381 case BK_HIDE2:
382 case BK_HIDE3:
384 if (game.curmap == STAGE_KINGS) // intro cutscene
385 ClearScreen(BLACK);
386 else
387 ClearScreen(DK_BLUE);
389 return;
391 default:
392 map.parscroll_x = map.parscroll_y = 0;
393 staterr("map_draw_backdrop: unhandled map scrolling type %d", map.scrolltype);
394 break;
397 map.parscroll_x %= backdrop[map.backdrop]->Width();
398 map.parscroll_y %= backdrop[map.backdrop]->Height();
399 int w = backdrop[map.backdrop]->Width();
400 int h = backdrop[map.backdrop]->Height();
402 for(y=0;y<SCREEN_HEIGHT+map.parscroll_y; y+=h)
404 for(x=0;x<SCREEN_WIDTH+map.parscroll_x; x+=w)
406 DrawSurface(backdrop[map.backdrop], x - map.parscroll_x, y - map.parscroll_y);
411 // blit OSide's BK_FASTLEFT_LAYERS
412 static void DrawFastLeftLayered(void)
414 static const int layer_ys[] = { 80, 122, 145, 176, 240 };
415 static const int move_spd[] = { 0, 1, 2, 4, 8 };
416 const int nlayers = 5;
417 int y1, y2;
418 int i, x;
420 if (--map.parscroll_x <= -(SCREEN_WIDTH*2))
421 map.parscroll_x = 0;
423 y1 = x = 0;
424 for(i=0;i<nlayers;i++)
426 y2 = layer_ys[i];
428 if (i) // not the static moon layer?
430 x = (map.parscroll_x * move_spd[i]) >> 1;
431 x %= SCREEN_WIDTH;
434 BlitPatternAcross(backdrop[map.backdrop], x, y1, y1, (y2-y1)+1);
435 y1 = (y2 + 1);
440 // loads a backdrop into memory, if it hasn't already been loaded
441 static bool LoadBackdropIfNeeded(int backdrop_no)
443 char fname[MAXPATHLEN];
445 // load backdrop now if it hasn't already been loaded
446 if (!backdrop[backdrop_no])
448 // use chromakey (transparency) on bkwater, all others don't
449 bool use_chromakey = (backdrop_no == 8);
451 sprintf(fname, "%s/%s.pbm", data_dir, backdrop_names[backdrop_no]);
453 backdrop[backdrop_no] = NXSurface::FromFile(fname, use_chromakey);
454 if (!backdrop[backdrop_no])
456 staterr("Failed to load backdrop '%s'", fname);
457 return 1;
461 return 0;
464 void map_flush_graphics()
466 int i;
468 for(i=0;i<MAX_BACKDROPS;i++)
470 delete backdrop[i];
471 backdrop[i] = NULL;
474 // re-copy star files
475 for(i=0;i<256;i++)
477 if (tilecode[i] == 0x43)
479 CopySpriteToTile(SPR_DESTROYABLE, i, 0, 0);
486 void c------------------------------() {}
489 // draw rising/falling water from eg Almond etc
490 void map_drawwaterlevel(void)
492 // water_sfc: 16 tall at 0
493 // just under: 16 tall at 32
494 // main tile: 32 tall at 16 (yes, overlapping)
495 int water_x, water_y;
497 if (!map.waterlevelobject)
498 return;
500 water_x = -(map.displayed_xscroll >> CSF);
501 water_x %= SCREEN_WIDTH;
503 water_y = (map.waterlevelobject->y >> CSF) - (map.displayed_yscroll >> CSF);
505 // draw the surface and just under the surface
506 BlitPatternAcross(backdrop[map.backdrop], water_x, water_y, 0, 16);
507 water_y += 16;
509 BlitPatternAcross(backdrop[map.backdrop], water_x, water_y, 32, 16);
510 water_y += 16;
512 // draw the rest of the pattern all the way down
513 while(water_y < (SCREEN_HEIGHT-1))
515 BlitPatternAcross(backdrop[map.backdrop], water_x, water_y, 16, 32);
516 water_y += 32;
521 // draw the map.
522 // if foreground = TA_FOREGROUND, draws the foreground tile layer.
523 // if foreground = 0, draws backdrop and background tiles.
524 void map_draw(uint8_t foreground)
526 int x, y;
527 int mapx, mapy;
528 int blit_x, blit_y, blit_x_start;
529 int scroll_x, scroll_y;
531 scroll_x = (map.displayed_xscroll >> CSF);
532 scroll_y = (map.displayed_yscroll >> CSF);
534 mapx = (scroll_x / TILE_W);
535 mapy = (scroll_y / TILE_H);
537 blit_y = -(scroll_y % TILE_H);
538 blit_x_start = -(scroll_x % TILE_W);
540 // MAP_DRAW_EXTRA_Y etc is 1 if resolution is changed to
541 // something not a multiple of TILE_H.
542 for(y=0; y <= (SCREEN_HEIGHT / TILE_H)+MAP_DRAW_EXTRA_Y; y++)
544 blit_x = blit_x_start;
546 for(x=0; x <= (SCREEN_WIDTH / TILE_W)+MAP_DRAW_EXTRA_X; x++)
548 int t = map.tiles[mapx+x][mapy+y];
549 if ((tileattr[t] & TA_FOREGROUND) == foreground)
550 draw_tile(blit_x, blit_y, t);
552 blit_x += TILE_W;
555 blit_y += TILE_H;
561 void c------------------------------() {}
564 // map scrolling code
565 void scroll_normal(void)
567 const int scroll_adj_rate = (0x2000 / map.scrollspeed);
569 // how many pixels to let player stray from the center of the screen
570 // before we start scrolling. high numbers let him reach closer to the edges,
571 // low numbers keep him real close to the center.
572 #define P_VARY_FROM_CENTER (64 << CSF)
574 if (player->dir == LEFT)
576 map.scrollcenter_x -= scroll_adj_rate;
577 if (map.scrollcenter_x < -P_VARY_FROM_CENTER)
578 map.scrollcenter_x = -P_VARY_FROM_CENTER;
580 else
582 map.scrollcenter_x += scroll_adj_rate;
583 if (map.scrollcenter_x > P_VARY_FROM_CENTER)
584 map.scrollcenter_x = P_VARY_FROM_CENTER;
587 // compute where the map "wants" to be
588 map.target_x = (player->CenterX() + map.scrollcenter_x) - ((SCREEN_WIDTH / 2) << CSF);
590 // Y scrolling
591 if (player->lookscroll == UP)
593 map.scrollcenter_y -= scroll_adj_rate;
594 if (map.scrollcenter_y < -P_VARY_FROM_CENTER) map.scrollcenter_y = -P_VARY_FROM_CENTER;
596 else if (player->lookscroll == DOWN)
598 map.scrollcenter_y += scroll_adj_rate;
599 if (map.scrollcenter_y > P_VARY_FROM_CENTER) map.scrollcenter_y = P_VARY_FROM_CENTER;
601 else
603 if (map.scrollcenter_y <= -scroll_adj_rate)
605 map.scrollcenter_y += scroll_adj_rate;
607 else if (map.scrollcenter_y >= scroll_adj_rate)
609 map.scrollcenter_y -= scroll_adj_rate;
613 map.target_y = (player->CenterY() + map.scrollcenter_y) - ((SCREEN_HEIGHT / 2) << CSF);
616 void map_scroll_do(void)
618 bool doing_normal_scroll = false;
620 if (!map.scroll_locked)
622 if (map.focus.has_target)
623 { // FON command
624 // this check makes it so if we <FON on an object which
625 // gets destroyed, the scroll stays locked at the last known
626 // position of the object.
627 if (map.focus.target)
629 Object *t = map.focus.target;
631 // Generally we want to focus on the center of the object, not it's UL corner.
632 // But a few objects (Cage in mimiga village) have offset drawpoints
633 // that affect the positioning of the scene. If the object has a drawpoint,
634 // we'll assume it's in an appropriate position, otherwise, we'll try to find
635 // the center ourselves.
636 if (sprites[t->sprite].frame[t->frame].dir[t->dir].drawpoint.equ(0, 0))
638 map.target_x = map.focus.target->CenterX() - ((SCREEN_WIDTH / 2) << CSF);
639 map.target_y = map.focus.target->CenterY() - ((SCREEN_HEIGHT / 2) << CSF);
641 else
643 map.target_x = map.focus.target->x - ((SCREEN_WIDTH / 2) << CSF);
644 map.target_y = map.focus.target->y - ((SCREEN_HEIGHT / 2) << CSF);
648 else
650 if (!player->hide)
652 scroll_normal();
654 if (!inputs[DEBUG_MOVE_KEY] || !settings->enable_debug_keys)
655 doing_normal_scroll = true;
660 map.real_xscroll += (map.target_x - map.real_xscroll) / map.scrollspeed;
661 map.real_yscroll += (map.target_y - map.real_yscroll) / map.scrollspeed;
663 map.displayed_xscroll = (map.real_xscroll + map.phase_adj);
664 map.displayed_yscroll = map.real_yscroll; // we don't compensate on Y, because player falls > 2 pixels per frame
666 if (doing_normal_scroll)
668 run_phase_compensator();
669 //dump_phase_data();
671 else
673 map.phase_adj -= MAP_PHASE_ADJ_SPEED;
674 if (map.phase_adj < 0) map.phase_adj = 0;
677 map_sanitycheck();
679 // do quaketime after sanity check so quake works in
680 // small levels like Shack.
681 if (game.quaketime)
683 if (!map.scroll_locked)
685 int pushx, pushy;
687 if (game.megaquaketime) // Ballos fight
689 game.megaquaketime--;
690 pushx = random(-5, 5) << CSF;
691 pushy = random(-3, 3) << CSF;
693 else
695 pushx = random(-1, 1) << CSF;
696 pushy = random(-1, 1) << CSF;
699 map.real_xscroll += pushx;
700 map.real_yscroll += pushy;
701 map.displayed_xscroll += pushx;
702 map.displayed_yscroll += pushy;
704 else
706 // quake after IronH battle...special case cause we don't
707 // want to show the walls of the arena.
708 int pushy = random(-0x500, 0x500);
710 map.real_yscroll += pushy;
711 if (map.real_yscroll < 0) map.real_yscroll = 0;
712 if (map.real_yscroll > (15 << CSF)) map.real_yscroll = (15 << CSF);
714 map.displayed_yscroll += pushy;
715 if (map.displayed_yscroll < 0) map.displayed_yscroll = 0;
716 if (map.displayed_yscroll > (15 << CSF)) map.displayed_yscroll = (15 << CSF);
719 game.quaketime--;
723 // this attempts to prevent jitter most visible when the player is walking on a
724 // long straight stretch. the jitter occurs because map.xscroll and player->x
725 // tend to be out-of-phase, and thus cross over pixel boundaries at different times.
726 // what we do here is try to tweak/fudge the displayed xscroll value by up to 512 subpixels
727 // (1 real pixel), so that it crosses pixel boundaries on exactly the same frame as
728 // the player does.
729 void run_phase_compensator(void)
731 int displayed_phase_offs = (map.displayed_xscroll - player->x) % 512;
733 if (displayed_phase_offs != 0)
735 int phase_offs = abs(map.real_xscroll - player->x) % 512;
736 //debug("%d", phase_offs);
738 // move phase_adj towards phase_offs; phase_offs is how far
739 // out of sync we are with the player and so once we reach it
740 // we will compensating exactly.
741 if (map.phase_adj < phase_offs)
743 map.phase_adj += MAP_PHASE_ADJ_SPEED;
744 if (map.phase_adj > phase_offs)
745 map.phase_adj = phase_offs;
747 else
749 map.phase_adj -= MAP_PHASE_ADJ_SPEED;
750 if (map.phase_adj < phase_offs)
751 map.phase_adj = phase_offs;
756 // debug function
757 void dump_phase_data()
759 int phase_offs = abs(map.real_xscroll - player->x) % 512;
760 int final_phase = abs(map.displayed_xscroll - player->x) % 512;
761 debug("phase_offs: %d", phase_offs);
762 debug("");
763 debug("real xscroll: %d", map.real_xscroll);
764 debug("displayed xscroll: %d", map.displayed_xscroll);
765 debug("difference: %d", map.real_xscroll - map.displayed_xscroll);
766 debug("");
767 debug("phase_adj: %d", map.phase_adj);
768 debug("final_phase: %d", final_phase);
772 void c------------------------------() {}
776 // scroll position sanity checking
777 void map_sanitycheck(void)
779 #define MAP_BORDER_AMT (8<<CSF)
780 if (map.real_xscroll < MAP_BORDER_AMT) map.real_xscroll = MAP_BORDER_AMT;
781 if (map.real_yscroll < MAP_BORDER_AMT) map.real_yscroll = MAP_BORDER_AMT;
782 if (map.real_xscroll > map.maxxscroll) map.real_xscroll = map.maxxscroll;
783 if (map.real_yscroll > map.maxyscroll) map.real_yscroll = map.maxyscroll;
785 if (map.displayed_xscroll < MAP_BORDER_AMT) map.displayed_xscroll = MAP_BORDER_AMT;
786 if (map.displayed_yscroll < MAP_BORDER_AMT) map.displayed_yscroll = MAP_BORDER_AMT;
787 if (map.displayed_xscroll > map.maxxscroll) map.displayed_xscroll = map.maxxscroll;
788 if (map.displayed_yscroll > map.maxyscroll) map.displayed_yscroll = map.maxyscroll;
792 void map_scroll_jump(int x, int y)
794 map.target_x = x - ((SCREEN_WIDTH / 2) << CSF);
795 map.target_y = y - ((SCREEN_HEIGHT / 2) << CSF);
796 map.real_xscroll = map.target_x;
797 map.real_yscroll = map.target_y;
799 map.displayed_xscroll = map.real_xscroll;
800 map.displayed_yscroll = map.real_yscroll;
801 map.phase_adj = 0;
803 map.scrollcenter_x = map.scrollcenter_y = 0;
804 map_sanitycheck();
807 // lock the scroll in it's current position. the target position will not change,
808 // however if the scroll is moved off the target (really only a quake could do this)
809 // the map will still seek it's old position.
810 void map_scroll_lock(bool lockstate)
812 map.scroll_locked = lockstate;
813 if (lockstate)
814 { // why do we do this?
815 map.real_xscroll = map.target_x;
816 map.real_yscroll = map.target_y;
820 // set the map focus and scroll speed.
821 // if o is specified, focuses on that object.
822 // if o is NULL, focuses on the player.
823 void map_focus(Object *o, int spd)
825 map.focus.target = o;
826 map.focus.has_target = (o != NULL);
828 map.scrollspeed = spd;
829 map.scroll_locked = false;
833 void c------------------------------() {}
836 // change tile at x,y into newtile while optionally spawning smoke clouds and boomflash
837 void map_ChangeTileWithSmoke(int x, int y, int newtile, int nclouds, bool boomflash, Object *push_behind)
839 if (x < 0 || y < 0 || x >= map.xsize || y >= map.ysize)
840 return;
842 map.tiles[x][y] = newtile;
844 int xa = ((x * TILE_W) + (TILE_W / 2)) << CSF;
845 int ya = ((y * TILE_H) + (TILE_H / 2)) << CSF;
846 SmokeXY(xa, ya, nclouds, TILE_W/2, TILE_H/2, push_behind);
848 if (boomflash)
849 effect(xa, ya, EFFECT_BOOMFLASH);
854 const char *map_get_stage_name(int mapno)
856 if (mapno == STAGE_KINGS)
857 return "";//Studio Pixel Presents";
859 return stages[mapno].stagename;
862 // show map name for "ticks" ticks
863 void map_show_map_name()
865 game.mapname_x = (SCREEN_WIDTH / 2) - (GetFontWidth(map_get_stage_name(game.curmap), 0) / 2);
866 game.showmapnametime = 120;
869 void map_draw_map_name(void)
871 if (game.showmapnametime)
873 font_draw(game.mapname_x, 84, map_get_stage_name(game.curmap), 0, &shadowfont);
874 game.showmapnametime--;
879 // animate all motion tiles
880 void AnimateMotionTiles(void)
882 int i;
883 int x_off, y_off;
885 for(i=0;i<map.nmotiontiles;i++)
887 switch(map.motiontiles[i].dir)
889 case LEFT: y_off = 0; x_off = map.motionpos; break;
890 case RIGHT: y_off = 0; x_off = (TILE_W - map.motionpos); break;
892 case UP: x_off = 0; y_off = map.motionpos; break;
893 case DOWN: x_off = 0; y_off = (TILE_H - map.motionpos); break;
895 default: x_off = y_off = 0; break;
898 CopySpriteToTile(map.motiontiles[i].sprite, map.motiontiles[i].tileno, x_off, y_off);
901 map.motionpos += 2;
902 if (map.motionpos >= TILE_W) map.motionpos = 0;
906 // attempts to find an object with id2 matching the given value else returns NULL
907 Object *FindObjectByID2(int id2)
909 Object *result = ID2Lookup[id2];
911 if (result)
912 staterr("FindObjectByID2: ID2 %04d found: type %s; coords: (%d, %d)", id2, DescribeObjectType(ID2Lookup[id2]->type), ID2Lookup[id2]->x>>CSF,ID2Lookup[id2]->y>>CSF);
913 else
914 staterr("FindObjectByID2: no such object %04d", id2);
916 return result;