romdevtools 0.44.0 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +572 -0
- package/README.md +9 -7
- package/examples/dreamcast/hello/main.c +24 -0
- package/examples/n64/platformer/main.c +158 -0
- package/examples/n64/puzzle/main.c +117 -0
- package/examples/n64/racing/main.c +147 -0
- package/examples/n64/shmup/main.c +122 -0
- package/examples/n64/sports/main.c +127 -0
- package/examples/ps1/platformer/main.c +158 -0
- package/examples/ps1/puzzle/main.c +125 -0
- package/examples/ps1/racing/main.c +147 -0
- package/examples/ps1/shmup/main.c +192 -0
- package/examples/ps1/sports/main.c +127 -0
- package/examples/ps1/sprite_move/main.c +38 -0
- package/package.json +11 -2
- package/src/analysis/analyze.js +224 -29
- package/src/analysis/decompile.js +7 -0
- package/src/analysis/decompiler/sleigh/mips.ldefs +25 -0
- package/src/analysis/decompiler/sleigh/mips32.pspec +78 -0
- package/src/analysis/decompiler/sleigh/mips32be.cspec +107 -0
- package/src/analysis/decompiler/sleigh/mips32be.sla +211162 -0
- package/src/analysis/decompiler/sleigh/mips32le.cspec +106 -0
- package/src/analysis/decompiler/sleigh/mips32le.sla +210624 -0
- package/src/analysis/rizin.js +24 -1
- package/src/cores/capabilities.js +122 -2
- package/src/cores/registry.js +17 -0
- package/src/host/LibretroGL.js +273 -0
- package/src/host/LibretroGLBridge.js +836 -0
- package/src/host/LibretroHost.js +203 -3
- package/src/host/callbacks.js +22 -2
- package/src/host/coreLoader.js +65 -5
- package/src/host/cpu-state.js +27 -0
- package/src/host/framebuffer.js +14 -1
- package/src/host/glOptionalDep.js +60 -0
- package/src/host/n64-ai-state.js +43 -0
- package/src/host/ps1-spu-state.js +65 -0
- package/src/host/retroConstants.js +14 -0
- package/src/mcp/tools/disasm.js +2 -0
- package/src/mcp/tools/frame.js +18 -9
- package/src/mcp/tools/lifecycle.js +1 -1
- package/src/mcp/tools/platform-tools.js +20 -4
- package/src/mcp/tools/platforms.js +38 -4
- package/src/mcp/tools/project.js +116 -0
- package/src/mcp/tools/rendering-context.js +2 -1
- package/src/mcp/tools/toolchain.js +1 -1
- package/src/platforms/n64/lib/c/n64.c +196 -0
- package/src/platforms/n64/lib/c/n64.h +68 -0
- package/src/platforms/ps1/lib/c/psx.c +200 -0
- package/src/platforms/ps1/lib/c/psx.h +83 -0
- package/src/toolchains/index.js +65 -0
- package/src/toolchains/mips-c/lib/be/libc.a +0 -0
- package/src/toolchains/mips-c/lib/be/libgcc.a +0 -0
- package/src/toolchains/mips-c/lib/be/libm.a +0 -0
- package/src/toolchains/mips-c/lib/el/libc.a +0 -0
- package/src/toolchains/mips-c/lib/el/libm.a +0 -0
- package/src/toolchains/mips-c/lib/n64-crt0.s +21 -0
- package/src/toolchains/mips-c/lib/n64-ipl3.s +30 -0
- package/src/toolchains/mips-c/lib/n64.ld +15 -0
- package/src/toolchains/mips-c/lib/ps1-crt0.s +20 -0
- package/src/toolchains/mips-c/lib/ps1.ld +15 -0
- package/src/toolchains/mips-c/lib/softint.c +37 -0
- package/src/toolchains/mips-c/mips-c.js +155 -0
- package/src/toolchains/mips-elf-gcc/gcc.js +130 -0
- package/src/toolchains/sh-c/lib/dc-crt0.s +23 -0
- package/src/toolchains/sh-c/lib/dc.h +102 -0
- package/src/toolchains/sh-c/lib/dc.ld +11 -0
- package/src/toolchains/sh-c/lib/libc.a +0 -0
- package/src/toolchains/sh-c/lib/libgcc.a +0 -0
- package/src/toolchains/sh-c/lib/libm.a +0 -0
- package/src/toolchains/sh-c/sh-c.js +101 -0
- package/src/toolchains/sh-elf-gcc/gcc.js +122 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* platformer/main.c — BLOCK HOP: a 3D Nintendo 64 platformer.
|
|
3
|
+
*
|
|
4
|
+
* A 3D character (cube hero) runs and jumps across floating platforms rendered in
|
|
5
|
+
* perspective. Gravity + jump physics in 16.16 fixed point, AABB landing tests
|
|
6
|
+
* against platform tops, coins to collect, a pit you can fall into (lose a life),
|
|
7
|
+
* a chase camera that follows the hero. Title -> play -> game-over, score + lives.
|
|
8
|
+
*
|
|
9
|
+
* Build: build({ platform:"n64", language:"c" }). Controls: LEFT/RIGHT run,
|
|
10
|
+
* CROSS jump, START begin/restart.
|
|
11
|
+
*
|
|
12
|
+
* 3D technique: platforms are flat-topped boxes drawn as no-cull quads; the hero
|
|
13
|
+
* is a culled cube. The camera tracks the hero's X with a slight downward tilt so
|
|
14
|
+
* you see the platform tops. Coins are small spinning cubes.
|
|
15
|
+
*/
|
|
16
|
+
#include "n64.h"
|
|
17
|
+
|
|
18
|
+
#define NPLAT 6
|
|
19
|
+
#define NCOIN 6
|
|
20
|
+
enum { TITLE, PLAY, OVER };
|
|
21
|
+
|
|
22
|
+
typedef struct { fix x, y, z, w; } Plat; /* top-center + half-width */
|
|
23
|
+
static Plat plat[NPLAT];
|
|
24
|
+
static struct { fix x, y, z; int got; } coin[NCOIN];
|
|
25
|
+
|
|
26
|
+
static fix hx, hy, vy; /* hero pos + vertical velocity */
|
|
27
|
+
static int onground;
|
|
28
|
+
static int state, score, lives, hi;
|
|
29
|
+
static fix cam_x, spin;
|
|
30
|
+
static unsigned int prev_pad;
|
|
31
|
+
|
|
32
|
+
static void box_top(fix cx, fix top, fix z, fix hw, fix depth, unsigned short col)
|
|
33
|
+
{
|
|
34
|
+
Vec3 a,b,c,d;
|
|
35
|
+
a.x=cx-hw; a.y=top; a.z=z-depth; b.x=cx+hw; b.y=top; b.z=z-depth;
|
|
36
|
+
c.x=cx+hw; c.y=top; c.z=z+depth; d.x=cx-hw; d.y=top; d.z=z+depth;
|
|
37
|
+
n64_quad3d_nc(a,b,c,d,col); /* top face */
|
|
38
|
+
/* front face for thickness */
|
|
39
|
+
a.y=top; b.y=top; c.y=top-FIX(2); d.y=top-FIX(2);
|
|
40
|
+
a.x=cx-hw; a.z=z+depth; b.x=cx+hw; b.z=z+depth; c.x=cx+hw; c.z=z+depth; d.x=cx-hw; d.z=z+depth;
|
|
41
|
+
n64_quad3d_nc(a,b,c,d, col - RGB(20,20,20));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static void draw_cube_at(fix x, fix y, fix z, fix s, fix yaw, unsigned short col)
|
|
45
|
+
{
|
|
46
|
+
Vec3 v[8]; int i;
|
|
47
|
+
static const int sx[8]={-1,1,1,-1,-1,1,1,-1},sy[8]={-1,-1,1,1,-1,-1,1,1},sz[8]={-1,-1,-1,-1,1,1,1,1};
|
|
48
|
+
n64_model(x,y,z,yaw);
|
|
49
|
+
for(i=0;i<8;i++){v[i].x=sx[i]*s;v[i].y=sy[i]*s;v[i].z=sz[i]*s;}
|
|
50
|
+
n64_quad3d(v[0],v[1],v[2],v[3],col); n64_quad3d(v[5],v[4],v[7],v[6],col);
|
|
51
|
+
n64_quad3d(v[4],v[0],v[3],v[7],col); n64_quad3d(v[1],v[5],v[6],v[2],col);
|
|
52
|
+
n64_quad3d(v[4],v[5],v[1],v[0],col); n64_quad3d(v[3],v[2],v[6],v[7],col);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static void reset_game(void)
|
|
56
|
+
{
|
|
57
|
+
int i;
|
|
58
|
+
/* a run of platforms along +X with a gap (pit) in the middle */
|
|
59
|
+
for (i = 0; i < NPLAT; i++) {
|
|
60
|
+
plat[i].x = (fix)(i * 4 - 4) << 16;
|
|
61
|
+
plat[i].y = (fix)((i % 3) - 1) << 16;
|
|
62
|
+
plat[i].z = FIX(8);
|
|
63
|
+
plat[i].w = FIX(2);
|
|
64
|
+
}
|
|
65
|
+
plat[3].y = FIX(-6); /* the pit: a platform dropped far below */
|
|
66
|
+
for (i = 0; i < NCOIN; i++) { coin[i].x = (fix)(i*4-3)<<16; coin[i].y = plat[i].y + FIX(2); coin[i].z = FIX(8); coin[i].got = 0; }
|
|
67
|
+
hx = FIX(-4); hy = FIX(2); vy = 0; onground = 0;
|
|
68
|
+
score = 0; lives = 3;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* find the platform top under the hero's x (or a very low floor = pit). */
|
|
72
|
+
static fix ground_at(fix x)
|
|
73
|
+
{
|
|
74
|
+
int i; fix best = FIX(-20);
|
|
75
|
+
for (i = 0; i < NPLAT; i++) {
|
|
76
|
+
fix dx = x - plat[i].x; if (dx < 0) dx = -dx;
|
|
77
|
+
if (dx < plat[i].w) { if (plat[i].y > best) best = plat[i].y; }
|
|
78
|
+
}
|
|
79
|
+
return best;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static void update(void)
|
|
83
|
+
{
|
|
84
|
+
unsigned int pad = n64_pad();
|
|
85
|
+
int i;
|
|
86
|
+
if (pad & PAD_LEFT) hx -= FIXF(0.18f);
|
|
87
|
+
if (pad & PAD_RIGHT) hx += FIXF(0.18f);
|
|
88
|
+
if (hx < FIX(-6)) hx = FIX(-6); if (hx > FIX(14)) hx = FIX(14);
|
|
89
|
+
|
|
90
|
+
if ((pad & PAD_A) && !(prev_pad & PAD_A) && onground) { vy = FIXF(0.55f); onground = 0; }
|
|
91
|
+
|
|
92
|
+
vy -= FIXF(0.04f); /* gravity */
|
|
93
|
+
hy += vy;
|
|
94
|
+
{
|
|
95
|
+
fix g = ground_at(hx) + FIX(1); /* hero sits 1 unit above the platform top */
|
|
96
|
+
if (hy <= g && vy <= 0) {
|
|
97
|
+
if (g < FIX(-8)) { /* fell in the pit */
|
|
98
|
+
if (--lives <= 0) { if (score>hi) hi=score; state = OVER; }
|
|
99
|
+
hx = FIX(-4); hy = FIX(4); vy = 0;
|
|
100
|
+
} else { hy = g; vy = 0; onground = 1; }
|
|
101
|
+
} else onground = 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (i = 0; i < NCOIN; i++) if (!coin[i].got) {
|
|
105
|
+
fix dx = hx - coin[i].x, dy = hy - coin[i].y;
|
|
106
|
+
if (dx<0)dx=-dx; if (dy<0)dy=-dy;
|
|
107
|
+
if (dx < FIX(1) && dy < FIX(2)) { coin[i].got = 1; score += 100; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
cam_x += (hx - cam_x) >> 3; /* smooth follow */
|
|
111
|
+
spin += FIX(4);
|
|
112
|
+
prev_pad = pad;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static void render(void)
|
|
116
|
+
{
|
|
117
|
+
int i;
|
|
118
|
+
n64_clear(RGB(40, 60, 120)); /* sky */
|
|
119
|
+
n64_camera(cam_x, FIX(2), FIX(-1), 0, FIXF(-0.22f));
|
|
120
|
+
|
|
121
|
+
for (i = 0; i < NPLAT; i++)
|
|
122
|
+
box_top(plat[i].x, plat[i].y, plat[i].z, plat[i].w, FIX(2),
|
|
123
|
+
(i==3)?RGB(120,40,40):RGB(90, 150, 70));
|
|
124
|
+
for (i = 0; i < NCOIN; i++) if (!coin[i].got)
|
|
125
|
+
draw_cube_at(coin[i].x, coin[i].y, coin[i].z, FIXF(0.3f), spin, RGB(255, 220, 40));
|
|
126
|
+
|
|
127
|
+
draw_cube_at(hx, hy, FIX(8), FIXF(0.6f), 0, RGB(230, 90, 60));
|
|
128
|
+
|
|
129
|
+
n64_number(8, 6, (unsigned)score, RGB(255,255,255));
|
|
130
|
+
for (i = 0; i < lives; i++) n64_rect(290 - i*10, 8, 6, 6, RGB(230, 90, 60));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
int main(void)
|
|
134
|
+
{
|
|
135
|
+
n64_init();
|
|
136
|
+
n64_srand(0xBEEF);
|
|
137
|
+
state = TITLE; prev_pad = 0; cam_x = 0;
|
|
138
|
+
for (;;) {
|
|
139
|
+
unsigned int pad = n64_pad();
|
|
140
|
+
if (state == TITLE) {
|
|
141
|
+
n64_clear(RGB(20, 30, 60));
|
|
142
|
+
n64_camera(0, 0, FIX(-1), 0, 0);
|
|
143
|
+
spin += FIX(3);
|
|
144
|
+
draw_cube_at(0, 0, FIX(6), FIX(1), spin, RGB(230, 90, 60));
|
|
145
|
+
if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
|
|
146
|
+
prev_pad = pad;
|
|
147
|
+
} else if (state == PLAY) { update(); render(); }
|
|
148
|
+
else {
|
|
149
|
+
n64_clear(RGB(8, 8, 30));
|
|
150
|
+
n64_number(110, 100, (unsigned)score, RGB(255, 220, 40));
|
|
151
|
+
n64_number(120, 130, (unsigned)hi, RGB(255, 255, 120));
|
|
152
|
+
if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
|
|
153
|
+
prev_pad = pad;
|
|
154
|
+
}
|
|
155
|
+
n64_flip();
|
|
156
|
+
}
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* puzzle/main.c — DROP GRID 64: a 3D Nintendo 64 falling-block puzzle.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the PS1 puzzle (flat 2D), this is rendered in 3D — the N64 was a 3D-first
|
|
5
|
+
* machine, so the well is a perspective box and the blocks are shaded cubes you
|
|
6
|
+
* watch fall in depth. Same falling-block logic (move, drop, clear full rows, ramp
|
|
7
|
+
* speed, stack-out = game over) but presented through the 3D pipeline at an angle.
|
|
8
|
+
*
|
|
9
|
+
* Build: build({ platform:"n64", language:"c" }). Controls: LEFT/RIGHT move the
|
|
10
|
+
* falling block, DOWN soft-drop, START begin/restart.
|
|
11
|
+
*
|
|
12
|
+
* 3D technique: each grid cell is a cube at (col, -row, 0) in world space; a tilted
|
|
13
|
+
* camera looks into the well so you see depth. The board logic is plain integers.
|
|
14
|
+
*/
|
|
15
|
+
#include "n64.h"
|
|
16
|
+
|
|
17
|
+
#define GW 6
|
|
18
|
+
#define GH 10
|
|
19
|
+
enum { TITLE, PLAY, OVER };
|
|
20
|
+
|
|
21
|
+
static unsigned char grid[GH][GW];
|
|
22
|
+
static int fx, fy, fc;
|
|
23
|
+
static int tick, fall_period;
|
|
24
|
+
static int state, score, hi;
|
|
25
|
+
static unsigned int prev_pad;
|
|
26
|
+
|
|
27
|
+
static const unsigned short PAL[5] = {
|
|
28
|
+
0, RGB(230,60,60), RGB(60,200,90), RGB(80,150,255), RGB(240,200,60)
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
static void cube_at(fix x, fix y, fix z, fix s, unsigned short col)
|
|
32
|
+
{
|
|
33
|
+
Vec3 v[8]; int i;
|
|
34
|
+
static const int sx[8]={-1,1,1,-1,-1,1,1,-1},sy[8]={-1,-1,1,1,-1,-1,1,1},sz[8]={-1,-1,-1,-1,1,1,1,1};
|
|
35
|
+
n64_model(x,y,z,0);
|
|
36
|
+
for(i=0;i<8;i++){v[i].x=sx[i]*s;v[i].y=sy[i]*s;v[i].z=sz[i]*s;}
|
|
37
|
+
n64_quad3d(v[0],v[1],v[2],v[3],col); n64_quad3d(v[5],v[4],v[7],v[6],col);
|
|
38
|
+
n64_quad3d(v[4],v[0],v[3],v[7],col); n64_quad3d(v[1],v[5],v[6],v[2],col);
|
|
39
|
+
n64_quad3d(v[4],v[5],v[1],v[0],col); n64_quad3d(v[3],v[2],v[6],v[7],col);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* grid cell (c,r) → world position (centered, y up). */
|
|
43
|
+
static fix cellx(int c) { return (fix)(c - GW/2) << 16; }
|
|
44
|
+
static fix celly(int r) { return (fix)((GH/2) - r) << 16; }
|
|
45
|
+
|
|
46
|
+
static void new_block(void)
|
|
47
|
+
{ fx=GW/2; fy=0; fc=1+(n64_rand()%4); if(grid[0][fx]){ if(score>hi)hi=score; state=OVER; } }
|
|
48
|
+
|
|
49
|
+
static void reset_game(void)
|
|
50
|
+
{ int r,c; for(r=0;r<GH;r++)for(c=0;c<GW;c++)grid[r][c]=0; score=0; tick=0; fall_period=30; new_block(); }
|
|
51
|
+
|
|
52
|
+
static int blocked(int x,int y){ return x<0||x>=GW||y>=GH||(y>=0&&grid[y][x]); }
|
|
53
|
+
|
|
54
|
+
static void lock_and_clear(void)
|
|
55
|
+
{
|
|
56
|
+
int r,c,rr;
|
|
57
|
+
grid[fy][fx]=fc;
|
|
58
|
+
for(r=GH-1;r>=0;r--){
|
|
59
|
+
int full=1; for(c=0;c<GW;c++) if(!grid[r][c]){ full=0; break; }
|
|
60
|
+
if(full){ for(rr=r;rr>0;rr--)for(c=0;c<GW;c++)grid[rr][c]=grid[rr-1][c]; for(c=0;c<GW;c++)grid[0][c]=0; score+=100; if(fall_period>8)fall_period--; r++; }
|
|
61
|
+
}
|
|
62
|
+
new_block();
|
|
63
|
+
}
|
|
64
|
+
static void step_fall(void){ if(!blocked(fx,fy+1)) fy++; else lock_and_clear(); }
|
|
65
|
+
|
|
66
|
+
static void update(void)
|
|
67
|
+
{
|
|
68
|
+
unsigned int pad = n64_pad();
|
|
69
|
+
if((pad&PAD_LEFT)&&!(prev_pad&PAD_LEFT)&&!blocked(fx-1,fy)) fx--;
|
|
70
|
+
if((pad&PAD_RIGHT)&&!(prev_pad&PAD_RIGHT)&&!blocked(fx+1,fy)) fx++;
|
|
71
|
+
if(pad&PAD_DOWN){ step_fall(); tick=0; }
|
|
72
|
+
if(++tick>=fall_period){ step_fall(); tick=0; }
|
|
73
|
+
prev_pad=pad;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static void render(void)
|
|
77
|
+
{
|
|
78
|
+
int r,c;
|
|
79
|
+
n64_clear(RGB(12,12,24));
|
|
80
|
+
n64_camera(0,0,FIX(-12),0,FIXF(-0.05f)); /* look into the well */
|
|
81
|
+
/* settled blocks */
|
|
82
|
+
for(r=0;r<GH;r++) for(c=0;c<GW;c++) if(grid[r][c])
|
|
83
|
+
cube_at(cellx(c), celly(r), 0, FIXF(0.45f), PAL[grid[r][c]]);
|
|
84
|
+
/* falling block */
|
|
85
|
+
if(fy>=0) cube_at(cellx(fx), celly(fy), 0, FIXF(0.45f), PAL[fc]);
|
|
86
|
+
/* well floor + walls as dim cubes for depth cue */
|
|
87
|
+
for(c=-1;c<=GW;c++){ cube_at(cellx(c), celly(GH), 0, FIXF(0.45f), RGB(50,50,70)); }
|
|
88
|
+
for(r=0;r<=GH;r++){ cube_at(cellx(-1), celly(r), 0, FIXF(0.45f), RGB(40,40,60)); cube_at(cellx(GW), celly(r), 0, FIXF(0.45f), RGB(40,40,60)); }
|
|
89
|
+
n64_number(8,6,(unsigned)score,RGB(255,255,255));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
int main(void)
|
|
93
|
+
{
|
|
94
|
+
n64_init();
|
|
95
|
+
n64_srand(0xD0D0);
|
|
96
|
+
state=TITLE; prev_pad=0;
|
|
97
|
+
for(;;){
|
|
98
|
+
unsigned int pad = n64_pad();
|
|
99
|
+
if(state==TITLE){
|
|
100
|
+
static fix t; t+=FIX(3);
|
|
101
|
+
n64_clear(RGB(20,12,30));
|
|
102
|
+
n64_camera(0,0,FIX(-6),0,0);
|
|
103
|
+
n64_model(0,0,0,t); cube_at(0,0,0,FIX(1),RGB(80,150,255));
|
|
104
|
+
if((pad&PAD_START)&&!(prev_pad&PAD_START)){ reset_game(); state=PLAY; }
|
|
105
|
+
prev_pad=pad;
|
|
106
|
+
} else if(state==PLAY){ update(); render(); }
|
|
107
|
+
else {
|
|
108
|
+
n64_clear(RGB(30,8,8));
|
|
109
|
+
n64_number(110,100,(unsigned)score,RGB(255,120,120));
|
|
110
|
+
n64_number(120,130,(unsigned)hi,RGB(255,255,120));
|
|
111
|
+
if((pad&PAD_START)&&!(prev_pad&PAD_START)){ reset_game(); state=PLAY; }
|
|
112
|
+
prev_pad=pad;
|
|
113
|
+
}
|
|
114
|
+
n64_flip();
|
|
115
|
+
}
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* racing/main.c — POLE BENDER: a 3D Nintendo 64 racer.
|
|
3
|
+
*
|
|
4
|
+
* The road is a ribbon of quads receding to the horizon (real perspective, drawn
|
|
5
|
+
* far-to-near). You steer a car near the camera between the verges; the world
|
|
6
|
+
* scrolls toward you and the track curves. Rival cars (cubes) sit further up the
|
|
7
|
+
* road and grow as you close on them — collide and you spin out (lose time/score).
|
|
8
|
+
* Title -> race -> results state machine, distance score, a lap timer.
|
|
9
|
+
*
|
|
10
|
+
* Build: build({ platform:"n64", language:"c" }). Controls: LEFT/RIGHT steer,
|
|
11
|
+
* UP accelerate, DOWN brake, START begins/restarts.
|
|
12
|
+
*
|
|
13
|
+
* 3D technique: the track is N segments ahead; each segment is a quad between two
|
|
14
|
+
* road-edge pairs, its centerline x driven by a curve value so the road bends. The
|
|
15
|
+
* camera sits just behind/above the car. All 16.16 fixed point.
|
|
16
|
+
*/
|
|
17
|
+
#include "n64.h"
|
|
18
|
+
|
|
19
|
+
#define SEGS 14
|
|
20
|
+
enum { TITLE, RACE, DONE };
|
|
21
|
+
|
|
22
|
+
static fix car_x; /* lateral position of the car */
|
|
23
|
+
static fix scroll; /* how far we've travelled (for segment phase) */
|
|
24
|
+
static fix curve; /* current road curvature */
|
|
25
|
+
static fix speed;
|
|
26
|
+
static int state, score, best;
|
|
27
|
+
static int rivals_z[3], rival_x[3];
|
|
28
|
+
static unsigned int prev_pad;
|
|
29
|
+
|
|
30
|
+
static void reset_race(void)
|
|
31
|
+
{
|
|
32
|
+
int i;
|
|
33
|
+
car_x = 0; scroll = 0; curve = 0; speed = FIXF(0.4f); score = 0;
|
|
34
|
+
for (i = 0; i < 3; i++) { rivals_z[i] = 8 + i * 5; rival_x[i] = ((i * 97) % 5) - 2; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* a flat ground-plane quad at depth z0..z1, centered at lateral cx with half-width hw. */
|
|
38
|
+
static void road_quad(fix cx0, fix z0, fix cx1, fix z1, fix hw, unsigned short col)
|
|
39
|
+
{
|
|
40
|
+
Vec3 a, b, c, d;
|
|
41
|
+
a.x = cx0 - hw; a.y = FIXF(-2.0f); a.z = z0;
|
|
42
|
+
b.x = cx0 + hw; b.y = FIXF(-2.0f); b.z = z0;
|
|
43
|
+
c.x = cx1 + hw; c.y = FIXF(-2.0f); c.z = z1;
|
|
44
|
+
d.x = cx1 - hw; d.y = FIXF(-2.0f); d.z = z1;
|
|
45
|
+
/* ground plane: no back-face cull (a flat floor's winding is ambiguous). */
|
|
46
|
+
n64_quad3d_nc(a, b, c, d, col);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static void draw_cube_at(fix x, fix y, fix z, fix s, unsigned short col)
|
|
50
|
+
{
|
|
51
|
+
Vec3 v[8]; int i;
|
|
52
|
+
static const int sx[8]={-1,1,1,-1,-1,1,1,-1},sy[8]={-1,-1,1,1,-1,-1,1,1},sz[8]={-1,-1,-1,-1,1,1,1,1};
|
|
53
|
+
n64_model(x, y, z, 0);
|
|
54
|
+
for (i = 0; i < 8; i++){ v[i].x=sx[i]*s; v[i].y=sy[i]*s; v[i].z=sz[i]*s; }
|
|
55
|
+
n64_quad3d(v[0],v[1],v[2],v[3],col); n64_quad3d(v[5],v[4],v[7],v[6],col);
|
|
56
|
+
n64_quad3d(v[4],v[0],v[3],v[7],col); n64_quad3d(v[1],v[5],v[6],v[2],col);
|
|
57
|
+
n64_quad3d(v[4],v[5],v[1],v[0],col); n64_quad3d(v[3],v[2],v[6],v[7],col);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static void update(void)
|
|
61
|
+
{
|
|
62
|
+
unsigned int pad = n64_pad();
|
|
63
|
+
int i;
|
|
64
|
+
if (pad & PAD_LEFT) car_x -= FMUL(speed, FIXF(0.4f));
|
|
65
|
+
if (pad & PAD_RIGHT) car_x += FMUL(speed, FIXF(0.4f));
|
|
66
|
+
if (pad & PAD_UP) { speed += FIXF(0.01f); if (speed > FIX(1)) speed = FIX(1); }
|
|
67
|
+
if (pad & PAD_DOWN) { speed -= FIXF(0.03f); if (speed < FIXF(0.1f)) speed = FIXF(0.1f); }
|
|
68
|
+
if (car_x < FIX(-4)) car_x = FIX(-4); if (car_x > FIX(4)) car_x = FIX(4);
|
|
69
|
+
|
|
70
|
+
/* advance the world + wander the curve */
|
|
71
|
+
scroll += speed;
|
|
72
|
+
curve = n64_sin(scroll >> 2); /* the road bends with a sine */
|
|
73
|
+
curve = FMUL(curve, FIX(3));
|
|
74
|
+
score += F2I(speed << 2);
|
|
75
|
+
|
|
76
|
+
/* rivals roll toward us; collide = spin (reset speed + small score penalty) */
|
|
77
|
+
for (i = 0; i < 3; i++) {
|
|
78
|
+
rivals_z[i] -= F2I(speed << 1) + 1;
|
|
79
|
+
if (rivals_z[i] < 2) { rivals_z[i] = 30 + (n64_rand() % 12); rival_x[i] = (int)(n64_rand() % 5) - 2; }
|
|
80
|
+
if (rivals_z[i] < 4) {
|
|
81
|
+
fix rx = (fix)rival_x[i] << 16;
|
|
82
|
+
fix dx = rx - car_x; if (dx < 0) dx = -dx;
|
|
83
|
+
if (dx < FIX(1)) { speed = FIXF(0.15f); if (score > 30) score -= 30; }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static void render(void)
|
|
89
|
+
{
|
|
90
|
+
int s, i;
|
|
91
|
+
n64_clear(RGB(70, 130, 200)); /* sky */
|
|
92
|
+
/* far ground band */
|
|
93
|
+
n64_rect(0, 120, 320, 120, RGB(40, 90, 40));
|
|
94
|
+
|
|
95
|
+
/* draw road segments far -> near; centerline curves with depth */
|
|
96
|
+
for (s = SEGS - 1; s >= 0; s--) {
|
|
97
|
+
fix z0 = FIX(2 + s * 3);
|
|
98
|
+
fix z1 = FIX(2 + (s + 1) * 3);
|
|
99
|
+
/* centerline x grows with depth*curve for the bend */
|
|
100
|
+
fix cx0 = FMUL(curve, FMUL(z0, z0)) >> 6;
|
|
101
|
+
fix cx1 = FMUL(curve, FMUL(z1, z1)) >> 6;
|
|
102
|
+
unsigned short col = (s & 1) ? RGB(60, 60, 70) : RGB(80, 80, 90);
|
|
103
|
+
road_quad(cx0 - car_x, z0, cx1 - car_x, z1, FIX(3), col);
|
|
104
|
+
/* verges */
|
|
105
|
+
road_quad(cx0 - car_x - FIX(3), z0, cx1 - car_x - FIX(3), z1, FIXF(0.4f), RGB(220,220,220));
|
|
106
|
+
road_quad(cx0 - car_x + FIX(3), z0, cx1 - car_x + FIX(3), z1, FIXF(0.4f), RGB(220,60,60));
|
|
107
|
+
}
|
|
108
|
+
/* rivals on the road */
|
|
109
|
+
for (i = 0; i < 3; i++) if (rivals_z[i] >= 2 && rivals_z[i] < 40) {
|
|
110
|
+
fix z = (fix)rivals_z[i] << 16;
|
|
111
|
+
fix cx = FMUL(curve, FMUL(z, z)) >> 6;
|
|
112
|
+
draw_cube_at(((fix)rival_x[i] << 16) + cx - car_x, FIXF(-1.4f), z, FIXF(0.5f), RGB(230, 200, 40));
|
|
113
|
+
}
|
|
114
|
+
/* the player's car, near the camera */
|
|
115
|
+
draw_cube_at(0, FIXF(-1.4f), FIX(3), FIXF(0.6f), RGB(220, 40, 40));
|
|
116
|
+
|
|
117
|
+
n64_number(8, 6, (unsigned)score, RGB(255, 255, 255));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
int main(void)
|
|
121
|
+
{
|
|
122
|
+
n64_init();
|
|
123
|
+
n64_srand(0x1337);
|
|
124
|
+
n64_camera(0, FIXF(-0.5f), FIX(-1), 0, FIXF(-0.18f)); /* low chase cam, looking down */
|
|
125
|
+
state = TITLE; prev_pad = 0;
|
|
126
|
+
|
|
127
|
+
for (;;) {
|
|
128
|
+
unsigned int pad = n64_pad();
|
|
129
|
+
if (state == TITLE) {
|
|
130
|
+
static fix t; t += FIX(3);
|
|
131
|
+
n64_clear(RGB(20, 20, 50));
|
|
132
|
+
draw_cube_at(0, 0, FIX(6), FIX(1), RGB(220, 40, 40));
|
|
133
|
+
if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_race(); state = RACE; }
|
|
134
|
+
} else if (state == RACE) {
|
|
135
|
+
update(); render();
|
|
136
|
+
if (score > 4000) { if (score > best) best = score; state = DONE; }
|
|
137
|
+
} else {
|
|
138
|
+
n64_clear(RGB(8, 30, 8));
|
|
139
|
+
n64_number(110, 100, (unsigned)score, RGB(120, 255, 120));
|
|
140
|
+
n64_number(120, 130, (unsigned)best, RGB(255, 255, 120));
|
|
141
|
+
if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_race(); state = RACE; }
|
|
142
|
+
}
|
|
143
|
+
prev_pad = pad;
|
|
144
|
+
n64_flip();
|
|
145
|
+
}
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* shmup/main.c — STARFALL 64: a 3D Nintendo 64 vertical shooter.
|
|
3
|
+
*
|
|
4
|
+
* The N64 twin of the PS1 STARFALL — same software 3D engine, N64 backend. The
|
|
5
|
+
* playfield recedes into the screen; enemy cubes fly in from the far distance and
|
|
6
|
+
* grow under perspective. Shoot them with bullets that fly into Z. Title -> play ->
|
|
7
|
+
* game-over, score + lives, AABB collision, xorshift wave spawner, starfield.
|
|
8
|
+
*
|
|
9
|
+
* Build: build({ platform:"n64", language:"c" }) → a self-booting .z64. Controls:
|
|
10
|
+
* d-pad/stick move, A fires, START begins/restarts. Renders through the software
|
|
11
|
+
* rasterizer + angrylion VI scanout (no GL).
|
|
12
|
+
*/
|
|
13
|
+
#include "n64.h"
|
|
14
|
+
|
|
15
|
+
#define MAX_E 8
|
|
16
|
+
#define MAX_B 6
|
|
17
|
+
enum { TITLE, PLAY, OVER };
|
|
18
|
+
typedef struct { int alive; fix x, y, z; } Ent;
|
|
19
|
+
|
|
20
|
+
static Ent enemy[MAX_E];
|
|
21
|
+
static Ent bullet[MAX_B];
|
|
22
|
+
static fix px, py;
|
|
23
|
+
static int state, score, lives, hi;
|
|
24
|
+
static unsigned int prev_pad;
|
|
25
|
+
|
|
26
|
+
static void draw_cube(fix s, unsigned short col)
|
|
27
|
+
{
|
|
28
|
+
Vec3 v[8]; int i;
|
|
29
|
+
static const int sx[8]={-1,1,1,-1,-1,1,1,-1},sy[8]={-1,-1,1,1,-1,-1,1,1},sz[8]={-1,-1,-1,-1,1,1,1,1};
|
|
30
|
+
for(i=0;i<8;i++){v[i].x=sx[i]*s;v[i].y=sy[i]*s;v[i].z=sz[i]*s;}
|
|
31
|
+
n64_quad3d(v[0],v[1],v[2],v[3],col); n64_quad3d(v[5],v[4],v[7],v[6],col);
|
|
32
|
+
n64_quad3d(v[4],v[0],v[3],v[7],col); n64_quad3d(v[1],v[5],v[6],v[2],col);
|
|
33
|
+
n64_quad3d(v[4],v[5],v[1],v[0],col); n64_quad3d(v[3],v[2],v[6],v[7],col);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static void reset_game(void)
|
|
37
|
+
{
|
|
38
|
+
int i;
|
|
39
|
+
for(i=0;i<MAX_E;i++) enemy[i].alive=0;
|
|
40
|
+
for(i=0;i<MAX_B;i++) bullet[i].alive=0;
|
|
41
|
+
px=0; py=FIXF(-1.5f); score=0; lives=3;
|
|
42
|
+
}
|
|
43
|
+
static void spawn_enemy(void)
|
|
44
|
+
{
|
|
45
|
+
int i;
|
|
46
|
+
for(i=0;i<MAX_E;i++) if(!enemy[i].alive){
|
|
47
|
+
enemy[i].alive=1;
|
|
48
|
+
enemy[i].x=(fix)((n64_rand()%7)-3)<<16;
|
|
49
|
+
enemy[i].y=(fix)((n64_rand()%5)-1)<<16;
|
|
50
|
+
enemy[i].z=FIX(40); return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
static void fire(void)
|
|
54
|
+
{
|
|
55
|
+
int i;
|
|
56
|
+
for(i=0;i<MAX_B;i++) if(!bullet[i].alive){ bullet[i].alive=1; bullet[i].x=px; bullet[i].y=py; bullet[i].z=FIX(4); return; }
|
|
57
|
+
}
|
|
58
|
+
static int hit(fix ax,fix ay,fix bx,fix by,fix r)
|
|
59
|
+
{ fix dx=ax-bx,dy=ay-by; if(dx<0)dx=-dx; if(dy<0)dy=-dy; return dx<r&&dy<r; }
|
|
60
|
+
|
|
61
|
+
static void update(void)
|
|
62
|
+
{
|
|
63
|
+
unsigned int pad = n64_pad();
|
|
64
|
+
int i,j;
|
|
65
|
+
if(pad&PAD_LEFT) px-=FIXF(0.18f);
|
|
66
|
+
if(pad&PAD_RIGHT) px+=FIXF(0.18f);
|
|
67
|
+
if(pad&PAD_UP) py+=FIXF(0.14f);
|
|
68
|
+
if(pad&PAD_DOWN) py-=FIXF(0.14f);
|
|
69
|
+
if(px<FIX(-4))px=FIX(-4); if(px>FIX(4))px=FIX(4);
|
|
70
|
+
if(py<FIX(-3))py=FIX(-3); if(py>FIX(2))py=FIX(2);
|
|
71
|
+
if((pad&PAD_A)&&!(prev_pad&PAD_A)) fire();
|
|
72
|
+
for(i=0;i<MAX_B;i++) if(bullet[i].alive){ bullet[i].z+=FIX(2); if(bullet[i].z>FIX(45)) bullet[i].alive=0; }
|
|
73
|
+
if((n64_rand()&31)==0) spawn_enemy();
|
|
74
|
+
for(i=0;i<MAX_E;i++) if(enemy[i].alive){
|
|
75
|
+
enemy[i].z-=FIXF(0.5f);
|
|
76
|
+
if(enemy[i].z<FIX(3)){ enemy[i].alive=0; if(--lives<=0){ if(score>hi)hi=score; state=OVER; } }
|
|
77
|
+
for(j=0;j<MAX_B;j++) if(bullet[j].alive){
|
|
78
|
+
fix dz=enemy[i].z-bullet[j].z; if(dz<0)dz=-dz;
|
|
79
|
+
if(dz<FIX(2)&&hit(enemy[i].x,enemy[i].y,bullet[j].x,bullet[j].y,FIX(1))){ enemy[i].alive=0; bullet[j].alive=0; score+=10; }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
prev_pad=pad;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static void render(void)
|
|
86
|
+
{
|
|
87
|
+
int i;
|
|
88
|
+
n64_clear(RGB(6,8,24));
|
|
89
|
+
for(i=0;i<12;i++){ fix sx=(fix)(((i*53)%9)-4)<<16, sy=(fix)(((i*37)%7)-3)<<16; n64_model(sx,sy,FIX(50),0); draw_cube(FIXF(0.15f),RGB(40,40,70)); }
|
|
90
|
+
for(i=0;i<MAX_E;i++) if(enemy[i].alive){ n64_model(enemy[i].x,enemy[i].y,enemy[i].z,enemy[i].z<<2); draw_cube(FIXF(0.7f),RGB(230,60,60)); }
|
|
91
|
+
for(i=0;i<MAX_B;i++) if(bullet[i].alive){ n64_model(bullet[i].x,bullet[i].y,bullet[i].z,0); draw_cube(FIXF(0.18f),RGB(255,240,80)); }
|
|
92
|
+
n64_model(px,py,FIX(3),0); draw_cube(FIXF(0.5f),RGB(80,200,255));
|
|
93
|
+
n64_number(8,6,(unsigned)score,RGB(255,255,255));
|
|
94
|
+
for(i=0;i<lives;i++) n64_rect(290-i*10,8,6,6,RGB(80,200,255));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
int main(void)
|
|
98
|
+
{
|
|
99
|
+
static fix t;
|
|
100
|
+
n64_init();
|
|
101
|
+
n64_srand(0xC0FFEE);
|
|
102
|
+
n64_camera(0,0,FIX(-2),0,FIXF(-0.08f));
|
|
103
|
+
state=TITLE; prev_pad=0;
|
|
104
|
+
for(;;){
|
|
105
|
+
unsigned int pad = n64_pad();
|
|
106
|
+
if(state==TITLE){
|
|
107
|
+
n64_clear(RGB(10,10,40));
|
|
108
|
+
t+=FIX(2); n64_model(0,0,FIX(6),t); draw_cube(FIX(1),RGB(80,200,255));
|
|
109
|
+
if((pad&PAD_START)&&!(prev_pad&PAD_START)){ reset_game(); state=PLAY; }
|
|
110
|
+
prev_pad=pad;
|
|
111
|
+
} else if(state==PLAY){ update(); render(); }
|
|
112
|
+
else {
|
|
113
|
+
n64_clear(RGB(40,8,8));
|
|
114
|
+
n64_number(110,100,(unsigned)score,RGB(255,80,80));
|
|
115
|
+
n64_number(120,130,(unsigned)hi,RGB(255,255,120));
|
|
116
|
+
if((pad&PAD_START)&&!(prev_pad&PAD_START)){ reset_game(); state=PLAY; }
|
|
117
|
+
prev_pad=pad;
|
|
118
|
+
}
|
|
119
|
+
n64_flip();
|
|
120
|
+
}
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* sports/main.c — SLAM COURT: a 3D Nintendo 64 sports game (air-hockey / pong).
|
|
3
|
+
*
|
|
4
|
+
* A 3D playfield seen in perspective: you control the near paddle, the CPU the far
|
|
5
|
+
* one. A ball bounces down the court in 3D (X across, Z into the screen); rally it
|
|
6
|
+
* past the CPU to score, miss and the CPU scores. First to 7. Title -> match ->
|
|
7
|
+
* game-over. The whole court is drawn with the perspective pipeline; the ball's
|
|
8
|
+
* size changes with depth, selling the 3D.
|
|
9
|
+
*
|
|
10
|
+
* Build: build({ platform:"n64", language:"c" }). Controls: LEFT/RIGHT move your
|
|
11
|
+
* paddle, START begin/restart.
|
|
12
|
+
*
|
|
13
|
+
* 3D technique: the court floor is a big no-cull quad; paddles + ball are cubes at
|
|
14
|
+
* world positions. The CPU paddle tracks the ball's X with a capped speed. 16.16 fp.
|
|
15
|
+
*/
|
|
16
|
+
#include "n64.h"
|
|
17
|
+
|
|
18
|
+
enum { TITLE, PLAY, OVER };
|
|
19
|
+
|
|
20
|
+
static fix px; /* player paddle X (near, z = small) */
|
|
21
|
+
static fix cx; /* cpu paddle X (far, z = big) */
|
|
22
|
+
static fix bx, bz, bvx, bvz; /* ball position + velocity */
|
|
23
|
+
static int you, cpu, state;
|
|
24
|
+
static unsigned int prev_pad;
|
|
25
|
+
|
|
26
|
+
#define COURT_HW FIX(5) /* court half-width */
|
|
27
|
+
#define NEAR_Z FIX(3)
|
|
28
|
+
#define FAR_Z FIX(22)
|
|
29
|
+
|
|
30
|
+
static void draw_cube_at(fix x, fix y, fix z, fix s, unsigned short col)
|
|
31
|
+
{
|
|
32
|
+
Vec3 v[8]; int i;
|
|
33
|
+
static const int sx[8]={-1,1,1,-1,-1,1,1,-1},sy[8]={-1,-1,1,1,-1,-1,1,1},sz[8]={-1,-1,-1,-1,1,1,1,1};
|
|
34
|
+
n64_model(x,y,z,0);
|
|
35
|
+
for(i=0;i<8;i++){v[i].x=sx[i]*s;v[i].y=sy[i]*s;v[i].z=sz[i]*s;}
|
|
36
|
+
n64_quad3d(v[0],v[1],v[2],v[3],col); n64_quad3d(v[5],v[4],v[7],v[6],col);
|
|
37
|
+
n64_quad3d(v[4],v[0],v[3],v[7],col); n64_quad3d(v[1],v[5],v[6],v[2],col);
|
|
38
|
+
n64_quad3d(v[4],v[5],v[1],v[0],col); n64_quad3d(v[3],v[2],v[6],v[7],col);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static void serve(int toward_cpu)
|
|
42
|
+
{
|
|
43
|
+
bx = 0; bz = (NEAR_Z + FAR_Z) >> 1;
|
|
44
|
+
bvx = (n64_rand() & 1) ? FIXF(0.12f) : FIXF(-0.12f);
|
|
45
|
+
bvz = toward_cpu ? FIXF(0.45f) : FIXF(-0.45f);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static void reset_game(void) { px = 0; cx = 0; you = 0; cpu = 0; serve(1); }
|
|
49
|
+
|
|
50
|
+
static void update(void)
|
|
51
|
+
{
|
|
52
|
+
unsigned int pad = n64_pad();
|
|
53
|
+
if (pad & PAD_LEFT) px -= FIXF(0.22f);
|
|
54
|
+
if (pad & PAD_RIGHT) px += FIXF(0.22f);
|
|
55
|
+
if (px < -COURT_HW) px = -COURT_HW; if (px > COURT_HW) px = COURT_HW;
|
|
56
|
+
|
|
57
|
+
/* cpu tracks the ball, capped */
|
|
58
|
+
{ fix d = bx - cx; if (d > FIXF(0.16f)) d = FIXF(0.16f); if (d < -FIXF(0.16f)) d = -FIXF(0.16f); cx += d; }
|
|
59
|
+
|
|
60
|
+
bx += bvx; bz += bvz;
|
|
61
|
+
if (bx < -COURT_HW || bx > COURT_HW) bvx = -bvx; /* side walls */
|
|
62
|
+
|
|
63
|
+
/* near paddle (player) */
|
|
64
|
+
if (bz <= NEAR_Z) {
|
|
65
|
+
fix d = bx - px; if (d < 0) d = -d;
|
|
66
|
+
if (d < FIX(2)) { bvz = -bvz; bz = NEAR_Z; bvx += (bx - px) >> 4; }
|
|
67
|
+
else { cpu++; if (cpu >= 7) { state = OVER; } else serve(1); }
|
|
68
|
+
}
|
|
69
|
+
/* far paddle (cpu) */
|
|
70
|
+
if (bz >= FAR_Z) {
|
|
71
|
+
fix d = bx - cx; if (d < 0) d = -d;
|
|
72
|
+
if (d < FIX(2)) { bvz = -bvz; bz = FAR_Z; }
|
|
73
|
+
else { you++; if (you >= 7) { state = OVER; } else serve(0); }
|
|
74
|
+
}
|
|
75
|
+
prev_pad = pad;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static void render(void)
|
|
79
|
+
{
|
|
80
|
+
Vec3 a,b,c,d;
|
|
81
|
+
n64_clear(RGB(20, 40, 30));
|
|
82
|
+
n64_camera(0, FIX(4), FIX(-1), 0, FIXF(-0.30f)); /* look down the court */
|
|
83
|
+
|
|
84
|
+
/* court floor */
|
|
85
|
+
a.x=-COURT_HW; a.y=0; a.z=NEAR_Z; b.x=COURT_HW; b.y=0; b.z=NEAR_Z;
|
|
86
|
+
c.x=COURT_HW; c.y=0; c.z=FAR_Z; d.x=-COURT_HW; d.y=0; d.z=FAR_Z;
|
|
87
|
+
n64_quad3d_nc(a,b,c,d, RGB(40, 90, 60));
|
|
88
|
+
/* center line */
|
|
89
|
+
a.x=-COURT_HW; a.z=(NEAR_Z+FAR_Z)>>1; b.x=COURT_HW; b.z=a.z;
|
|
90
|
+
c.x=COURT_HW; c.z=a.z+FIXF(0.3f); d.x=-COURT_HW; d.z=c.z;
|
|
91
|
+
a.y=FIXF(0.02f);b.y=a.y;c.y=a.y;d.y=a.y;
|
|
92
|
+
n64_quad3d_nc(a,b,c,d, RGB(220,220,220));
|
|
93
|
+
|
|
94
|
+
draw_cube_at(px, FIXF(0.5f), NEAR_Z, FIXF(0.9f), RGB(80, 180, 255)); /* you */
|
|
95
|
+
draw_cube_at(cx, FIXF(0.5f), FAR_Z, FIXF(0.9f), RGB(255, 100, 80)); /* cpu */
|
|
96
|
+
draw_cube_at(bx, FIXF(0.5f), bz, FIXF(0.4f), RGB(255, 240, 120)); /* ball */
|
|
97
|
+
|
|
98
|
+
n64_number(40, 8, (unsigned)you, RGB(80, 180, 255));
|
|
99
|
+
n64_number(240, 8, (unsigned)cpu, RGB(255, 100, 80));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
int main(void)
|
|
103
|
+
{
|
|
104
|
+
n64_init();
|
|
105
|
+
n64_srand(0x5A11);
|
|
106
|
+
state = TITLE; prev_pad = 0;
|
|
107
|
+
for (;;) {
|
|
108
|
+
unsigned int pad = n64_pad();
|
|
109
|
+
if (state == TITLE) {
|
|
110
|
+
static fix t; t += FIX(3);
|
|
111
|
+
n64_clear(RGB(15, 30, 25));
|
|
112
|
+
n64_camera(0, 0, FIX(-1), 0, 0);
|
|
113
|
+
draw_cube_at(0, 0, FIX(6), FIX(1), RGB(80, 180, 255));
|
|
114
|
+
if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
|
|
115
|
+
prev_pad = pad;
|
|
116
|
+
} else if (state == PLAY) { update(); render(); }
|
|
117
|
+
else {
|
|
118
|
+
n64_clear(RGB(8, 20, 16));
|
|
119
|
+
n64_number(120, 100, (unsigned)you, RGB(80, 180, 255));
|
|
120
|
+
n64_number(160, 100, (unsigned)cpu, RGB(255, 100, 80));
|
|
121
|
+
if ((pad & PAD_START) && !(prev_pad & PAD_START)) { reset_game(); state = PLAY; }
|
|
122
|
+
prev_pad = pad;
|
|
123
|
+
}
|
|
124
|
+
n64_flip();
|
|
125
|
+
}
|
|
126
|
+
return 0;
|
|
127
|
+
}
|