8 MapRecord stages
[MAX_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
35 Sprites::FlushSheets();
39 if (Tileset::Load(stages
[stage_no
].tileset
))
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
;
67 void c------------------------------() {}
71 bool load_map(const char *fname
)
76 fp
= fileopen(fname
, "rb");
79 staterr("load_map: no such file: '%s'", fname
);
83 if (!fverifystring(fp
, "PXM"))
85 staterr("load_map: invalid map format: '%s'", fname
);
89 memset(&map
, 0, sizeof(map
));
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
);
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
);
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
);
122 // load a PXE (entity list for a map)
123 bool load_entities(const char *fname
)
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");
138 staterr("load_entities: no such file: '%s'", fname
);
142 if (!fverifystring(fp
, "PXE"))
144 staterr("load_entities: not a PXE: '%s'", fname
);
149 nEntities
= fgetl(fp
);
151 for(i
=0;i
<nEntities
;i
++)
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
)
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
])
187 stat(" -- Disappearing object %02d (%s) because flag %d is set", id2
, DescribeObjectType(type
), id1
);
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
);
208 ID2Lookup
[o
->id2
] = o
;
210 // now that it's all set up, execute OnSpawn,
211 // since we didn't do it in CreateObject.
217 //stat("load_entities: loaded %d objects", nEntities);
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
)
244 map
.nmotiontiles
= 0;
246 stat("load_pxa: reading in %s", fname
);
247 fp
= fileopen(fname
, "rb");
250 staterr("load_pxa: no such file: '%s'", fname
);
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
;
274 stat("Added tile %02x to animation list, tc=%02x", i
, tc
);
282 bool load_stages(void)
286 fp
= fileopen("stage.dat", "rb");
289 staterr("%s(%d): failed to open stage.dat", __FILE__
, __LINE__
);
294 num_stages
= fgetc(fp
);
295 for(int i
=0;i
<num_stages
;i
++)
296 fread(&stages
[i
], sizeof(MapRecord
), 1, fp
);
302 bool initmapfirsttime(void)
307 stat("initmapfirsttime: loading tilekey.dat.");
308 if (!(fp
= fileopen("tilekey.dat", "rb")))
310 staterr("tilekey.dat is missing!");
315 tilekey
[i
] = fgetl(fp
);
318 return load_stages();
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)
343 if (!backdrop
[map
.backdrop
])
345 LoadBackdropIfNeeded(map
.backdrop
);
346 if (!backdrop
[map
.backdrop
])
350 switch(map
.scrolltype
)
358 map
.parscroll_x
= (map
.displayed_xscroll
>> CSF
);
359 map
.parscroll_y
= (map
.displayed_yscroll
>> CSF
);
363 map
.parscroll_y
= (map
.displayed_yscroll
>> CSF
) / 2;
364 map
.parscroll_x
= (map
.displayed_xscroll
>> CSF
) / 2;
367 case BK_FASTLEFT
: // Ironhead
368 map
.parscroll_x
+= 6;
372 case BK_FASTLEFT_LAYERS
:
373 case BK_FASTLEFT_LAYERS_NOFALLLEFT
:
375 DrawFastLeftLayered();
384 if (game
.curmap
== STAGE_KINGS
) // intro cutscene
387 ClearScreen(DK_BLUE
);
392 map
.parscroll_x
= map
.parscroll_y
= 0;
393 staterr("map_draw_backdrop: unhandled map scrolling type %d", map
.scrolltype
);
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;
420 if (--map
.parscroll_x
<= -(SCREEN_WIDTH
*2))
424 for(i
=0;i
<nlayers
;i
++)
428 if (i
) // not the static moon layer?
430 x
= (map
.parscroll_x
* move_spd
[i
]) >> 1;
434 BlitPatternAcross(backdrop
[map
.backdrop
], x
, y1
, y1
, (y2
-y1
)+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
);
464 void map_flush_graphics()
468 for(i
=0;i
<MAX_BACKDROPS
;i
++)
474 // re-copy star files
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
)
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);
509 BlitPatternAcross(backdrop
[map
.backdrop
], water_x
, water_y
, 32, 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);
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
)
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
);
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
;
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
);
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
;
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
)
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
);
643 map
.target_x
= map
.focus
.target
->x
- ((SCREEN_WIDTH
/ 2) << CSF
);
644 map
.target_y
= map
.focus
.target
->y
- ((SCREEN_HEIGHT
/ 2) << CSF
);
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();
673 map
.phase_adj
-= MAP_PHASE_ADJ_SPEED
;
674 if (map
.phase_adj
< 0) map
.phase_adj
= 0;
679 // do quaketime after sanity check so quake works in
680 // small levels like Shack.
683 if (!map
.scroll_locked
)
687 if (game
.megaquaketime
) // Ballos fight
689 game
.megaquaketime
--;
690 pushx
= random(-5, 5) << CSF
;
691 pushy
= random(-3, 3) << CSF
;
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
;
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
);
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
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
;
749 map
.phase_adj
-= MAP_PHASE_ADJ_SPEED
;
750 if (map
.phase_adj
< phase_offs
)
751 map
.phase_adj
= phase_offs
;
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
);
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
);
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
;
803 map
.scrollcenter_x
= map
.scrollcenter_y
= 0;
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
;
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
)
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
);
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)
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
);
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
];
912 staterr("FindObjectByID2: ID2 %04d found: type %s; coords: (%d, %d)", id2
, DescribeObjectType(ID2Lookup
[id2
]->type
), ID2Lookup
[id2
]->x
>>CSF
,ID2Lookup
[id2
]->y
>>CSF
);
914 staterr("FindObjectByID2: no such object %04d", id2
);