SvgaLib/demos/fun.c

533 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);
}