9 ////////////////////////////////////////////////////////////////////////////////
10 static Uint16 sqrttbl
[200000];
13 /* n: [0..(1<<30-1)] */
14 static inline unsigned int isqrt0 (unsigned int n
) {
16 #define ISQRT_SQRT_BIT(k) if (n >= (t = (s+(1<<(k-1)))<<(k+1))) { n -= t; s |= 1<<k; }
18 ISQRT_SQRT_BIT(14); ISQRT_SQRT_BIT(13); ISQRT_SQRT_BIT(12); ISQRT_SQRT_BIT(11); ISQRT_SQRT_BIT(10);
19 ISQRT_SQRT_BIT(9); ISQRT_SQRT_BIT(8); ISQRT_SQRT_BIT(7); ISQRT_SQRT_BIT(6); ISQRT_SQRT_BIT(5);
20 ISQRT_SQRT_BIT(4); ISQRT_SQRT_BIT(3); ISQRT_SQRT_BIT(2); ISQRT_SQRT_BIT(1);
21 if (n
> s
<<1) s
|= 1UL;
27 __attribute__((constructor
)) static void _ctor_sqrttbl (void) {
28 for (size_t f
= 0; f
< sizeof(sqrttbl
)/sizeof(sqrttbl
[0]); ++f
) sqrttbl
[f
] = isqrt0(f
);
32 static inline unsigned int isqrt (unsigned int n
) {
33 return (n
< sizeof(sqrttbl
)/sizeof(sqrttbl
[0]) ? sqrttbl
[n
] : isqrt0(n
));
38 static ccBool r_lights_additive = CC_FALSE;
39 CONVAR_BOOL(r_lights_additive, r_lights_additive, 0, "additive intensity lights?")
41 static int r_lights_additive
= 0;
44 ////////////////////////////////////////////////////////////////////////////////
45 void (*lb_monster_hit_by_light
) (int mnum
, int x
, int y
, int alpha
);
48 ////////////////////////////////////////////////////////////////////////////////
49 /* lightbuf format (bytes):
51 * (b,g,r) are premultiplied (wow! i'm smart!)
53 static Uint32 lightbuf
[MAP_RES_Y
*MAP_RES_X
]; /* keeps bgra values */
56 ////////////////////////////////////////////////////////////////////////////////
58 int player_light
= 0; /* is this player light? player light lights monsters */
61 ////////////////////////////////////////////////////////////////////////////////
62 /* current light parameters for tracer */
63 static int z_light_mindist
= 0;
64 static int lcx
, lcy
, lrad
;
69 ////////////////////////////////////////////////////////////////////////////////
70 static inline void lb_add_rgb (int x
, int y
, Uint32 bgr
, Uint8 alpha
) {
71 Uint32
*pos
= lightbuf
+320*y
+x
, aa
;
72 if (!r_lights_additive
) {
73 aa
= K8MAX(alpha
, (pos
[0]>>24));
75 aa
= byteclamp((pos
[0]>>24)+alpha
);
77 // premultiply light color
79 Uint32 add
, add_nc
, cbits
, cmask
;
81 ((((bgr
&0xff00ff)*(alpha
+1)+0x800080)>>8)&0xff00ff)|
82 ((((bgr
&0x00ff00)*(alpha
+1)+0x008000)>>8)&0x00ff00);
83 // you are not expected to understand this
84 // but if you are really curious, this is
85 // 8-bit rgb adding with clamping
86 add
= (pos
[0]&0xffffffu
)+bgr
;
87 add_nc
= (pos
[0]&0xffffffu
)^bgr
;
88 cbits
= (add
^add_nc
)&0x01010100;
89 cmask
= cbits
-(cbits
>>8);
90 bgr
= ((add
^cbits
)|cmask
)&0xffffffu
;
93 *pos
= (pos
[0]&0x00ffffffu
)|(aa
<<24);
98 ////////////////////////////////////////////////////////////////////////////////
99 static void lb_put_light_point (int x
, int y
) {
100 int dst
= isqrt((x
-lcx
)*(x
-lcx
)+(y
-lcy
)*(y
-lcy
));
101 if (dst
>= z_light_mindist
) {
102 int a
= lalpha
-(dst
*lalpha
/lrad
);
103 if (a
> 0 && mapbmp_orig
[y
][x
] < 16) {
104 if (a
> 255) a
= 255;
106 if (a
> 128) a
= 128;
107 if (player_light
&& monbmp
[y
][x
]) {
108 /* player light hits some monster */
109 lb_monster_hit_by_light(monbmp
[y
][x
]-1, x
, y
, a
);
111 lb_add_rgb(x
, y
, lbgr
, a
);
117 ////////////////////////////////////////////////////////////////////////////////
118 static Uint8
*lightmask
= NULL
; /* bitmap; bit 31 == leftmost pos */
119 static int lm_wdt
= 0, lm_hgt
= 0;
122 static void prepare_lightmask (void) {
123 int wdt
= lrad
*2+1, hgt
= wdt
;
124 if (wdt
*hgt
> lm_wdt
*lm_hgt
) {
125 lightmask
= realloc(lightmask
, wdt
*hgt
*sizeof(lightmask
[0]));
126 if (lightmask
== NULL
) {
127 fprintf(stderr
, "ZLIGHT: out of memory for lightmask!\n");
133 memset(lightmask
, 0, lm_wdt
*lm_hgt
*sizeof(lightmask
[0]));
137 static inline void lb_light_plot (int x
, int y
) {
141 if (x
< 0 || y
< 0 || x
>= lm_wdt
|| y
>= lm_hgt
) abort();
147 static void lb_light_hline (int x
, int y
, int len
) {
151 if (x
< 0 || y
< 0 || x
+len
-1 >= lm_wdt
|| y
>= lm_hgt
) abort();
153 memset(lightmask
+a
, 1, len
);
157 ////////////////////////////////////////////////////////////////////////////////
158 static void realize_lightmask (void) {
160 for (int dy
= 0; dy
< lrad
*2; ++dy
, ++sy
) {
163 for (int dx
= 0; dx
< lrad
*2; ++dx
, ++a
, ++sx
) {
164 if (lightmask
[a
]) lb_put_light_point(sx
, sy
);
170 ////////////////////////////////////////////////////////////////////////////////
171 static int trace_point_count
;
178 void t_draw_line_no_last (int x0
, int y0
, int x1
, int y1
) {
179 int dx
= K8ABS(x1
-x0
), sx
= (x0
< x1
? 1 : -1);
180 int dy
= -K8ABS(y1
-y0
), sy
= (y0
< y1
? 1 : -1);
181 int err
= dx
+dy
, e2
; /* error value e_xy */
183 if (x0
== x1
&& y0
== y1
) break;
184 //put_pixel(x0, y0, col);
185 if (x0
>= 0 && y0
>= 0 && x0
< MAP_RES_X
&& y0
< MAP_RES_Y
) {
186 //lb_put_light_point(x0, y0);
187 //lb_add_rgb(x0, y0, lbgr, 255);
188 lb_light_plot(x0
, y0
);
191 if (e2
>= dy
) { err
+= dy
; x0
+= sx
; } /* e_xy+e_x > 0 */
192 if (e2
<= dx
) { err
+= dx
; y0
+= sy
; } /* e_xy+e_y < 0 */
197 static void trace_start (void) {
198 trace_point_count
= 0;
203 static void trace_end (void) {
204 if (trace_point_count
> 1 && trace_plist
[trace_point_count
-1].x
== trace_plist
[0].x
&& trace_plist
[trace_point_count
-1].y
== trace_plist
[0].y
) {
207 if (trace_point_count
> 2) {
208 int op
= trace_point_count
-1;
209 for (int pp
= 0; pp
< trace_point_count
; op
= pp
++) {
210 t_draw_line_no_last(trace_plist
[op
].x
, trace_plist
[op
].y
, trace_plist
[pp
].x
, trace_plist
[pp
].y
);
216 static inline void trace_add_point (int x
, int y
) {
217 if (!trace_point_count
|| trace_plist
[trace_point_count
-1].x
!= x
|| trace_plist
[trace_point_count
-1].y
!= y
) {
218 trace_plist
[trace_point_count
].x
= x
;
219 trace_plist
[trace_point_count
].y
= y
;
222 polymod_add_vertex(x
, y
);
226 static void trace_to_point (int x1
, int y1
) {
227 int x0
= lcx
, y0
= lcy
;
228 int dx
= K8ABS(x1
-x0
), sx
= (x0
< x1
? 1 : -1);
229 int dy
= -K8ABS(y1
-y0
), sy
= (y0
< y1
? 1 : -1);
230 int err
= dx
+dy
, e2
; /* error value e_xy */
231 int ox
= x0
, oy
= y0
, dst
= lrad
*lrad
;
233 if ((x0
-lcx
)*(x0
-lcx
)+(y0
-lcy
)*(y0
-lcy
) >= dst
) {
234 trace_add_point(x0
, y0
);
237 if (mapbmp_orig
[y0
][x0
] >= 16) {
238 trace_add_point(ox
, oy
);
243 if (e2
>= dy
) { err
+= dy
; x0
+= sx
; } /* e_xy+e_x > 0 */
244 if (e2
<= dx
) { err
+= dx
; y0
+= sy
; } /* e_xy+e_y < 0 */
249 static inline double deg2rad (double angle
) {
250 return angle
*0.017453292519943295f
; // (angle/180)*Math.PI;
262 static const char *ename
[4] = {"RIGHT","BOTTOM","LEFT","TOP"};
265 /* find intersection point between square (lcx-lrad,lcy-lrad)-(lcx+lrad,lcy+lrad)
266 * and ray originating from (lcx,lcy) fired at angle 'angle' degrees;
267 * also return edge code */
268 static void ray_edge_intersection (int angle
, int *ix
, int *iy
, int *edge
) {
271 //angle = ((angle%360)+360)%360;
274 case 0: *ix
= lcx
+lrad
; *iy
= lcy
; *edge
= EDGE_RIGHT
; return;
275 case 45: *ix
= lcx
+lrad
; *iy
= lcy
+lrad
; *edge
= EDGE_RIGHT
/*|EDGE_BOTTOM*/; return;
276 case 90: *ix
= lcx
; *iy
= lcy
+lrad
; *edge
= EDGE_BOTTOM
; return;
277 case 135: *ix
= lcx
-lrad
; *iy
= lcy
+lrad
; *edge
= /*EDGE_LEFT|*/EDGE_BOTTOM
; return;
278 case 180: *ix
= lcx
-lrad
; *iy
= lcy
; *edge
= EDGE_LEFT
; return;
279 case 225: *ix
= lcx
-lrad
; *iy
= lcy
-lrad
; *edge
= EDGE_LEFT
/*|EDGE_TOP*/; return;
280 case 270: *ix
= lcx
; *iy
= lcy
-lrad
; *edge
= EDGE_TOP
; return;
281 case 315: *ix
= lcx
+lrad
; *iy
= lcy
-lrad
; *edge
= /*EDGE_RIGHT|*/EDGE_TOP
; return;
283 /* now determine outcode */
284 /* 316..359; 0..45: right side */
285 /* 46..135: bottom side */
286 /* 136..225: left side */
287 /* 226..315: top side */
288 if (angle
>= 315 || angle
<= 45) outcode
= EDGE_RIGHT
;
289 else if (angle
>= 46 && angle
<= 135) outcode
= EDGE_BOTTOM
;
290 else if (angle
>= 136 && angle
<= 225) outcode
= EDGE_LEFT
;
291 else if (angle
>= 226 && angle
<= 315) outcode
= EDGE_TOP
;
292 else abort(); /* the thing that shoild not be */
293 /* set ex and ey to reasonable big value */
294 rad
= deg2rad(angle
);
295 ex
= lcx
+cos(rad
)*1000;//(lrad*2+100);
296 ey
= lcy
+sin(rad
)*1000;//(lrad*2+100);
297 /* and find intersection point */
298 /* use formulas y = y0+slope*(x-x0), x = x0+(1/slope)*(y-y0) */
299 if (outcode
== EDGE_TOP
) {
300 *ix
= lcx
+(ex
-lcx
)*((lcy
-lrad
)-lcy
)/(ey
-lcy
);
302 } else if (outcode
== EDGE_BOTTOM
) {
303 *ix
= lcx
+(ex
-lcx
)*((lcy
+lrad
)-lcy
)/(ey
-lcy
);
305 } else if (outcode
== EDGE_RIGHT
) {
306 *iy
= lcy
+(ey
-lcy
)*((lcx
+lrad
)-lcx
)/(ex
-lcx
);
308 } else if (outcode
== EDGE_LEFT
) {
309 *iy
= lcy
+(ey
-lcy
)*((lcx
-lrad
)-lcx
)/(ex
-lcx
);
312 abort(); /* the thing that shoild not be */
318 /******************************************************************************/
319 /* funny recursive 'shadowcasting' algo from roguebasin */
320 /******************************************************************************/
321 static inline int light_is_blocked (int x
, int y
) {
322 return (x
< 0 || y
< 0 || x
>= MAP_RES_X
|| y
>= MAP_RES_Y
|| mapbmp_orig
[y
][x
] >= 16);
326 // recursive lightcasting function
327 static void light_octant (int cx
, int cy
, int row
, double start
, double end
, int radius
, int octant
) {
329 // multipliers for transforming coordinates to other octants
330 static const int mult
[4][8] = {
331 {1, 0, 0,-1,-1, 0, 0, 1},
332 {0, 1,-1, 0, 0,-1, 1, 0},
333 {0, 1, 1, 0, 0,-1,-1, 0},
334 {1, 0, 0, 1,-1, 0, 0,-1},
336 int xx
= mult
[0][octant
];
337 int xy
= mult
[1][octant
];
338 int yx
= mult
[2][octant
];
339 int yy
= mult
[3][octant
];
340 int radius_sqr
= radius
*radius
;
341 for (int j
= row
; j
<= radius
; ++j
) {
343 int dy
= -j
, dysqr
= dy
*dy
;
345 double new_start
= 666.666;
346 int x
= cx
+dx
*xx
+dy
*xy
;
347 int y
= cy
+dx
*yx
+dy
*yy
;
348 double dyp5
= dy
+0.5;
349 double dym5
= dy
-0.5;
352 // translate the dx, dy coordinates into map coordinates
355 // l_slope and r_slope store the slopes of the left and right
356 // extremities of the tile we're considering
357 double l_slope
= (dx
-0.5)/dyp5
;
358 double r_slope
= (dx
+0.5)/dym5
;
359 if (end
> l_slope
) break;
360 if (start
>= r_slope
) {
361 // our light beam is touching this tile; light it
362 int blk
= light_is_blocked(x
, y
);
364 // we're scanning a row of blocked squares
370 if (dx
*dx
+dysqr
< radius_sqr
) lb_light_plot(x
, y
);
373 if (blk
&& j
< radius
) {
374 // this is a blocking tile, start a child scan
376 light_octant(cx
, cy
, j
+1, start
, l_slope
, radius
, octant
);
379 if (!blk
&& dx
*dx
+dysqr
< radius_sqr
) lb_light_plot(x
, y
);
384 // row is scanned; do next row unless last tile was blocked
389 /******************************************************************************/
392 /* lcx, lcy, lrad: light parameters */
393 /* as: start angle, ae: end ange; same: do full circle */
394 static void trace_light (int as
, int ae
) {
396 //int ang = (int)(atan2f(ye-cy, xe-cx)*180.0f/M_PI);
397 //ang = (ang+360)%360;
398 int ang = atan2i(ye-cy, xe-cx);
400 int asx
= ((as
%360)+360)%360;
401 int aex
= ((ae
%360)+360)%360;
404 /* do full circle (square, actually) */
407 for (int d
= -lrad
; d
< lrad
; ++d
) trace_to_point(lcx
+d
, lcy
-lrad
);
409 for (int d
= -lrad
; d
< lrad
; ++d
) trace_to_point(lcx
+lrad
, lcy
+d
);
411 for (int d
= lrad
; d
> -lrad
; --d
) trace_to_point(lcx
+d
, lcy
+lrad
);
413 for (int d
= lrad
; d
> -lrad
; --d
) trace_to_point(lcx
-lrad
, lcy
+d
);
415 for (int oct
= 0; oct
< 8; ++oct
) light_octant(lcx
, lcy
, 1, 1.0, 0.0, lrad
, oct
);
416 lb_light_plot(lcx
, lcy
);
419 /* do light sector */
422 int dir
= (as
< ae
? 1 : -1); /* -1: counter-clockwise; 1: clockwise */
423 /* now determine starting and ending coords */
424 ray_edge_intersection(asx
, &sx
, &sy
, &sedge
);
425 ray_edge_intersection(aex
, &ex
, &ey
, &eedge
);
426 /* now process edges */
428 fprintf(stderr
, "---------------------------\n(%d,%d)-(%d,%d)\n", lcx
-lrad
, lcy
-lrad
, lcx
+lrad
, lcy
+lrad
);
429 fprintf(stderr
, "as=%d; asx=%d; ae=%d; aex=%d; sx=%d; sy=%d; sedge=%s; ex=%d; ey=%d; eedge=%s\n", as
, asx
, ae
, aex
, sx
, sy
, ename
[sedge
], ex
, ey
, ename
[eedge
]);
431 trace_add_point(lcx
, lcy
);
432 for (int f
= 0; f
<= 4; ++f
) {
433 int delta
, dx
, dy
, eex
, eey
;
434 /* determine side delta */
435 delta
= (sedge
== EDGE_RIGHT
|| sedge
== EDGE_TOP
? dir
: -dir
);
437 case EDGE_RIGHT
: eex
= lcx
+lrad
; eey
= lcy
+lrad
*delta
; dx
= 0; dy
= delta
; break;
438 case EDGE_BOTTOM
: eex
= lcx
+lrad
*delta
; eey
= lcy
+lrad
; dx
= delta
; dy
= 0; break;
439 case EDGE_LEFT
: eex
= lcx
-lrad
; eey
= lcy
+lrad
*delta
; dx
= 0; dy
= delta
; break;
440 case EDGE_TOP
: eex
= lcx
+lrad
*delta
; eey
= lcy
-lrad
; dx
= delta
; dy
= 0; break;
441 default: abort(); /* the thing that shoild not be */
443 if (light_dump
) fprintf(stderr
, "EDGE: %s; sx=%d; sy=%d; xs=%d; ys=%d; delta=%d; dir=%d\n", ename
[sedge
], sx
, sy
, K8SIGN(ex
-sx
), K8SIGN(ey
-sy
), delta
, dir
);
444 if (sedge
== eedge
&& (f
> 0 || dir
== 1)) {
445 /* this is the last edge */
446 if (light_dump
) fprintf(stderr
, "LAST EDGE! ex=%d; ey=%d; dx=%d; dy=%d\n", ex
, ey
, dx
, dy
);
447 while (sx
!= ex
|| sy
!= ey
) {
448 trace_to_point(sx
, sy
);
452 /* that's all, folks */
455 /* this is not the last edge, trace it fully */
456 if (light_dump
) fprintf(stderr
, " eex=%d; eey=%d; dx=%d; dy=%d\n", eex
, eey
, dx
, dy
);
457 while (sx
!= eex
|| sy
!= eey
) {
458 trace_to_point(sx
, sy
);
462 /* go to the next side */
463 sedge
= (sedge
+4+dir
)&0x03;
471 ////////////////////////////////////////////////////////////////////////////////
472 void lb_reset (void) {
473 memset(lightbuf
, 0, sizeof(lightbuf
));
477 void lb_draw (void) {
478 const Uint8
*msrc
= (void *)mapbmp
;
479 const Uint32
*lbsrc
= lightbuf
;
480 Uint32
*dest
= sdl_vscr
;
481 for (size_t f
= 0; f
< MAP_RES_X
*MAP_RES_Y
; ++f
) {
482 Uint32 mbgr
= (palette
[*msrc
]&0xffffff);
483 if ((lbsrc
[0]&0xff000000u
) > 0) {
484 /*if (mapbmp_orig[f/SCR_WIDTH][f%SCR_WIDTH] == msrc[0])*/ {
486 Sint32 alpha
= 256-(lbsrc
[0]>>24);
487 if (mapbmp_orig
[f
/SCR_WIDTH
][f
%SCR_WIDTH
] != msrc
[0]) {
488 alpha
= 256-byteclamp((lbsrc
[0]>>24)+50);
490 Uint32 drb
= (mbgr
&0xff00ff), dg
= (mbgr
&0x00ff00);
491 // real pixel, do alpha
492 drb
= (drb
+(((-drb
)*alpha
+0x800080)>>8))&0xff00ff;
493 dg
= (dg
+(((-dg
)*alpha
+0x008000)>>8))&0x00ff00;
494 // lights, premultiplied
495 if (lbsrc
[0]&0xffffffu
) {
496 Uint32 add
, add_nc
, cbits
, cmask
, bgr
= (drb
|dg
);
497 // you are not expected to understand this
498 // but if you are really curious, this is
499 // 8-bit rgb adding with clamping
500 add
= (lbsrc
[0]&0xffffffu
)+bgr
;
501 add_nc
= (lbsrc
[0]&0xffffffu
)^bgr
;
502 cbits
= (add
^add_nc
)&0x01010100;
503 cmask
= cbits
-(cbits
>>8);
504 mbgr
= ((add
^cbits
)|cmask
)&0xffffffu
;
520 ////////////////////////////////////////////////////////////////////////////////
521 void render_warfog_sphere (int cx
, int cy
, int radius
) {
522 for (int y
= cy
-radius
; y
<= cy
+radius
; ++y
) {
523 for (int x
= cx
-radius
; x
<= cx
+radius
; ++x
) {
524 if (x
>= 0 && y
>= 0 && x
< MAP_RES_X
&& y
< MAP_RES_Y
) {
525 int dst
= isqrt((x
-cx
)*(x
-cx
)+(y
-cy
)*(y
-cy
));
526 int a
= 255-(dst
*255/radius
);
527 if (a
> 0 && mapbmp_orig
[y
][x
] >= 16) {
528 if (a
> 255) a
= 255;
529 lb_add_rgb(x
, y
, /*lbgr*/0, a
);
537 ////////////////////////////////////////////////////////////////////////////////
538 /* x, y: map coordinates (in pixels) */
539 /* alpha is actually light intensity */
540 void renderlight_sector (int x
, int y
, int radius
, int as
, int ae
, Uint32 rgba
) {
541 //fprintf(stderr, "x=%d; y=%d; sx=%d; sy=%d\n", x, y, sx, sy);
542 if (x
-radius
>= MAP_RES_X
|| x
+radius
< 0 || y
-radius
>= MAP_RES_Y
|| y
+radius
< 0) { z_light_mindist
= 0; return; }
543 if (radius
< 2 /*|| fldhit1(x, y)*/ || (rgba
&0xff000000u
) == 0 || z_light_mindist
>= radius
) { z_light_mindist
= 0; return; }
544 if (mapbmp_orig
[y
][x
] >= 16) return;
549 lbgr
= (rgba
&0xffffff);
558 ////////////////////////////////////////////////////////////////////////////////
570 #define MAX_LIGHTS (4096)
572 static light_info_t lights
[MAX_LIGHTS
];
573 static int light_count
;
574 static int level_light_count
;
577 ////////////////////////////////////////////////////////////////////////////////
578 void reset_level_lights (void) {
579 level_light_count
= light_count
= 0;
583 void reset_lights (void) {
584 light_count
= level_light_count
;
588 void fix_level_lights (void) {
589 level_light_count
= light_count
;
593 int add_light_sector_mr (int x
, int y
, int radius
, int as
, int ae
, Uint32 rgba
, int minrad
) {
594 if (light_count
< MAX_LIGHTS
&& radius
>= 2 && (rgba
&0xff000000u
) > 0 && minrad
<= radius
) {
595 lights
[light_count
].x
= x
;
596 lights
[light_count
].y
= y
;
597 lights
[light_count
].radius
= radius
;
598 lights
[light_count
].as
= as
;
599 lights
[light_count
].ae
= ae
;
600 lights
[light_count
].rgba
= rgba
;
601 lights
[light_count
].minrad
= minrad
;
602 return light_count
++;
608 int add_light_sector (int x
, int y
, int radius
, int as
, int ae
, Uint32 rgba
) {
609 return add_light_sector_mr(x
, y
, radius
, as
, ae
, rgba
, 0);
613 void render_lights (void) {
614 const light_info_t
*lt
= lights
;
615 for (int f
= light_count
; f
> 0; --f
, ++lt
) {
616 z_light_mindist
= lt
->minrad
;
618 renderlight_sector(lt
->x
, lt
->y
, lt
->radius
, lt
->as
, lt
->ae
, lt
->rgba
);
623 ////////////////////////////////////////////////////////////////////////////////
624 static __attribute__((constructor
)) void _ctor_polymod_init (void) {
625 //polymod_put_pixel = lb_put_light_point;
626 polymod_hline
= lb_light_hline
;
627 polymod_screen_width
= MAP_RES_X
;
628 polymod_screen_height
= MAP_RES_Y
;