4 #include "romimage.hpp"
5 #include "framebuffer.hpp"
6 #include "messages.hpp"
7 #include "instance.hpp"
8 #include "library/minmax.hpp"
11 Point of escape is approximately at normalized y=0.16.
12 Baseline is approximately at normalized y=0.52
13 1st tile after starts approximately at y=0.38
22 //Projection parameters.
23 //Inverse of normalized y0 at baseline.
24 const double z0
= 1.9;
25 //Depth projection scale.
26 const double zscale
= 0.6;
27 //Point of escape on screen.
28 const double yescape
= 0.16;
29 //Horizontal draw scale.
30 const double hscale
= 0.15;
31 //Vertical draw scale.
32 const double vscale
= 0.1;
33 //Normalized floor thickness.
34 const double fthickness
= 0.33;
36 //Type of point representation.
43 const unsigned top_palette
[] = {61, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
44 const unsigned front_palette
[] = {62, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30};
45 const unsigned right_palette
[] = {63, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45};
46 const unsigned left_palette
[] = {64, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60};
47 const double slopepoints
[] = {-3.45, -2.45, -1.45, 0, 1.45, 2.45, 3.45};
48 const double angles
[] = {1.35, 1.07, 0.88, 0.745};
50 double calc_slope(double x
)
52 return 1.6 * (hscale
* x
) / (1 / z0
- yescape
);
55 std::pair
<signed, signed> point_project(double x
, double y
, double z
)
57 double c
= (z0
+ zscale
* z
);
61 double s
= (y0
- yescape
) / (1 / z0
- yescape
);
63 return std::make_pair(FB_WIDTH
* (hscale
* x
* s
+ 0.5), FB_HEIGHT
* (y0
- vscale
* y
* s
));
65 return std::make_pair(FB_WIDTH
/ 2, FB_HEIGHT
* yescape
);
68 point_t
point_project_b(double x
, double y
, double z
)
70 double c
= (z0
+ zscale
* z
);
74 double s
= (y0
- yescape
) / (1 / z0
- yescape
);
77 p
.x
= FB_WIDTH
* (hscale
* x
* s
+ 0.5);
78 p
.y
= FB_HEIGHT
* (y0
- vscale
* y
* s
);
81 p
.y
= FB_HEIGHT
* yescape
;
86 int32_t ship_sprite_normal(uint16_t hpos
, int vdir
, uint8_t thruster
)
89 if(hpos
< 18048) hdir
= 0;
90 else if(hpos
< 23936) hdir
= 1;
91 else if(hpos
< 29824) hdir
= 2;
92 else if(hpos
< 35712) hdir
= 3;
93 else if(hpos
< 41600) hdir
= 4;
94 else if(hpos
< 47488) hdir
= 5;
96 if(vdir
== -1) vdir
= 2;
97 return 9 * hdir
+ 3 * vdir
+ thruster
+ 14;
100 int32_t ship_sprite_explode(int32_t frame
)
102 return (frame
< 42) ? (frame
/ 3) : -1;
105 int32_t ship_sprite(gstate
& s
)
108 return ship_sprite_explode(s
.p
.expframe
);
109 if(s
.p
.death
== physics::death_finished
)
113 if(s
.p
.vspeed
> 300) vdir
= 1;
114 else if(s
.p
.vspeed
< -300) vdir
= -1;
116 if(s
.p
.death
== physics::death_fuel
)
119 thruster
= s
.rng
.pull() % 3;
120 return ship_sprite_normal(s
.p
.hpos
, vdir
, thruster
% 3);
123 void draw_sprite(struct instance
& inst
, double h
, double v
, int32_t num
)
127 signed x
= FB_WIDTH
* hscale
* h
+ FB_WIDTH
/ 2 - FB_SCALE
* 15;
128 signed y
= FB_HEIGHT
/ z0
- FB_HEIGHT
* vscale
* v
- FB_SCALE
* inst
.ship
.width
+ FB_SCALE
;
129 uint32_t offset
= inst
.ship
.width
* 30 * num
;
130 //For some darn reason, CARS.LZS has the image rotated 90 degrees counterclockwise.
131 for(signed j
= 0; j
< FB_SCALE
* (int)inst
.ship
.width
; j
++) {
132 if(y
+ j
< 0 || y
+ j
> inst
.overlap_end
)
134 uint32_t offset2
= offset
+ j
/ FB_SCALE
;
135 size_t base
= FB_WIDTH
* (y
+ j
) + x
;
136 for(signed i
= 0; i
< 30 * FB_SCALE
; i
++) {
139 if(x
+ i
>= FB_WIDTH
)
141 uint8_t c
= inst
.ship
.decode
[offset2
+ inst
.ship
.width
* (i
/ FB_SCALE
)];
144 uint32_t pix
= inst
.ship
.palette
[c
] & 0xFFFFFF;
145 if((inst
.framebuffer
[base
+ i
] >> 24) == 0)
146 inst
.framebuffer
[base
+ i
] = pix
;
151 void draw_grav_g_meter(struct instance
& inst
)
153 uint16_t realgrav
= 100 * (inst
.state
.p
.gravity
- 3);
156 static const size_t sep
= strlen(_numbers_g
) / 10;
158 uint8_t digit
= realgrav
% 10;
159 draw_block2(inst
, _numbers_g
+ sep
* digit
, y
* 320 + (x
- 5), inst
.dashpalette
[6],
160 inst
.dashpalette
[5], true);
166 void draw_indicator(struct instance
& inst
, uint8_t& curval
, uint8_t newval
, gauge
& g
, uint8_t on1
,
167 uint8_t on2
, uint8_t off1
, uint8_t off2
)
169 unsigned tmp
= newval
;
170 if(tmp
> g
.maxlimit()) tmp
= g
.maxlimit();
172 for(unsigned i
= curval
; i
< tmp
; i
++) {
173 draw_block(inst
, g
.get_data(i
), g
.get_position(i
), inst
.dashpalette
[on1
],
174 inst
.dashpalette
[on2
]);
177 for(unsigned i
= tmp
; i
< curval
; i
++) {
178 draw_block(inst
, g
.get_data(i
), g
.get_position(i
), inst
.dashpalette
[off1
],
179 inst
.dashpalette
[off2
]);
184 void draw_gauges(struct instance
& inst
)
186 //draw_grav_g_meter(s);
187 uint8_t timermod
= ((inst
.state
.p
.framecounter
% 9) > 4) ? 1 : 0;
188 draw_indicator(inst
, inst
.state
.speedind
, (inst
.state
.p
.lspeed
- inst
.state
.p
.speedbias
) / 0x141,
189 inst
.speed_dat
, 2, 3, 0, 1);
190 draw_indicator(inst
, inst
.state
.o2ind
, (inst
.state
.p
.o2_left
+ 0xbb7) / 0xbb8, inst
.oxydisp_dat
, 2, 3,
192 draw_indicator(inst
, inst
.state
.fuelind
, (inst
.state
.p
.fuel_left
+ 0xbb7) / 0xbb8, inst
.fueldisp_dat
,
195 bool lck
= inst
.state
.p
.is_set(physics::flag_locked
);
196 if(lck
!= inst
.state
.lockind
)
197 draw_block2(inst
, _lockind_g
+ (lck
? strlen(_lockind_g
) / 2 : 0), 0x9c * 320 + 0xcb,
198 inst
.dashpalette
[6], inst
.dashpalette
[5], true);
199 inst
.state
.lockind
= lck
;
200 //Out of oxygen blink&beep.
201 if(inst
.state
.p
.death
== physics::death_o2
&& inst
.state
.beep_phase
!= timermod
) {
202 blink_between(inst
, 0xa0, 0xa1, 7, 7, inst
.dashpalette
[7], inst
.dashpalette
[8]);
204 inst
.gsfx(sound_beep
);
206 //Out of fuel blink&beep.
207 if(inst
.state
.p
.death
== physics::death_fuel
&& inst
.state
.beep_phase
!= timermod
) {
208 blink_between(inst
, 0x9b, 0xa9, 16, 5, inst
.dashpalette
[7], inst
.dashpalette
[8]);
210 inst
.gsfx(sound_beep
);
213 uint32_t res
= inst
.state
.curlevel
.apparent_length() / (29 * FB_SCALE
+ 1);
214 size_t tmp
= (inst
.state
.p
.lpos
- 3 * 65536) / res
;
215 for(unsigned i
= inst
.state
.distind
; i
< tmp
&& i
< (29 * FB_SCALE
+ 1); i
++)
216 draw_distance_column(inst
, i
, inst
.dashpalette
[4]);
217 inst
.state
.distind
= tmp
;
218 inst
.state
.beep_phase
= timermod
;
221 inline double horizon_distance() { return (1 / yescape
- z0
) / zscale
; }
222 inline double near_distance(struct instance
& inst
) { return (600 / inst
.overlap_end
- z0
) / zscale
; }
225 void draw_quad_x(struct instance
& inst
, point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
)
227 if(fabs(p1
.x
- p2
.x
) < 0.1)
229 double qp1
= min(p1
.x
, p2
.x
);
230 double qp2
= max(p1
.x
, p2
.x
);
233 double utmax
= max(p1
.y
, p2
.y
);
234 double utmin
= min(p1
.y
, p2
.y
);
235 double ltmax
= max(p3
.y
, p4
.y
);
236 double ltmin
= min(p3
.y
, p4
.y
);
237 signed y1
= max((int)floor(utmin
), 0);
238 signed y2
= min((int)ceil(ltmax
), (int)inst
.overlap_end
);
239 for(signed j
= y1
; j
< y2
; j
++) {
240 signed xstart
= x1
, xend
= x2
;
243 xstart
= floor((qp1
- qp2
) * (j
- p2
.y
) / (p1
.y
- p2
.y
) + qp2
);
245 xend
= ceil((qp2
- qp1
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + qp1
);
249 xend
= ceil((qp1
- qp2
) * (j
- p4
.y
) / (p3
.y
- p4
.y
) + qp2
);
251 xstart
= floor((qp2
- qp1
) * (j
- p3
.y
) / (p4
.y
- p3
.y
) + qp1
);
253 xstart
= max(xstart
, 0);
254 xend
= min(xend
, FB_WIDTH
);
255 size_t base
= FB_WIDTH
* j
;
256 if(j
< inst
.overlap_start
)
257 for(signed i
= xstart
; i
< xend
; i
++)
258 inst
.framebuffer
[base
+ i
] = color
;
260 for(signed i
= xstart
; i
< xend
; i
++)
261 framebuffer_blend2(inst
.framebuffer
[base
+ i
], color
);
265 void draw_quad_y(struct instance
& inst
, point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
)
267 if(fabs(p1
.y
- p3
.y
) < 0.1)
269 signed y1
= max((int)floor(p1
.y
), 0);
270 signed y2
= min((int)ceil(p3
.y
), (int)inst
.overlap_end
);
271 for(signed j
= y1
; j
< y2
; j
++) {
273 xstart
= floor((p3
.x
- p1
.x
) * (j
- p1
.y
) / (p3
.y
- p1
.y
) + p1
.x
);
274 xend
= ceil((p4
.x
- p2
.x
) * (j
- p1
.y
) / (p3
.y
- p1
.y
) + p2
.x
);
275 xstart
= max(xstart
, 0);
276 xend
= min(xend
, FB_WIDTH
);
277 size_t base
= FB_WIDTH
* j
;
278 if(j
< inst
.overlap_start
)
279 for(signed i
= xstart
; i
< xend
; i
++)
280 inst
.framebuffer
[base
+ i
] = color
;
282 for(signed i
= xstart
; i
< xend
; i
++)
283 framebuffer_blend2(inst
.framebuffer
[base
+ i
], color
);
287 void draw_quad_z(struct instance
& inst
, point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
)
289 if(fabs(p1
.x
- p2
.x
) < 0.1 || fabs(p1
.y
- p3
.y
) < 0.1)
291 signed x1
= max((int)floor(p1
.x
), 0);
292 signed x2
= min((int)ceil(p2
.x
), FB_WIDTH
);
293 signed y1
= max((int)floor(p1
.y
), 0);
294 signed y2
= min((int)ceil(p3
.y
), (int)inst
.overlap_end
);
295 for(signed j
= y1
; j
< y2
; j
++) {
296 size_t base
= FB_WIDTH
* j
;
297 if(j
< inst
.overlap_start
)
298 for(signed i
= x1
; i
< x2
; i
++)
299 inst
.framebuffer
[base
+ i
] = color
;
301 for(signed i
= x1
; i
< x2
; i
++)
302 framebuffer_blend2(inst
.framebuffer
[base
+ i
], color
);
306 void draw_quad_z_tunshadow(struct instance
& inst
, point_t p1
, point_t p2
, point_t p3
, point_t p4
,
307 uint32_t color
, int x
)
309 if(fabs(p1
.x
- p2
.x
) < 0.1 || fabs(p1
.y
- p3
.y
) < 0.1)
312 return; //Center pipe has no shadow.
313 signed x1
= max((int)floor(p1
.x
), 0);
314 signed x2
= min((int)ceil(p2
.x
), FB_WIDTH
);
315 signed y1
= max((int)floor(p1
.y
), 0);
316 signed y2
= min((int)ceil(p3
.y
), (int)inst
.overlap_end
);
317 double slope
= calc_slope(slopepoints
[x
+ 3]);
318 const double width
= p2
.x
- p1
.x
;
319 const double hwidth
= 0.45 * width
;
320 const double center
= (p1
.x
+ p2
.x
) / 2;
321 for(signed j
= y1
; j
< y2
; j
++) {
322 signed c1
= x2
, c2
= x1
;
323 size_t base
= FB_WIDTH
* j
;
324 double ry
= (p3
.y
- j
) / (0.625 * vscale
/ hscale
* 2 * hwidth
);
326 c1
= center
- hwidth
* sqrt(1 - ry
* ry
);
327 c2
= center
+ hwidth
* sqrt(1 - ry
* ry
);
330 //The shadow is on left.
331 c2
= center
- hwidth
- slope
* (p3
.y
- j
);
333 //The shadow is on right.
334 c1
= center
+ hwidth
- slope
* (p3
.y
- j
);
337 c2
= min(c2
, FB_WIDTH
);
338 if(j
< inst
.overlap_start
)
339 for(signed i
= c1
; i
< c2
; i
++)
340 inst
.framebuffer
[base
+ i
] = color
;
342 for(signed i
= c1
; i
< c2
; i
++)
343 framebuffer_blend2(inst
.framebuffer
[base
+ i
], color
);
347 void draw_quad_z_tunnel(struct instance
& inst
, point_t p1
, point_t p2
, point_t p3
, point_t p4
, uint32_t color
)
349 if(fabs(p1
.x
- p2
.x
) < 0.1 || fabs(p1
.y
- p3
.y
) < 0.1)
351 signed x1
= max((int)floor(p1
.x
), 0);
352 signed x2
= min((int)ceil(p2
.x
), FB_WIDTH
);
353 signed y1
= max((int)floor(p1
.y
), 0);
354 signed y2
= min((int)ceil(p3
.y
), (int)inst
.overlap_end
);
355 const double width
= p2
.x
- p1
.x
;
356 const double hwidth
= 0.45 * width
;
357 const double center
= (p1
.x
+ p2
.x
) / 2;
358 for(signed j
= y1
; j
< y2
; j
++) {
359 signed c1
= x2
, c2
= x2
;
360 size_t base
= FB_WIDTH
* j
;
361 double ry
= (p3
.y
- j
) / (0.625 * vscale
/ hscale
* 2 * hwidth
);
363 c1
= center
- hwidth
* sqrt(1 - ry
* ry
);
364 c2
= center
+ hwidth
* sqrt(1 - ry
* ry
);
368 x2
= min(x2
, FB_WIDTH
);
369 c1
= min(c1
, FB_WIDTH
);
370 if(j
< inst
.overlap_start
) {
371 for(signed i
= x1
; i
< c1
; i
++)
372 inst
.framebuffer
[base
+ i
] = color
;
373 for(signed i
= c2
; i
< x2
; i
++)
374 inst
.framebuffer
[base
+ i
] = color
;
376 for(signed i
= x1
; i
< c1
; i
++)
377 framebuffer_blend2(inst
.framebuffer
[base
+ i
], color
);
378 for(signed i
= c2
; i
< x2
; i
++)
379 framebuffer_blend2(inst
.framebuffer
[base
+ i
], color
);
384 void draw_quad_z_pipefront(struct instance
& inst
, double z
, int x
, uint32_t color
)
386 auto p5
= point_project_b(x
- 0.5, 0, z
);
387 auto p6
= point_project_b(x
, 1, z
);
388 auto p7
= point_project_b(x
+ 0.5, 0, z
);
391 int16_t ymax
= ceil(ymaxd
);
392 if(ymaxd
- ymind
< 0.1)
394 int16_t cheight
= floor(p5
.y
- p6
.y
);
395 int16_t ciheight
= floor(0.9 * (p5
.y
- p6
.y
));
396 int16_t cwidth
= floor((p7
.x
- p5
.x
) / 2);
397 int16_t ciwidth
= floor(0.9 * (p7
.x
- p5
.x
) / 2);
398 int16_t ccenter
= floor((p7
.x
+ p5
.x
) / 2);
399 int16_t nxs
= ccenter
- cwidth
;
400 int16_t nxe
= ccenter
+ cwidth
;
401 for(signed j
= ymax
- cheight
; j
< ymax
; j
++) {
402 if(j
< 0 || j
> inst
.overlap_end
)
404 int16_t dstart
= nxs
;
406 int16_t cstart
= ccenter
;
407 int16_t cend
= ccenter
;
408 int16_t cistart
= ccenter
;
409 int16_t ciend
= ccenter
;
411 int16_t o
= ymax
- j
;
412 double o2
= 1.0 * o
/ cheight
;
413 int16_t w
= cwidth
* sqrt(1 - o2
* o2
);
417 if(j
>= ymax
- ciheight
) {
418 int16_t o
= ymax
- j
;
419 double o2
= 1.0 * o
/ ciheight
;
420 int16_t w
= ciwidth
* sqrt(1 - o2
* o2
);
424 dstart
= max(cstart
, (int16_t)0);
425 dend
= min(cend
, (int16_t)FB_WIDTH
);
426 size_t base
= FB_WIDTH
* j
;
427 if(j
< inst
.overlap_start
)
428 for(signed i
= dstart
; i
< dend
; i
++) {
429 if(i
< cistart
|| i
>= ciend
)
430 inst
.framebuffer
[base
+ i
] = color
;
433 for(signed i
= dstart
; i
< dend
; i
++) {
434 if(i
< cistart
|| i
>= ciend
)
435 framebuffer_blend2(inst
.framebuffer
[base
+ i
], color
);
440 // x = (k*y0+b)*c*(0.5*sin(alpha)+X0)
441 // y = -(k*y0+b)*d*cos(alpha)+y0
443 // dx/dy0 = k*c*0.5*sin(alpha)+k*c*X0
444 // dy/dy0 = k*d*cos(alpha)+1
445 // dx/dy = (k*c*0.5*sin(alpha)+k*c*X0)/(k*d*cos(alpha)+1)
446 // d/dalpha[(k*c*0.5*sin(alpha)+k*c*X0)/(k*d*cos(alpha)+1)]
447 //=(k*c*0.5*cos(alpha))/(k*d*cos(alpha)+1) +(k*c*0.5*sin(alpha)+k*c*X0)*(k*d*sin(alpha))/(k*d*cos(alpha)+1)^2
449 // 0.5*k*d+0.5*cos(alpha)+X0*(k*d*sin(alpha)) = 0
451 uint32_t mix_color(uint32_t* c
, uint32_t c2
)
453 uint32_t low
= c
[c2
/ 256];
454 uint32_t high
= c
[c2
/ 256 + 1];
455 uint16_t mod
= c2
% 256;
456 uint16_t imod
= 256 - mod
;
457 const uint32_t Lm
= 0x00FF00FF;
458 const uint32_t Hm
= 0xFF00FF00;
459 uint32_t L
= (((low
& Lm
) * imod
+ (high
& Lm
) * mod
) >> 8) & Lm
;
460 uint32_t H
= (((low
& Hm
) >> 8) * imod
+ ((high
& Hm
) >> 8) * mod
) & Hm
;
464 void rebuild_pipe_quad_cache(pipe_cache
& p
, int x
, uint32_t* c
)
466 double k
= 1 / (1 / z0
- yescape
);
467 double b
= -yescape
* k
;
468 double gmin
= 999999;
469 double gmax
= -999999;
470 double amin
= 999999;
471 double amax
= -999999;
472 double y00
= (1-b
)/k
;
473 for(unsigned i
= 0; i
< 256; i
++)
474 p
.colors
[i
] = 0xFFFFFFFF;
475 //Compute span range.
476 for(double a
= -M_PI
/ 2; a
< M_PI
/ 2; a
+= 0.01) {
477 double A
= vscale
* cos(a
);
478 double y0
= (y00
+ b
* A
) / (1 - k
* A
);
479 double xp
= (k
* y0
+ b
) * (x
+ 0.5 * sin(a
));
481 p
.min_h
= x
+ 0.5 * sin(a
);
487 p
.max_h
= x
+ 0.5 * sin(a
);
493 for(double a
= amin
; a
< amax
; a
+= 0.01) {
494 double A
= vscale
* cos(a
);
495 double y0
= (y00
+ b
* A
) / (1 - k
* A
);
496 double xp
= (k
* y0
+ b
) * (x
+ 0.5 * sin(a
));
497 signed c2
= (a
+ M_PI
/ 2) * 1280 / M_PI
;
498 signed x
= 255 * (xp
- gmin
) / (gmax
- gmin
);
499 c2
= max(min(c2
, 1280), 0);
500 x
= max(min(x
, 255), 0);
501 p
.colors
[x
] = mix_color(c
, c2
);
503 for(unsigned i
= 0; i
< 256; i
++) {
504 if(p
.colors
[i
] == 0xFFFFFFFF) {
506 for(unsigned j
= 0; j
< 255; j
++)
507 if(p
.colors
[j
] != 0xFFFFFFFF) {
508 p
.colors
[i
] = p
.colors
[j
];
512 p
.colors
[i
] = p
.colors
[i
- 1];
515 //for(unsigned i = 0; i < 256; i++)
516 // p.colors[i] = 0xFF8000 + i;
519 void rebuild_pipe_quad_caches(struct instance
& i
, uint32_t color1
, uint32_t color2
, uint32_t color3
,
529 for(int x
= 0; x
< 7; x
++)
530 rebuild_pipe_quad_cache(i
.pipecache
[x
], x
- 3, c
);
533 void draw_quad_y_pipe_last(struct instance
& inst
, double z
, int x
, bool top
)
535 struct pipe_cache
& p
= inst
.pipecache
[x
+ 3];
536 uint32_t fcolor
= (!top
&& z
< 0.2) ? 0x1000000 : 0;
537 //We need these for slope projection.
538 auto p1
= point_project_b(p
.min_h
, p
.min_v
, z
);
539 auto p2
= point_project_b(p
.max_h
, p
.max_v
, z
);
540 auto p3
= point_project_b(p
.min_h
, p
.min_v
, z
- 1);
541 auto p4
= point_project_b(p
.max_h
, p
.max_v
, z
- 1);
542 auto p5
= point_project_b(x
- 0.5, 0, z
);
543 auto p6
= point_project_b(x
, 1, z
);
544 auto p7
= point_project_b(x
+ 0.5, 0, z
);
545 if(p3
.y
- p1
.y
< 0.1)
547 if(p4
.y
- p2
.y
< 0.1)
550 int16_t ymax
= ceil(ymaxd
);
551 double sl1
= (p3
.x
- p1
.x
) / (p3
.y
- p1
.y
);
552 double sl2
= (p4
.x
- p2
.x
) / (p4
.y
- p2
.y
);
553 double c1
= p1
.x
- sl1
* p1
.y
;
554 double c2
= p2
.x
- sl2
* p2
.y
;
555 int16_t cheight
= floor(p5
.y
- p6
.y
);
556 int16_t cwidth
= floor((p7
.x
- p5
.x
) / 2);
557 int16_t ccenter
= floor((p7
.x
+ p5
.x
) / 2);
558 for(signed j
= ymax
- cheight
; j
< ymax
; j
++) {
559 if(j
< 0 || j
> inst
.overlap_end
)
561 int16_t nxs
= floor(sl1
* j
+ c1
);
562 int16_t nxe
= ceil(sl2
* j
+ c2
);
563 int16_t dstart
= nxs
;
567 uint32_t cstep
= 255 * 65536 / (nxe
- nxs
+ 1);
570 int16_t o
= ymax
- j
;
571 double o2
= 1.0 * o
/ cheight
;
572 int16_t w
= cwidth
* sqrt(1 - o2
* o2
);
573 dstart
= ccenter
- w
;
576 dstart
= max(max(dstart
, nxs
), (int16_t)0);
577 dend
= min(min(dend
, nxe
), (int16_t)FB_WIDTH
);
579 color
+= (dstart
- nxs
) * cstep
;
580 size_t base
= FB_WIDTH
* j
;
581 if(j
< inst
.overlap_start
)
582 for(signed i
= dstart
; i
< dend
; i
++) {
583 inst
.framebuffer
[base
+ i
] = fcolor
| p
.colors
[color
>> 16];
587 for(signed i
= dstart
; i
< dend
; i
++) {
588 framebuffer_blend2(inst
.framebuffer
[base
+ i
], fcolor
|
589 p
.colors
[color
>> 16]);
595 void draw_quad_y_pipe_first(struct instance
& inst
, double z1
, double z2
, int x
, bool top
)
597 struct pipe_cache
& p
= inst
.pipecache
[x
+ 3];
598 uint32_t fcolor
= (!top
&& z1
< 0) ? 0x1000000 : 0;
599 auto p1
= point_project_b(p
.min_h
, p
.min_v
, z2
);
600 auto p2
= point_project_b(p
.max_h
, p
.max_v
, z2
);
601 auto p3
= point_project_b(p
.min_h
, p
.min_v
, z1
);
602 auto p4
= point_project_b(p
.max_h
, p
.max_v
, z1
);
603 auto p5
= point_project_b(x
- 0.5, 0, z1
);
604 auto p6
= point_project_b(x
, 1, z1
);
605 auto p7
= point_project_b(x
+ 0.5, 0, z1
);
606 double ymind
= min(p1
.y
, p2
.y
);
608 int16_t ymin
= floor(ymind
);
609 int16_t ymax
= ceil(ymaxd
);
610 int16_t ymin2
= ceil(max(p1
.y
, p2
.y
));
611 if(p3
.y
- p1
.y
< 0.1)
613 if(p4
.y
- p2
.y
< 0.1)
615 double sl1
= (p3
.x
- p1
.x
) / (p3
.y
- p1
.y
);
616 double sl2
= (p4
.x
- p2
.x
) / (p4
.y
- p2
.y
);
617 double c1
= p1
.x
- sl1
* p1
.y
;
618 double c2
= p2
.x
- sl2
* p2
.y
;
619 int16_t cheight
= floor(p5
.y
- p6
.y
);
620 int16_t cwidth
= floor((p7
.x
- p5
.x
) / 2);
621 int16_t ccenter
= floor((p7
.x
+ p5
.x
) / 2);
623 uint16_t mindist
= 65535;
624 for(signed j
= ymin
; j
< ymax
; j
++) {
625 if(j
< 0 || j
> inst
.overlap_end
)
627 int16_t nxs
= floor(sl1
* j
+ c1
);
628 int16_t nxe
= ceil(sl2
* j
+ c2
);
629 int16_t dstart
= nxs
;
633 uint32_t cstep
= 255 * 65536 / (nxe
- nxs
+ 1);
635 int16_t cstart
= ccenter
;
636 int16_t cend
= ccenter
;
637 if(j
< ymin2
&& p1
.y
!= p2
.y
) {
638 //The upper triangular region.
640 dend
= ceil((p2
.x
- p1
.x
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + p1
.x
);
642 dstart
= floor((p2
.x
- p1
.x
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + p1
.x
);
644 if(j
>= ymax
- cheight
) {
645 int16_t o
= ymax
- j
;
646 double o2
= 1.0 * o
/ cheight
;
647 int16_t w
= cwidth
* sqrt(1 - o2
* o2
);
652 dstart
= max((int16_t)dstart
, cstart
);
654 dend
= min((int16_t)dend
, cend
);
658 dist
= cstart
- dstart
;
668 dstart
= max(max(dstart
, nxs
), (int16_t)0);
669 dend
= min(min(dend
, nxe
), (int16_t)FB_WIDTH
);
671 color
+= (dstart
- nxs
) * cstep
;
672 size_t base
= FB_WIDTH
* j
;
673 if(j
< inst
.overlap_start
)
674 for(signed i
= dstart
; i
< dend
; i
++) {
675 if(i
< cstart
|| i
>= cend
)
676 inst
.framebuffer
[base
+ i
] = fcolor
| p
.colors
[color
>> 16];
680 for(signed i
= dstart
; i
< dend
; i
++) {
681 if(i
< cstart
|| i
>= cend
)
682 framebuffer_blend2(inst
.framebuffer
[base
+ i
], fcolor
|
683 p
.colors
[color
>> 16]);
689 void draw_quad_y_pipe(struct instance
& inst
, double z1
, double z2
, int x
, bool top
)
691 struct pipe_cache
& p
= inst
.pipecache
[x
+ 3];
692 uint32_t fcolor
= (!top
&& z1
< 0.2) ? 0x1000000 : 0;
693 auto p1
= point_project_b(p
.min_h
, p
.min_v
, z2
);
694 auto p2
= point_project_b(p
.max_h
, p
.max_v
, z2
);
695 auto p3
= point_project_b(p
.min_h
, p
.min_v
, z1
);
696 auto p4
= point_project_b(p
.max_h
, p
.max_v
, z1
);
697 double ymind
= min(p1
.y
, p2
.y
);
698 double ymaxd
= max(p3
.y
, p4
.y
);
699 int16_t ymin
= floor(ymind
);
700 int16_t ymax
= ceil(ymaxd
) + 1;
701 int16_t ymin2
= ceil(max(p1
.y
, p2
.y
));
702 int16_t ymax2
= floor(min(p3
.y
, p4
.y
));
703 if(p3
.y
- p1
.y
< 0.1)
705 if(p4
.y
- p2
.y
< 0.1)
707 double sl1
= (p3
.x
- p1
.x
) / (p3
.y
- p1
.y
);
708 double sl2
= (p4
.x
- p2
.x
) / (p4
.y
- p2
.y
);
709 double c1
= p1
.x
- sl1
* p1
.y
;
710 double c2
= p2
.x
- sl2
* p2
.y
;
711 //FIXME: This leaves some graphical glitching in seams.
712 for(signed j
= ymin
; j
< ymax
; j
++) {
713 if(j
< 0 || j
> inst
.overlap_end
)
715 int16_t nxs
= floor(sl1
* j
+ c1
);
716 int16_t nxe
= ceil(sl2
* j
+ c2
);
717 int16_t dstart
= nxs
;
721 uint32_t cstep
= 255 * 65536 / (nxe
- nxs
+ 1);
723 if(j
< ymin2
&& p1
.y
!= p2
.y
) {
724 //The upper triangular region.
726 dend
= ceil((p2
.x
- p1
.x
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + p1
.x
);
728 dstart
= floor((p2
.x
- p1
.x
) * (j
- p1
.y
) / (p2
.y
- p1
.y
) + p1
.x
);
730 if(j
>= ymax2
&& p3
.y
!= p4
.y
) {
731 //The lower triangular region.
733 dstart
= floor((p4
.x
- p3
.x
) * (j
- p3
.y
- 1) / (p4
.y
- p3
.y
) + p3
.x
);
735 dend
= ceil((p4
.x
- p3
.x
) * (j
- p3
.y
- 1) / (p4
.y
- p3
.y
) + p3
.x
);
737 dstart
= max(max(dstart
, nxs
), (int16_t)0);
738 dend
= min(min(dend
, nxe
), (int16_t)FB_WIDTH
);
740 color
+= (dstart
- nxs
) * cstep
;
741 size_t base
= FB_WIDTH
* j
;
742 if(j
< inst
.overlap_start
)
743 for(signed i
= dstart
; i
< dend
; i
++) {
744 inst
.framebuffer
[base
+ i
] = fcolor
| p
.colors
[color
>> 16];
748 for(signed i
= dstart
; i
< dend
; i
++) {
749 framebuffer_blend2(inst
.framebuffer
[base
+ i
], fcolor
| p
.colors
[color
>> 16]);
755 void draw_level(struct instance
& inst
)
757 for(unsigned i
= 0; i
< inst
.overlap_start
; i
++)
758 for(unsigned j
= 0; j
< FB_WIDTH
; j
++)
759 inst
.framebuffer
[i
* FB_WIDTH
+ j
] = inst
.origbuffer
[(i
/ FB_SCALE
) * 320 +
761 for(unsigned i
= inst
.overlap_start
; i
< inst
.overlap_end
; i
++)
762 for(unsigned j
= 0; j
< FB_WIDTH
; j
++)
763 framebuffer_blend2(inst
.framebuffer
[i
* FB_WIDTH
+ j
], inst
.origbuffer
[
764 (i
/ FB_SCALE
) * 320 + (j
/ FB_SCALE
)]);
765 static const signed dorder
[] = {-3, 3, -2, 2, -1, 1, 0};
766 level
& l
= inst
.state
.curlevel
;
767 double zship
= inst
.state
.p
.lpos
/ 65536.0;
768 double horizon
= horizon_distance();
769 double near
= near_distance(inst
);
770 signed maxtile
= ceil(zship
+ horizon
);
771 signed mintile
= floor(zship
+ near
- 1);
772 for(signed zt
= maxtile
; zt
>= mintile
; zt
--) {
773 for(signed dxt
= 0; dxt
< 7; dxt
++) {
774 signed xt
= dorder
[dxt
];
775 tile t
= l
.at_tile(zt
, xt
);
776 tile tleft
= l
.at_tile(zt
, xt
- 1);
777 tile tright
= l
.at_tile(zt
, xt
+ 1);
778 tile tfront
= l
.at_tile(zt
- 1, xt
);
779 tile tback
= l
.at_tile(zt
+ 1, xt
);
780 //First, draw the floor level.
781 if(t
.lower_floor() && (!t
.is_rblock() || t
.is_tunnel())) {
782 bool ifront
= (inst
.state
.p
.vpos
< 10240);
783 ifront
&= (xt
>= 0 || inst
.state
.p
.hpos
< 5888 * xt
+ 32768 - 2944);
784 ifront
&= (xt
<= 0 || inst
.state
.p
.hpos
> 5888 * xt
+ 32768 + 2944);
785 draw_quad_y(inst
, point_project_b(xt
- 0.5, 0, zt
+ 1 - zship
),
786 point_project_b(xt
+ 0.5, 0, zt
+ 1 - zship
),
787 point_project_b(xt
- 0.5, 0, zt
- zship
),
788 point_project_b(xt
+ 0.5, 0, zt
- zship
),
789 l
.get_palette_color(top_palette
[t
.lower_floor()], ifront
));
792 if(t
.is_tunnel() && !tfront
.is_block()) {
793 //Pipe shadow never obscures the ship.
794 draw_quad_z_tunshadow(inst
, point_project_b(xt
- 0.5, 1, zt
- zship
),
795 point_project_b(xt
+ 0.5, 1, zt
- zship
),
796 point_project_b(xt
- 0.5, 0, zt
- zship
),
797 point_project_b(xt
+ 0.5, 0, zt
- zship
),
798 l
.get_palette_color(t
.is_rblock() ? 65 : 67), xt
);
800 //Draw the top surface.
802 signed q
= t
.apparent_height();
803 bool ifront
= (inst
.state
.p
.vpos
< 10240 + q
* 2560);
804 ifront
&= (xt
>= 0 || inst
.state
.p
.hpos
< 5888 * xt
+ 32768 - 2944);
805 ifront
&= (xt
<= 0 || inst
.state
.p
.hpos
> 5888 * xt
+ 32768 + 2944);
806 ifront
|= (l
.in_pipe(zt
* 65536, inst
.state
.p
.hpos
, inst
.state
.p
.vpos
) &&
808 draw_quad_y(inst
, point_project_b(xt
- 0.5, q
, zt
+ 1 - zship
),
809 point_project_b(xt
+ 0.5, q
, zt
+ 1 - zship
),
810 point_project_b(xt
- 0.5, q
, zt
- zship
),
811 point_project_b(xt
+ 0.5, q
, zt
- zship
),
812 l
.get_palette_color(top_palette
[t
.upper_floor()], ifront
));
814 //Left/Right block surface.
815 if(t
.is_rblock() && xt
< 0 && tright
.apparent_height() < t
.apparent_height()) {
816 signed q1
= tright
.apparent_height();
817 signed q2
= t
.apparent_height();
818 bool ifront
= (inst
.state
.p
.vpos
< 10240 + q2
* 2560);
819 ifront
&= (inst
.state
.p
.hpos
< 5888 * xt
+ 32768 + 2944);
820 draw_quad_x(inst
, point_project_b(xt
+ 0.5, q2
, zt
- zship
),
821 point_project_b(xt
+ 0.5, q2
, zt
+ 1 - zship
),
822 point_project_b(xt
+ 0.5, q1
, zt
- zship
),
823 point_project_b(xt
+ 0.5, q1
, zt
+ 1 - zship
),
824 l
.get_palette_color(63, ifront
));
826 if(t
.is_rblock() && xt
> 0 && tleft
.apparent_height() < t
.apparent_height()) {
827 signed q1
= tleft
.apparent_height();
828 signed q2
= t
.apparent_height();
829 bool ifront
= (inst
.state
.p
.vpos
< 10240 + q2
* 2560);
830 ifront
&= (inst
.state
.p
.hpos
> 5888 * xt
+ 32768 - 2944);
831 draw_quad_x(inst
, point_project_b(xt
- 0.5, q2
, zt
+ 1 - zship
),
832 point_project_b(xt
- 0.5, q2
, zt
- zship
),
833 point_project_b(xt
- 0.5, q1
, zt
+ 1- zship
),
834 point_project_b(xt
- 0.5, q1
, zt
- zship
),
835 l
.get_palette_color(64, ifront
));
837 //Front block surface.
838 if(t
.is_rblock() && tfront
.apparent_height() < t
.apparent_height()) {
839 signed q1
= tfront
.apparent_height();
840 signed q2
= t
.apparent_height();
841 bool ifront
= (zt
< zship
);
842 if(t
.is_tunnel() && !tfront
.is_block())
843 draw_quad_z_tunnel(inst
, point_project_b(xt
- 0.5, q2
, zt
- zship
),
844 point_project_b(xt
+ 0.5, q2
, zt
- zship
),
845 point_project_b(xt
- 0.5, q1
, zt
- zship
),
846 point_project_b(xt
+ 0.5, q1
, zt
- zship
),
847 l
.get_palette_color(62, ifront
));
849 draw_quad_z(inst
, point_project_b(xt
- 0.5, q2
, zt
- zship
),
850 point_project_b(xt
+ 0.5, q2
, zt
- zship
),
851 point_project_b(xt
- 0.5, q1
, zt
- zship
),
852 point_project_b(xt
+ 0.5, q1
, zt
- zship
),
853 l
.get_palette_color(62, ifront
));
857 if(t
.is_tunnel() && !t
.is_rblock()) {
858 bool top
= (inst
.state
.p
.vpos
> 10240);
859 top
&= !l
.in_pipe(zt
* 65536, inst
.state
.p
.hpos
, inst
.state
.p
.vpos
);
860 if(tback
.is_rblock() || !tback
.is_block())
861 draw_quad_y_pipe_last(inst
, zt
+ 1 - zship
, xt
, top
);
862 //Standalone tunnel top.
863 if(tfront
.is_block())
864 draw_quad_y_pipe(inst
, zt
- zship
, zt
+ 1 - zship
, xt
, top
);
866 draw_quad_y_pipe_first(inst
, zt
- zship
, zt
+ 1 - zship
, xt
, top
);
867 //Standalone tunnel front.
868 if(!tfront
.is_block()) {
869 bool ifront
= (zt
< zship
);
870 draw_quad_z_pipefront(inst
, zt
- zship
, xt
, l
.get_palette_color(66,
874 //Left/Right floor surface.
875 if(xt
< 0 && t
.lower_floor() && !tright
.lower_floor()) {
876 bool ifront
= (inst
.state
.p
.vpos
< 10240);
877 ifront
&= (inst
.state
.p
.hpos
> 5888 * xt
+ 32768);
878 draw_quad_x(inst
, point_project_b(xt
+ 0.5, 0, zt
- zship
),
879 point_project_b(xt
+ 0.5, 0, zt
+ 1 - zship
),
880 point_project_b(xt
+ 0.5, -fthickness
, zt
- zship
),
881 point_project_b(xt
+ 0.5, -fthickness
, zt
+ 1 - zship
),
882 l
.get_palette_color(right_palette
[t
.lower_floor()]));
884 if(xt
> 0 && t
.lower_floor() && !tleft
.lower_floor()) {
885 bool ifront
= (inst
.state
.p
.vpos
< 10240);
886 ifront
&= (inst
.state
.p
.hpos
> 5888 * xt
+ 32768);
887 draw_quad_x(inst
, point_project_b(xt
- 0.5, 0, zt
+ 1 - zship
),
888 point_project_b(xt
- 0.5, 0, zt
- zship
),
889 point_project_b(xt
- 0.5, -fthickness
, zt
+ 1- zship
),
890 point_project_b(xt
- 0.5, -fthickness
, zt
- zship
),
891 l
.get_palette_color(left_palette
[t
.lower_floor()], ifront
));
893 //Front floor surface.
894 if(t
.lower_floor() && !tfront
.lower_floor()) {
895 bool ifront
= (inst
.state
.p
.vpos
< 10240);
896 ifront
&= (inst
.state
.p
.hpos
< 5888 * xt
+ 32768);
897 draw_quad_z(inst
, point_project_b(xt
- 0.5, 0, zt
- zship
),
898 point_project_b(xt
+ 0.5, 0, zt
- zship
),
899 point_project_b(xt
- 0.5, -fthickness
, zt
- zship
),
900 point_project_b(xt
+ 0.5, -fthickness
, zt
- zship
),
901 l
.get_palette_color(front_palette
[t
.lower_floor()], ifront
));
905 draw_sprite(inst
, (inst
.state
.p
.hpos
- 32768.0) / 5888.0, (inst
.state
.p
.vpos
- 10240.0) / 2560.0,
906 ship_sprite(inst
.state
));
909 const char* const period
= "%&(ccK";
910 const char* const dash
= "%'(cScS";
911 const char* const vline
= "%$(b";
912 const char* const tback
= "%F*ccccccccccccccccccccccccccccccccccccccccc";
914 void draw_timeattack_time(struct instance
& inst
, const char* msg
)
917 uint16_t nst
= strlen(_numbers_g
) / 10;
918 draw_block2(inst
, tback
, 0, 0xFFFFFF, 0xFFFFFF, false);
920 if(*msg
>= '0' && *msg
<= '9') {
921 draw_block2(inst
, _numbers_g
+ (*msg
- '0') * nst
, w
, 0xFFFFFF, 0xFFFFFF, true);
922 draw_block2(inst
, vline
, w
+ 4, 0xFFFFFF, 0xFFFFFF, true);
924 } else if(*msg
== ':') {
925 draw_block2(inst
, period
, w
, 0xFFFFFF, 0xFFFFFF, true);
927 } else if(*msg
== '-') {
928 draw_block2(inst
, dash
, w
, 0xFFFFFF, 0xFFFFFF, true);
929 draw_block2(inst
, vline
, w
+ 4, 0xFFFFFF, 0xFFFFFF, true);
934 for(unsigned i
= 0; i
< 7; i
++)
935 for(unsigned j
= 0; j
< 35; j
++)
936 inst
.origbuffer
[320 * i
+ j
] ^= 0xFFFFFF;
937 render_framebuffer_update(inst
, 0, 0, 35, 7);
940 void draw_timeattack_time(struct instance
& inst
, uint16_t frames
)
944 strcpy(msg
, "----:--");
946 unsigned seconds
= 18227 * frames
/ 656250;
947 unsigned subseconds
= 36454U * frames
/ 13125 - 100 * seconds
;
948 sprintf(msg
, "%u:%02u", seconds
, subseconds
);
950 draw_timeattack_time(inst
, msg
);