Skip to content

Commit 01831ee

Browse files
committed
Rewrite pacman_march as classic intermission animation
Ghosts chase Pac-Man off screen, then Pac-Man chases scared blue ghosts back. Sprites have two animation frames and face the correct direction. Removed -R option since direction alternates automatically.
1 parent f64849c commit 01831ee

2 files changed

Lines changed: 125 additions & 83 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Self-playing Space Invaders. Use `-C` for colored mode.
9696
Animated Space Invader aliens scrolling across the screen like a marquee. Use `-C` for colored mode, `-R` to reverse direction, `-S <ticks>` to control scroll speed (default: 4), `-N <ticks>` to control animation speed (default: 1).
9797

9898
### pacman_march
99-
Pac-Man chased by ghosts scrolling across the screen with dots. Use `-C` for colored mode, `-R` to reverse direction, `-S <ticks>` for scroll speed (default: 4), `-N <ticks>` for animation speed (default: 1).
99+
Classic Pac-Man intermission: ghosts chase Pac-Man off screen, then Pac-Man chases scared (blue) ghosts back. Use `-C` for colored mode, `-S <ticks>` for scroll speed (default: 4), `-N <ticks>` for animation speed (default: 1).
100100

101101
### Examples
102102

pixelwall/design_pacman_march.c

Lines changed: 124 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,37 @@ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
2020
#define SPRITE_W 7
2121
#define SPRITE_H 7
2222
#define NUM_FRAMES 2
23+
#define NUM_GHOSTS 2
24+
#define SPACING 9
2325
#define DEFAULT_SPEED 4
2426
#define DEFAULT_ANIM_SPEED 1
2527

