#include #include #include #include #include #include #define max(x, y) ({ \ __typeof__(x) __x = x, __y = y; \ (__x < __y ? __y : __x); \ }) #define min(x, y) ({ \ __typeof__(x) __x = x, __y = y; \ (__x > __y ? __y : __x); \ }) enum { TILE_KIND_WALL, TILE_KIND_EMPTY }; typedef uint32_t Tile; #define LEVEL_WIDTH 40 #define LEVEL_HEIGHT 25 #define TILE_SIZE 20 static Tile level[LEVEL_WIDTH * LEVEL_HEIGHT]; enum { ENTITY_KIND_FAUCET }; #define TILE_AT(x, y) (*({ \ uint32_t __x = x; \ uint32_t __y = y; \ level + (__x + LEVEL_WIDTH * __y); \ })) typedef uint32_t Entity_Kind; typedef struct Entity { Entity_Kind kind; uint16_t x, y; union { struct { uint32_t flow; uint32_t tick; } faucet; } entity; } Entity; #define MAX_ENTITIES 10 static uint32_t entity_count; static Entity entities[MAX_ENTITIES]; #define CYCLE_MS 10 #define CYCLES_PER_SEC 100 static int last_frame_time; static int time_left_over; cpSpace *space; cpBody *scenery; #define MAX_WATER 10000 uint32_t water_count; cpBody *water[MAX_WATER]; #define WATER_RADIUS 3.0 void draw_tile(uint32_t x, uint32_t y) { Tile tile = TILE_AT(x, y); if (tile == TILE_KIND_WALL) { glColor3f(1, 0, 0); glBegin(GL_QUADS); glVertex2f((x + 0) * TILE_SIZE, (y + 0) * TILE_SIZE); glVertex2f((x + 1) * TILE_SIZE, (y + 0) * TILE_SIZE); glVertex2f((x + 1) * TILE_SIZE, (y + 1) * TILE_SIZE); glVertex2f((x + 0) * TILE_SIZE, (y + 1) * TILE_SIZE); glEnd(); glColor3f(1, 1, 1); } else if (tile == TILE_KIND_EMPTY) { } else { abort(); } } void draw_entity(uint32_t i) { Entity_Kind kind = entities[i].kind; float x = entities[i].x; float y = entities[i].y; if (kind == ENTITY_KIND_FAUCET) { glColor3f(0, 1, 1); glBegin(GL_QUADS); glVertex2f((x + 0.25f) * TILE_SIZE, (y + 0.25f) * TILE_SIZE); glVertex2f((x + 0.75f) * TILE_SIZE, (y + 0.25f) * TILE_SIZE); glVertex2f((x + 0.75f) * TILE_SIZE, (y + 0.75f) * TILE_SIZE); glVertex2f((x + 0.25f) * TILE_SIZE, (y + 0.75f) * TILE_SIZE); glEnd(); glColor3f(1, 1, 1); } else { abort(); } } void draw_water(uint32_t i) { cpVect p = water[i]->p; glColor3f(0, 0, 1); glBegin(GL_QUADS); glVertex2f(p.x - WATER_RADIUS, p.y - WATER_RADIUS); glVertex2f(p.x + WATER_RADIUS, p.y - WATER_RADIUS); glVertex2f(p.x + WATER_RADIUS, p.y + WATER_RADIUS); glVertex2f(p.x - WATER_RADIUS, p.y + WATER_RADIUS); glEnd(); } void update(void) { for (uint32_t i = 0; i < entity_count; ++i) { if (entities[i].kind == ENTITY_KIND_FAUCET) { ++entities[i].entity.faucet.tick; if (water_count < MAX_WATER && entities[i].entity.faucet.tick == entities[i].entity.faucet.flow) { entities[i].entity.faucet.tick = 0; cpBody *dropletBody = cpBodyNew(1.0, INFINITY); dropletBody->p = cpv( TILE_SIZE * (entities[i].x + 0.5) + rand() % 3 - 1, TILE_SIZE * (entities[i].y + 0.5) + rand() % 3 - 1); cpSpaceAddBody(space, dropletBody); cpShape *dropletShape = cpCircleShapeNew( dropletBody, WATER_RADIUS, cpvzero); dropletShape->e = 1.0; dropletShape->u = 0.0; cpSpaceAddShape(space, dropletShape); water[water_count++] = dropletBody; } } else { abort(); } } cpSpaceStep(space, CYCLE_MS * 0.001); } void display(void) { int now = glutGet(GLUT_ELAPSED_TIME); int dt = now - last_frame_time + time_left_over; last_frame_time = now; while (dt >= CYCLE_MS) { update(); dt -= CYCLE_MS; } time_left_over = dt; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 800, 500, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); for (uint32_t y = 0; y < LEVEL_HEIGHT; ++y) { for (uint32_t x = 0; x < LEVEL_WIDTH; ++x) { draw_tile(x, y); } } for (uint32_t i = 0; i < entity_count; ++i) { draw_entity(i); } for (uint32_t i = 0; i < water_count; ++i) { draw_water(i); } glutSwapBuffers(); } void reshape(int width, int height) { glViewport(0, 0, width, height); } void idle(void) { glutPostRedisplay(); } cpVect WALL_VERTS[4] = { { 0, TILE_SIZE }, { TILE_SIZE, TILE_SIZE }, { TILE_SIZE, 0 }, { 0, 0 } }; void read_level(char const *path) { FILE *f = fopen(path, "rb"); if (!f) { fprintf(stderr, "can't open %s\n", path); exit(EXIT_FAILURE); } for (uint32_t y = 0; y < LEVEL_HEIGHT; ++y) { for (uint32_t x = 0; x < LEVEL_WIDTH; ++x) { int c = fgetc(f); if (c == '#') { TILE_AT(x, y) = TILE_KIND_WALL; cpShape *shape = cpPolyShapeNew( scenery, 4, WALL_VERTS, cpv(x * TILE_SIZE, y * TILE_SIZE)); cpSpaceAddStaticShape(space, shape); } else { TILE_AT(x, y) = TILE_KIND_EMPTY; if (c == ' ') { } else if (c == 'F') { if (entity_count == MAX_ENTITIES) { fprintf(stderr, "exceeded max entities (%c) in %s\n", c, path); exit(EXIT_FAILURE); } entities[entity_count].kind = ENTITY_KIND_FAUCET; entities[entity_count].x = x; entities[entity_count].y = y; entities[entity_count].entity.faucet.flow = 10; entities[entity_count].entity.faucet.tick = 0; entity_count ++; } else { fprintf(stderr, "syntax error (%c) in %s\n", c, path); exit(EXIT_FAILURE); } } } int c = fgetc(f); if (c != '\n') { fprintf(stderr, "syntax error (%c) in %s\n", c, path); exit(EXIT_FAILURE); } } } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_ALPHA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(800, 500); glutCreateWindow("WT"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutIdleFunc(idle); cpInitChipmunk(); space = cpSpaceNew(); space->gravity = cpv(0.0, 5.0 * TILE_SIZE); scenery = cpBodyNew(INFINITY, INFINITY); read_level("level.txt"); last_frame_time = glutGet(GLUT_ELAPSED_TIME); glutMainLoop(); return EXIT_SUCCESS; }