534 lines
10 KiB
C
534 lines
10 KiB
C
|
/* Roaming-blobs-on-mars-collect-some-dust-on-a-tropical-island-and-go-pearl-
|
||
|
diving-before-population-goes-out-of-control. */
|
||
|
|
||
|
/* Each frame, a background virtual screen is copied to a virtual screen; */
|
||
|
/* sprites (well, pixels) are written on that virtual screen; and the */
|
||
|
/* virtual screen is copied to video memory. The background is updated as */
|
||
|
/* appropriate. This simple animation technique works well for 320x200 */
|
||
|
/* because it's so small. */
|
||
|
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
#include <time.h>
|
||
|
#include <vga.h>
|
||
|
#include <vgagl.h>
|
||
|
|
||
|
|
||
|
/* This can be changed into any 256 color mode. */
|
||
|
/* For planar 256 color modes, enable page flipping. */
|
||
|
/* Even 16 color modes work (ugly colors). */
|
||
|
#define VGAMODE G800x600x256
|
||
|
|
||
|
//#define USE_PAGEFLIPPING
|
||
|
|
||
|
/* #define USE_SMALLOC */
|
||
|
|
||
|
/* This is the size of the animated window. */
|
||
|
#define MAPWIDTH 800
|
||
|
#define MAPHEIGHT 560
|
||
|
|
||
|
#define MAXMOVERS 2000
|
||
|
#define MAXCITIES 1000
|
||
|
#define NUMBEROFCITIES 20
|
||
|
#define NUMBEROFMOVERS 1400
|
||
|
#define MOVERTHRESHOLD 1400
|
||
|
#define MOVERLIFETIME 1000
|
||
|
#define COLORTIME 2000
|
||
|
|
||
|
#define randomn( n ) (random() % n)
|
||
|
#define red(x) (32 + x)
|
||
|
#define green(x) (64 + x)
|
||
|
#define yellow(x) (96 + x)
|
||
|
#define blue(x) (128 + x)
|
||
|
#define magenta(x) (160 + x)
|
||
|
#define cyan(x) (192 + x)
|
||
|
#define white(x) (224 + x)
|
||
|
|
||
|
|
||
|
/* Data types */
|
||
|
|
||
|
typedef struct {
|
||
|
int x;
|
||
|
int y;
|
||
|
} Position;
|
||
|
|
||
|
#define STAT_ACTIVE 1
|
||
|
|
||
|
typedef struct {
|
||
|
int stat;
|
||
|
int x;
|
||
|
int y;
|
||
|
int vx;
|
||
|
int vy;
|
||
|
int color;
|
||
|
int time;
|
||
|
} Mover;
|
||
|
|
||
|
typedef struct {
|
||
|
int x;
|
||
|
int y;
|
||
|
int pop;
|
||
|
int hit;
|
||
|
} City;
|
||
|
|
||
|
/* Global variables */
|
||
|
|
||
|
int map[MAPWIDTH][MAPHEIGHT];
|
||
|
/* Map encoding i: */
|
||
|
/* (0 - 0xffff Mover number i) */
|
||
|
/* 0x10000... Part of city (i - 0x10000) */
|
||
|
|
||
|
Mover mover[MAXMOVERS];
|
||
|
int nu_movers = 0;
|
||
|
City city[MAXCITIES];
|
||
|
int nu_cities = 0;
|
||
|
int mytime = 0; /* used to be "time" but collids w/libc function time() */
|
||
|
int pop = 0;
|
||
|
int framerate, framecount, frameclock;
|
||
|
|
||
|
GraphicsContext *physicalscreen;
|
||
|
GraphicsContext *backscreen;
|
||
|
GraphicsContext *background;
|
||
|
|
||
|
|
||
|
void error(char *s)
|
||
|
{
|
||
|
printf("%s\n", s);
|
||
|
vga_setmode(TEXT);
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
void setcustompalette(void)
|
||
|
{
|
||
|
/* colors 0-31 are a RGB mix (bits 0 and 1 red, 2 green, 3 and 4 blue) */
|
||
|
/* 32-63 black to red */
|
||
|
/* 64-95 black to green */
|
||
|
/* 96-127 black to yellow */
|
||
|
/* 128-159 black to blue */
|
||
|
/* 160-191 black to magenta */
|
||
|
/* 192-223 black to cyan */
|
||
|
/* 224-255 black to white */
|
||
|
Palette pal;
|
||
|
int i;
|
||
|
for (i = 0; i < 256; i++) {
|
||
|
int r, g, b;
|
||
|
r = g = b = 0;
|
||
|
if ((i & 32) > 0)
|
||
|
r = (i & 31) << 1;
|
||
|
if ((i & 64) > 0)
|
||
|
g = (i & 31) << 1;
|
||
|
if ((i & 128) > 0)
|
||
|
b = (i & 31) << 1;
|
||
|
if (i < 32) {
|
||
|
r = (i & 3) << 4; /* 2 bits */
|
||
|
g = (i & 4) << 3; /* 1 bit */
|
||
|
b = (i & 24) << 1; /* 2 bits */
|
||
|
}
|
||
|
pal.color[i].red = r;
|
||
|
pal.color[i].green = g;
|
||
|
pal.color[i].blue = b;
|
||
|
}
|
||
|
gl_setpalette(&pal);
|
||
|
}
|
||
|
|
||
|
void initfont(void)
|
||
|
{
|
||
|
void *font;
|
||
|
#ifdef USE_SMALLOC
|
||
|
font = smalloc(256 * 8 * 8 * BYTESPERPIXEL);
|
||
|
#else
|
||
|
font = malloc(256 * 8 * 8 * BYTESPERPIXEL);
|
||
|
#endif
|
||
|
gl_expandfont(8, 8, white(24), gl_font8x8, font);
|
||
|
gl_setfont(8, 8, font);
|
||
|
}
|
||
|
|
||
|
int fsize(FILE * f)
|
||
|
{
|
||
|
int oldpos, size;
|
||
|
oldpos = ftell(f);
|
||
|
fseek(f, 0, SEEK_END);
|
||
|
size = ftell(f);
|
||
|
fseek(f, oldpos, SEEK_SET);
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
void loadfile(char **buf, char *fname)
|
||
|
{
|
||
|
FILE *f;
|
||
|
int size;
|
||
|
f = fopen(fname, "rb");
|
||
|
size = fsize(f);
|
||
|
*buf = malloc(size);
|
||
|
fread(*buf, 1, size, f);
|
||
|
fclose(f);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Map */
|
||
|
|
||
|
void clearmap(void)
|
||
|
{
|
||
|
int x, y;
|
||
|
for (y = 0; y < MAPHEIGHT; y++)
|
||
|
for (x = 0; x < MAPWIDTH; x++)
|
||
|
map[x][y] = 0;
|
||
|
}
|
||
|
|
||
|
Position
|
||
|
findfreeposition(void)
|
||
|
{
|
||
|
int x, y;
|
||
|
Position p;
|
||
|
do {
|
||
|
x = randomn(MAPWIDTH);
|
||
|
y = randomn(MAPHEIGHT);
|
||
|
}
|
||
|
while (map[x][y] != 0);
|
||
|
p.x = x;
|
||
|
p.y = y;
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Movers */
|
||
|
|
||
|
void initmovers(void)
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i < MAXMOVERS; i++)
|
||
|
mover[i].stat = 0;
|
||
|
}
|
||
|
|
||
|
int findfreemoverslot(void)
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i < MAXMOVERS; i++)
|
||
|
if (!(mover[i].stat & STAT_ACTIVE))
|
||
|
return i;
|
||
|
error("Mover table overflow");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void addrandommover(void)
|
||
|
{
|
||
|
Position p = findfreeposition();
|
||
|
int i = findfreemoverslot();
|
||
|
int c;
|
||
|
mover[i].x = p.x;
|
||
|
mover[i].y = p.y;
|
||
|
do {
|
||
|
mover[i].vx = randomn(3) - 1;
|
||
|
mover[i].vy = randomn(3) - 1;
|
||
|
}
|
||
|
while (mover[i].vx == 0 && mover[i].vy == 0);
|
||
|
mover[i].stat = STAT_ACTIVE;
|
||
|
switch (randomn(4)) {
|
||
|
case 0:
|
||
|
c = blue(20);
|
||
|
break;
|
||
|
case 1:
|
||
|
c = green(20);
|
||
|
break;
|
||
|
case 2:
|
||
|
c = magenta(20);
|
||
|
break;
|
||
|
default:
|
||
|
c = cyan(20);
|
||
|
break;
|
||
|
}
|
||
|
mover[i].time = 0;
|
||
|
mover[i].color = c;
|
||
|
nu_movers++;
|
||
|
}
|
||
|
|
||
|
void killmover(int i)
|
||
|
{
|
||
|
mover[i].stat = 0;
|
||
|
nu_movers--;
|
||
|
}
|
||
|
|
||
|
void drawmover(int i)
|
||
|
{
|
||
|
gl_setpixel(mover[i].x, mover[i].y, mover[i].color);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Cities */
|
||
|
|
||
|
void initcities(void)
|
||
|
{
|
||
|
nu_cities = 0;
|
||
|
}
|
||
|
|
||
|
void addcity(int x, int y)
|
||
|
{
|
||
|
int i = nu_cities++;
|
||
|
map[x][y] = i + 0x10000;
|
||
|
city[i].x = x;
|
||
|
city[i].y = y;
|
||
|
city[i].pop = 1;
|
||
|
city[i].hit = 0;
|
||
|
}
|
||
|
|
||
|
int cityat(int x, int y)
|
||
|
{
|
||
|
if (map[x][y] >= 0x10000)
|
||
|
return map[x][y] - 0x10000;
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int citycolor(void)
|
||
|
{
|
||
|
static int colortable[5] =
|
||
|
{yellow(31), blue(31), white(31), green(31), cyan(31)};
|
||
|
return colortable[(mytime / COLORTIME) % 5]
|
||
|
- (mytime % COLORTIME) * 25 / COLORTIME;
|
||
|
}
|
||
|
|
||
|
void growcity(int cx, int cy, int x, int y, int ct)
|
||
|
{
|
||
|
/* add city unit at (x, y) adjacent to city unit (cx, cy) */
|
||
|
int c;
|
||
|
map[x][y] = ct + 0x10000;
|
||
|
c = citycolor();
|
||
|
gl_setpixel(x, y, c);
|
||
|
city[ct].pop++;
|
||
|
city[ct].hit = 20;
|
||
|
pop++;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Main components */
|
||
|
|
||
|
void createbackground(void)
|
||
|
{
|
||
|
/* Create fancy dark red background */
|
||
|
int x, y;
|
||
|
for (y = 0; y < MAPHEIGHT; y++)
|
||
|
for (x = 0; x < MAPWIDTH; x++) {
|
||
|
int i = 0;
|
||
|
int n = 0;
|
||
|
int c;
|
||
|
if (x > 0) {
|
||
|
i += gl_getpixel(x - 1, y) - red(0);
|
||
|
n++;
|
||
|
}
|
||
|
if (y > 0) {
|
||
|
i += gl_getpixel(x, y - 1) - red(0);
|
||
|
n++;
|
||
|
}
|
||
|
c = (i + randomn(16)) / (n + 1);
|
||
|
if (c > 9)
|
||
|
c = 9;
|
||
|
gl_setpixel(x, y, red(0) + c);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void drawbackground(void)
|
||
|
{
|
||
|
/* Build up background from map data */
|
||
|
int x, y;
|
||
|
gl_setcontext(background);
|
||
|
gl_clearscreen(0);
|
||
|
createbackground();
|
||
|
for (y = 0; y < MAPHEIGHT; y++)
|
||
|
for (x = 0; x < MAPWIDTH; x++) {
|
||
|
int c = cityat(x, y);
|
||
|
if (c != -1)
|
||
|
gl_setpixel(x, y, citycolor());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void createcities(void)
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i < NUMBEROFCITIES; i++) {
|
||
|
Position p = findfreeposition();
|
||
|
addcity(p.x, p.y);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void writestat(void)
|
||
|
{
|
||
|
char s[41];
|
||
|
int i, x, y;
|
||
|
int maxpopcity, maxpop;
|
||
|
sprintf(s, "Pop %7d Time %7d Rate %5d.%d", pop, mytime,
|
||
|
framerate / 10, framerate % 10);
|
||
|
gl_setwritemode(WRITEMODE_OVERWRITE);
|
||
|
gl_write(0, HEIGHT - 8, s);
|
||
|
maxpop = -1;
|
||
|
maxpopcity = 0;
|
||
|
for (i = 0; i < nu_cities; i++)
|
||
|
if (city[i].pop > maxpop) {
|
||
|
maxpop = city[i].pop;
|
||
|
maxpopcity = i;
|
||
|
}
|
||
|
gl_enableclipping();
|
||
|
gl_circle(city[maxpopcity].x, city[maxpopcity].y, 10,
|
||
|
blue(31));
|
||
|
gl_disableclipping();
|
||
|
|
||
|
gl_setwritemode(WRITEMODE_MASKED);
|
||
|
x = city[maxpopcity].x;
|
||
|
y = city[maxpopcity].y;
|
||
|
sprintf(s, "%d", maxpop);
|
||
|
/* clipping */
|
||
|
if (x + strlen(s) * 8 > MAPWIDTH)
|
||
|
x = MAPWIDTH - strlen(s) * 8;
|
||
|
if (y + 8 > MAPHEIGHT)
|
||
|
y = MAPHEIGHT - 8;
|
||
|
gl_write(x, y, s);
|
||
|
}
|
||
|
|
||
|
void drawscreen(void)
|
||
|
{
|
||
|
int i;
|
||
|
/* Copy background to backscreen. */
|
||
|
gl_setcontext(background);
|
||
|
gl_copyscreen(backscreen);
|
||
|
|
||
|
/* Now draw the objects in backscreen. */
|
||
|
gl_setcontext(backscreen);
|
||
|
|
||
|
for (i = 0; i < MAXMOVERS; i++)
|
||
|
if (mover[i].stat & STAT_ACTIVE) {
|
||
|
drawmover(i);
|
||
|
}
|
||
|
writestat();
|
||
|
|
||
|
/* Copy backscreen to physical screen. */
|
||
|
gl_copyscreen(physicalscreen);
|
||
|
}
|
||
|
|
||
|
void move(void)
|
||
|
{
|
||
|
int i;
|
||
|
gl_setcontext(background);
|
||
|
for (i = 0; i < MAXMOVERS; i++)
|
||
|
if (mover[i].stat & STAT_ACTIVE) {
|
||
|
int nx, ny;
|
||
|
int c;
|
||
|
if (++mover[i].time == MOVERLIFETIME) {
|
||
|
killmover(i);
|
||
|
continue;
|
||
|
}
|
||
|
for (;;) {
|
||
|
nx = mover[i].x + mover[i].vx;
|
||
|
ny = mover[i].y + mover[i].vy;
|
||
|
if (nx < 0 || nx >= MAPWIDTH) {
|
||
|
mover[i].vx = -mover[i].vx;
|
||
|
continue;
|
||
|
}
|
||
|
if (ny < 0 || ny >= MAPHEIGHT) {
|
||
|
mover[i].vy = -mover[i].vy;
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
c = cityat(nx, ny);
|
||
|
if (c != -1) { /* found city */
|
||
|
killmover(i);
|
||
|
growcity(nx, ny, mover[i].x, mover[i].y, c);
|
||
|
continue; /* next mover */
|
||
|
}
|
||
|
mover[i].x = nx;
|
||
|
mover[i].y = ny;
|
||
|
}
|
||
|
if (pop >= MAPWIDTH * MAPHEIGHT * 255 / 256) {
|
||
|
/* start all over again */
|
||
|
printf("fun: new run.\n");
|
||
|
pop = 0;
|
||
|
mytime = 0;
|
||
|
clearmap();
|
||
|
initcities();
|
||
|
createcities();
|
||
|
drawbackground();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void createmovers(void)
|
||
|
{
|
||
|
int i;
|
||
|
for (i = 0; i < NUMBEROFMOVERS; i++)
|
||
|
addrandommover();
|
||
|
}
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
vga_init();
|
||
|
|
||
|
clearmap();
|
||
|
initmovers();
|
||
|
createcities();
|
||
|
createmovers();
|
||
|
|
||
|
vga_setmode(VGAMODE);
|
||
|
gl_setcontextvga(VGAMODE);
|
||
|
physicalscreen = gl_allocatecontext();
|
||
|
gl_getcontext(physicalscreen);
|
||
|
|
||
|
#ifdef USE_PAGEFLIPPING
|
||
|
/* Try to enable page flipping. */
|
||
|
printf("pf=%i\n", gl_enablepageflipping(physicalscreen));
|
||
|
#endif
|
||
|
setcustompalette();
|
||
|
/* initfont() here caused trouble with planar 256 color modes. */
|
||
|
|
||
|
gl_setcontextvgavirtual(VGAMODE);
|
||
|
backscreen = gl_allocatecontext();
|
||
|
gl_getcontext(backscreen);
|
||
|
#ifdef USE_SMALLOC
|
||
|
free(backscreen->vbuf);
|
||
|
backscreen->vbuf = smalloc(BYTEWIDTH * HEIGHT);
|
||
|
gl_setcontext(backscreen);
|
||
|
#endif
|
||
|
|
||
|
initfont();
|
||
|
|
||
|
gl_setcontextvgavirtual(VGAMODE);
|
||
|
background = gl_allocatecontext();
|
||
|
gl_getcontext(background);
|
||
|
#ifdef USE_SMALLOC
|
||
|
free(background->vbuf);
|
||
|
background->vbuf = smalloc(BYTEWIDTH * HEIGHT);
|
||
|
gl_setcontext(background);
|
||
|
#endif
|
||
|
|
||
|
drawbackground();
|
||
|
|
||
|
framerate = 0;
|
||
|
framecount = 0;
|
||
|
frameclock = clock();
|
||
|
|
||
|
for (;;) {
|
||
|
int i;
|
||
|
drawscreen();
|
||
|
move();
|
||
|
for (i = 0; i < 4; i++)
|
||
|
if (nu_movers < MOVERTHRESHOLD)
|
||
|
addrandommover();
|
||
|
mytime++;
|
||
|
/* Update frame rate every 3 seconds. */
|
||
|
framecount++;
|
||
|
if (clock() - frameclock >= CLOCKS_PER_SEC) {
|
||
|
framerate = framecount * CLOCKS_PER_SEC / (clock() - frameclock);
|
||
|
framecount = 0;
|
||
|
frameclock = clock();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifndef USE_SMALLOC
|
||
|
gl_freecontext(backscreen);
|
||
|
gl_freecontext(background);
|
||
|
#endif
|
||
|
|
||
|
vga_setmode(TEXT);
|
||
|
exit(0);
|
||
|
}
|