26-
// Pac-Man: 2 frames (mouth open / mouth closed)
27-
static const uint8_t pacman[NUM_FRAMES][SPRITE_H] = {
28+
// Pac-Man facing left: 2 frames
29+
static const uint8_t pacman_left[NUM_FRAMES][SPRITE_H] = {
30+
// Frame 0 - mouth open
31+
{
32+
0x1C, // ..###..
33+
0x3E, // .#####.
34+
0x1F, // ..#####
35+
0x07, // ....###
36+
0x1F, // ..#####
37+
0x3E, // .#####.
38+
0x1C, // ..###..
39+
},
40+
// Frame 1 - mouth closing
41+
{
42+
0x1C, // ..###..
43+
0x3E, // .#####.
44+
0x3F, // .######
45+
0x7F, // #######
46+
0x3F, // .######
47+
0x3E, // .#####.
48+
0x1C, // ..###..
49+
},
50+
};
51+
52+
// Pac-Man facing right: 2 frames
53+
static const uint8_t pacman_right[NUM_FRAMES][SPRITE_H] = {
2854
// Frame 0 - mouth open
2955
{
3056
0x1C, // ..###..
@@ -47,9 +73,8 @@ static const uint8_t pacman[NUM_FRAMES][SPRITE_H] = {
4773
},
4874
};
4975

50-
// Ghost: 2 frames (legs alternate)
51-
static const uint8_t ghost[NUM_FRAMES][SPRITE_H] = {
52-
// Frame 0
76+
// Normal ghost: 2 frames (legs alternate)
77+
static const uint8_t ghost_normal[NUM_FRAMES][SPRITE_H] = {
5378
{
5479
0x1C, // ..###..
5580
0x3E, // .#####.
@@ -59,7 +84,6 @@ static const uint8_t ghost[NUM_FRAMES][SPRITE_H] = {
5984
0x7F, // #######
6085
0x55, // #.#.#.#
6186
},
62-
// Frame 1
6387
{
6488
0x1C, // ..###..
6589
0x3E, // .#####.
@@ -71,23 +95,39 @@ static const uint8_t ghost[NUM_FRAMES][SPRITE_H] = {
7195
},
7296
};
7397

74-
// Dot: small pellet centered in a 3-wide space (only middle row has a pixel)
75-
#define DOT_W 3
76-
#define DOT_H 7
77-
78-
// Pattern layout:
79-
// [pacman 7] [gap 2] [dot 3] [dot 3] [dot 3] [ghost 7] [gap 2] [ghost 7] [gap 2]
80-
// Total: 7 + 2 + 3 + 3 + 3 + 7 + 2 + 7 + 2 = 36
98+
// Scared ghost (blue): 2 frames
99+
static const uint8_t ghost_scared[NUM_FRAMES][SPRITE_H] = {
100+
{
101+
0x1C, // ..###..
102+
0x3E, // .#####.
103+
0x55, // #.#.#.#
104+
0x7F, // #######
105+
0x6B, // ##.#.##
106+
0x7F, // #######
107+
0x55, // #.#.#.#
108+
},
109+
{
110+
0x1C, // ..###..
111+
0x3E, // .#####.
112+
0x55, // #.#.#.#
113+
0x7F, // #######
114+
0x6B, // ##.#.##
115+
0x7F, // #######
116+
0x2A, // .#.#.#.
117+
},
118+
};
81119

82-
#define NUM_ELEMENTS 9
83-
static const int element_widths[NUM_ELEMENTS] = { 7, 2, 3, 3, 3, 7, 2, 7, 2 };
84-
// Element types: 0=pacman, 1=gap, 2=dot, 3=ghost
85-
static const int element_types[NUM_ELEMENTS] = { 0, 1, 2, 2, 2, 3, 1, 3, 1 };
86-
#define TOTAL_PATTERN_W 36
120+
// Phase 0: ghosts chase pac-man, all moving left
121+
// positions: pacman at x, ghost0 at x+SPACING, ghost1 at x+2*SPACING
122+
// start: x = grid->cols, end: x + 2*SPACING + SPRITE_W < 0
123+
//
124+
// Phase 1: pac-man chases scared ghosts, all moving right
125+
// positions: ghost0 at x, ghost1 at x+SPACING, pacman at x+2*SPACING
126+
// start: x = -total_width, end: x > grid->cols
87127

88128
typedef struct {
89-
int offset;
90-
int dir;
129+
int x;
130+
int phase;
91131
int tick;
92132
int speed;
93133
int anim_tick;
@@ -96,42 +136,50 @@ typedef struct {
96136
bool colorful;
97137
Color color;
98138
Color pacman_color;
99-
Color ghost_colors[2];
100-
Color dot_color;
139+
Color ghost_colors[NUM_GHOSTS];
140+
Color scared_color;
141+
int cols;
101142
} PacmanMarchData;
102143

103144
static void PrintHelp() {
104145
printf(" -C Enable colored mode\n");
105-
printf(" -R Reverse direction (scroll right)\n");
106146
printf(" -S <ticks> Scroll speed, higher = slower (default: %d)\n", DEFAULT_SPEED);
107147
printf(" -N <ticks> Animation speed, higher = slower (default: %d)\n", DEFAULT_ANIM_SPEED);
108148
printf(" -P <color> Pac-Man color (R,G,B, default: 255,255,85)\n");
109149
printf(" -G <color> Ghost 1 color (R,G,B, default: 255,85,85)\n");
110-
printf(" -K <color> Ghost 2 color (R,G,B, default: 85,255,255)\n");
111-
printf(" -E <color> Dot color (R,G,B, default: 255,185,80)\n");
150+
printf(" -K <color> Ghost 2 color (R,G,B, default: 255,85,255)\n");
151+
printf(" -E <color> Scared ghost color (R,G,B, default: 85,85,255)\n");
152+
}
153+
154+
static void StartPhase(PacmanMarchData *pd, int phase) {
155+
pd->phase = phase;
156+
int total = 2 * SPACING + SPRITE_W;
157+
if (phase == 0) {
158+
pd->x = pd->cols;
159+
} else {
160+
pd->x = -total;
161+
}
112162
}
113163

114164
static void *Create(Grid *grid, int argc, char *argv[]) {
115-
(void)grid;
116165
PacmanMarchData *pd = calloc(1, sizeof(PacmanMarchData));
117166
if (!pd) return NULL;
118167

119168
pd->color = GREEN;
120-
pd->dir = 1;
121169
pd->speed = DEFAULT_SPEED;
122170
pd->anim_speed = DEFAULT_ANIM_SPEED;
123171
pd->colorful = false;
124172
pd->pacman_color = (Color){255, 255, 85, 255};
125173
pd->ghost_colors[0] = (Color){255, 85, 85, 255};
126-
pd->ghost_colors[1] = (Color){85, 255, 255, 255};
127-
pd->dot_color = (Color){255, 185, 80, 255};
174+
pd->ghost_colors[1] = (Color){255, 85, 255, 255};
175+
pd->scared_color = (Color){85, 85, 255, 255};
176+
pd->cols = grid->cols;
128177

129178
int opt;
130179
optind = 1;
131-
while ((opt = getopt(argc, argv, ":d:CRS:N:P:G:K:E:")) != -1) {
180+
while ((opt = getopt(argc, argv, ":d:CS:N:P:G:K:E:")) != -1) {
132181
switch (opt) {
133182
case 'C': pd->colorful = true; break;
134-
case 'R': pd->dir = -1; break;
135183
case 'S':
136184
pd->speed = atoi(optarg);
137185
if (pd->speed < 1) pd->speed = 1;
@@ -143,60 +191,25 @@ static void *Create(Grid *grid, int argc, char *argv[]) {
143191
case 'P': pd->pacman_color = ParseColor(optarg); break;
144192
case 'G': pd->ghost_colors[0] = ParseColor(optarg); break;
145193
case 'K': pd->ghost_colors[1] = ParseColor(optarg); break;
146-
case 'E': pd->dot_color = ParseColor(optarg); break;
194+
case 'E': pd->scared_color = ParseColor(optarg); break;
147195
}
148196
}
149197

198+
StartPhase(pd, 0);
150199
return pd;
151200
}
152201

153-
static void DrawColumn(PacmanMarchData *pd, Grid *grid, int screen_x, int pattern_col,
154-
int start_row, int frame) {
155-
// Find which element this column belongs to
156-
int col = pattern_col;
157-
int elem = 0;
158-
while (elem < NUM_ELEMENTS && col >= element_widths[elem]) {
159-
col -= element_widths[elem];
160-
elem++;
161-
}
162-
if (elem >= NUM_ELEMENTS) return;
163-
164-
int type = element_types[elem];
165-
166-
if (type == 1) return; // gap
167-
168-
if (type == 2) {
169-
// Dot: single pixel in the center (col 1, row 3)
170-
if (col == 1) {
171-
int gy = start_row + SPRITE_H / 2;
172-
if (gy >= 0 && gy < grid->rows) {
173-
Color c = pd->colorful ? pd->dot_color : pd->color;
174-
GridSetColor(grid, (Pos){screen_x, gy}, c);
202+
static void DrawSprite(Grid *grid, const uint8_t *sprite, int sx, int start_row, Color color) {
203+
for (int c = 0; c < SPRITE_W; c++) {
204+
int gx = sx + c;
205+
if (gx < 0 || gx >= grid->cols) continue;
206+
for (int r = 0; r < SPRITE_H; r++) {
207+
int gy = start_row + r;
208+
if (gy < 0 || gy >= grid->rows) continue;
209+
if ((sprite[r] >> (SPRITE_W - 1 - c)) & 1) {
210+
GridSetColor(grid, (Pos){gx, gy}, color);
175211
}
176212
}
177-
return;
178-
}
179-
180-
const uint8_t *sprite;
181-
Color c;
182-
183-
if (type == 0) {
184-
// Pac-Man
185-
sprite = pacman[frame];
186-
c = pd->colorful ? pd->pacman_color : pd->color;
187-
} else {
188-
// Ghost - figure out which one (first or second ghost in pattern)
189-
int ghost_idx = (elem == 5) ? 0 : 1;
190-
sprite = ghost[frame];
191-
c = pd->colorful ? pd->ghost_colors[ghost_idx] : pd->color;
192-
}
193-
194-
for (int r = 0; r < SPRITE_H; r++) {
195-
int gy = start_row + r;
196-
if (gy < 0 || gy >= grid->rows) continue;
197-
if ((sprite[r] >> (SPRITE_W - 1 - col)) & 1) {
198-
GridSetColor(grid, (Pos){screen_x, gy}, c);
199-
}
200213
}
201214
}
202215

@@ -207,18 +220,47 @@ static void UpdateFrame(Grid *grid, void *data) {
207220
GridFillColor(grid, bg);
208221

209222
int start_row = (grid->rows - SPRITE_H) / 2;
210-
211-
for (int x = 0; x < grid->cols; x++) {
212-
int pcol = ((x + pd->offset) % TOTAL_PATTERN_W + TOTAL_PATTERN_W) % TOTAL_PATTERN_W;
213-
DrawColumn(pd, grid, x, pcol, start_row, pd->frame);
223+
int f = pd->frame;
224+
225+
if (pd->phase == 0) {
226+
// Ghosts chase Pac-Man, all moving left
227+
// Pac-Man leads (leftmost), ghosts follow
228+
Color pc = pd->colorful ? pd->pacman_color : pd->color;
229+
DrawSprite(grid, pacman_left[f], pd->x, start_row, pc);
230+
for (int i = 0; i < NUM_GHOSTS; i++) {
231+
Color gc = pd->colorful ? pd->ghost_colors[i] : pd->color;
232+
DrawSprite(grid, ghost_normal[f], pd->x + (i + 1) * SPACING, start_row, gc);
233+
}
234+
} else {
235+
// Pac-Man chases scared ghosts, all moving right
236+
// Ghosts lead (leftmost), Pac-Man follows
237+
for (int i = 0; i < NUM_GHOSTS; i++) {
238+
Color sc = pd->colorful ? pd->scared_color : pd->color;
239+
DrawSprite(grid, ghost_scared[f], pd->x + i * SPACING, start_row, sc);
240+
}
241+
Color pc = pd->colorful ? pd->pacman_color : pd->color;
242+
DrawSprite(grid, pacman_right[f], pd->x + NUM_GHOSTS * SPACING, start_row, pc);
214243
}
215244

245+
// Scroll
216246
pd->tick++;
217247
if (pd->tick >= pd->speed) {
218248
pd->tick = 0;
219-
pd->offset += pd->dir;
249+
int total = 2 * SPACING + SPRITE_W;
250+
if (pd->phase == 0) {
251+
pd->x--;
252+
if (pd->x + total < 0) {
253+
StartPhase(pd, 1);
254+
}
255+
} else {
256+
pd->x++;
257+
if (pd->x > pd->cols) {
258+
StartPhase(pd, 0);
259+
}
260+
}
220261
}
221262

263+
// Animation
222264
pd->anim_tick++;
223265
if (pd->anim_tick >= pd->anim_speed) {
224266
pd->anim_tick = 0;

0 commit comments

Comments
 (0)