An hour or two on a previous evening were devoted to a quick test of the graphics idea, and some brainstorming about what the game should be like.
Some noteable game code elements:
The noise generator uses 64KB of "precomputed" random bytes, generated by XORing the Mersenne Twister with the linear congruential generator used by BCPL (in other words, stb_rand()^stb_randLCG() ), combined with 32K random indexes into the 64KB table.
Sample room (scaled-down):
Sample creature:
Creature art is dynamically stretched and squished when drawn
to give it more "life".
Here's one example:
void object_waypoint(Object *o) { switch (o->id) { // wait only at end points of path case 1: case 22: case 23: case 24: case 25: case 26: case 27: case 28: if (o->target != 1 && o->target != o->path->num_path) o->wait = 0; break; // pick a random next destination case 6: case 7: { int n = stb_rand() % (o->path->num_path-2); if (o->target > o->prev_target) { if (n >= o->prev_target) ++n; if (n >= o->target) ++n; } else { if (n >= o->target) ++n; if (n >= o->prev_target) ++n; } o->target = n; break; } // teleport back to start case 8: case 9: case 10: case 11: if (o->target == o->path->num_path-1) { o->target = 1; o->prev_target = 0; } break; // teleport back to start case 19: case 20: if (o->target == 0) { o->target = 1; o->prev_target = 0; } break; // when we reach a waypoint, hide case 18: o->y = 4000; break; } }
The inner loop of the noise display looks like this:
for (i=0; i < z->w; ++i) { int c = z->pixels[j*z->w + i]; uint8 n = noises[c][i]; out->pixels[j*out->w+i] = clut[n]; }
Here's the entire music player:
// MUSIC PLAYER #ifdef _DEBUG int audio=1; #else int audio=1; #endif stb_vorbis *vorb; short (*audio_data)[2]; int length_decoded; uint next_measure_time; stb_thread vthread; void * decode_vorbis(void *p) { int pos=0; for(;;) { int n = stb_vorbis_get_frame_short_interleaved(vorb, 2, audio_data[pos], 8192); if (n == 0) { vthread = NULL; return NULL; } pos += n; length_decoded = pos; stb_barrier(); if (next_measure_time) // once playback has started, start sleeping Sleep(1); } } void load_vorbis(void) { int total_len; int len; uint8 *data = packed_stb_file("merged.ogg", &len); vorb = stb_vorbis_open_memory(data, len, NULL, NULL); total_len = stb_vorbis_stream_length_in_samples(vorb); audio_data = malloc(total_len * sizeof(audio_data[0])); if (!audio_data) outofmem(); vthread = stb_create_thread(decode_vorbis, NULL); } typedef struct { int id; int loc; int len; int start_offset; int duration; } Measure; Measure *measures; #define music ((void *) 1) int play_measure(int m) { int i; if (stb_arr_len(measures) == 0) return 0; for (i=0; i < stb_arr_len(measures); ++i) { if (measures[i].id == m) { Measure *z = &measures[i]; if (z->loc + z->len <= length_decoded) mixlow_add_playback(audio_data[z->loc], z->len, 1, 2, 0, next_measure_time - z-> t_offset, z->len, 1, FADE_none,0,0, 1,0,music); else { next_measure_time = mixhigh_time() + 4000; return 0; } next_measure_time += z->duration; return 1; } } return 1; } int *music_loop, mloop_len, mloop_next, mloop_start; void set_loop(int *loop, int len, int start) { if (music_loop == loop && mloop_len == len) return; music_loop = loop; mloop_len = len; mloop_next = 0; mloop_start = start; } void update_measure(void) { if (!audio) return; while (next_measure_time < mixhigh_time() + 6000) { if (mloop_next >= mloop_len) mloop_next = 0; if (!play_measure(music_loop[mloop_next])) return; ++mloop_next; if (mloop_next >= mloop_len) mloop_next = mloop_start; } } int title_loop[] = { 1,1, 2,27,5,30, 2,27,3,28, 4,29,5,27, 6,31,7,32, 6,31,7,32, 8,33,7,32, }; #define MLOOP(x) set_loop(x, sizeof(x)/sizeof(x[0]), 0) #define MLOOPS(x,y) set_loop(x, sizeof(x)/sizeof(x[0]), y) short *intro; int intro_len; void load_music(void) { int i,n=0,c,t,mlen; char **data, *mem; if (!audio) return; mem = packed_stb_file("static_title_3.ogg", &mlen); if (mem) n = stb_vorbis_decode_memory(mem, mlen, &c, &intro); #ifdef EDITOR if (n) n = 2000; #endif t = mixhigh_time() + 4000; if (n) mixlow_add_playback(intro, n, TRUE, c, 0, t, n, 1, FADE_none,0,0, 0.75, 0, music); next_measure_time = t + n + 44100 * 0.5; mem = packed_stb_file("merged_data.txt", &mlen); if (mem) { mem[mlen-1] = 0; data = stb_tokens(mem, "\r\n", &n); //data = stb_stringfile("merged_data.txt", &n); for (i=0; i < n; ++i) { Measure m; sscanf(data[i], "%d %d %d %d %d", &m.id, &m.loc, &m.len, &m.start_offset, &m.duration); stb_arr_push(measures, m); } } load_vorbis(); MLOOPS(title_loop,6); } void audio_loopmode(void) { if (!audio) return; // we're trying to run at at least 30hz, which is: // 44100 / 30 = 4410 / 3 = 1300 samples mixhigh_step(4000); if (next_measure_time == 0) { if (length_decoded >= 44100*4) { // wait until 4 seconds of audio is decoded next_measure_time = mixhigh_time() + 2000; } return; } update_measure(); } // atexit function void music_stop(int nice) { int t = mixhigh_time(); if (vthread) stb_destroy_thread(vthread); mixlow_end_set(music, FADE_linear, 0, nice ? 32000 : 3000); while (mixlow_num_active()) { mixhigh_step(4000); Sleep(10); } mixhigh_deinit(); }