#include #include #include #include #include #include #include "dat.h" #define BULLET_SIZE 2 #define ENTITY_SIZE 16 typedef struct { Body body; } Player; typedef struct { Body body; bool active; } Ghost; typedef struct { Body body; float time; bool active; } Bullet; typedef struct { enum { EPLAYER, EGHOST, EBULLET, } type; bool active; Body body; float time; } Entity; typedef struct { Entity *self; Entity *target; } EntityCollision; typedef void (EntityCollisionProc)(Entity *self, Entity *other); static void render(void); static void update(float delta); static void process_events(void); static Entity *allocentity(void); static void freeentity(Entity *); static void entphysics(float delta); static void enqcoll(EntityCollision *c); static EntityCollision *deqcoll(void); static EntityCollisionProc bulletcollproc; static EntityCollisionProc *collisionProcs[] = { [EBULLET] = bulletcollproc, }; static SDL_Window *window; static SDL_Renderer *renderer; static bool running; static Uint64 old_ticks, new_ticks; static Entity *player; static Entity entitybuffer[2048]; static int ebufi; static EntityCollision ecoll[2048]; static int ecollstart, ecollend, ecollsi; static float camera_x, camera_y; static bool shot; static int shotx, shoty; extern char map_data[]; extern int map_width, map_height; int main() { if(!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) { fprintf(stderr, "SDL_Init(): %s\n", SDL_GetError()); return 1; } if(!SDL_CreateWindowAndRenderer("window", 800, 600, SDL_WINDOW_OPENGL, &window, &renderer)) { fprintf(stderr, "SDL_CreateWindowAndRenderer: %s\n", SDL_GetError()); return -1; } player = allocentity(); player->type = EPLAYER; player->body.size[0] = ENTITY_SIZE; player->body.size[1] = ENTITY_SIZE; for(int i = 0; i < 10; i++) { Entity *e = allocentity(); e->type = EGHOST; e->body.pos[0] = rand() % 800; e->body.pos[1] = rand() % 600; e->body.size[0] = ENTITY_SIZE; e->body.size[1] = ENTITY_SIZE; } running = true; old_ticks = SDL_GetTicksNS(); while(running) { double delta; process_events(); new_ticks = SDL_GetTicksNS(); delta = (new_ticks - old_ticks) / 1000000000.0; old_ticks = new_ticks; update(delta); render(); SDL_RenderPresent(renderer); } SDL_DestroyWindow(window); SDL_Quit(); return 0; } void render(void) { { float plx, ply, minx, miny, maxx, maxy; plx = player->body.pos[0] - camera_x; ply = player->body.pos[1] - camera_y; minx = plx - 80; miny = ply - 80; maxx = plx + 80; maxy = ply + 80; if(minx < 0) camera_x += minx; if(miny < 0) camera_y += miny; if(maxx > 800) camera_x += (maxx - 800); if(maxy > 600) camera_y += (maxy - 600); } SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF); SDL_RenderClear(renderer); SDL_SetRenderDrawColor(renderer, 0xAA, 0xAA, 0xAA, 0xFF); for(int y = 0; y < map_height; y++) for(int x = 0; x < map_width; x++) { if(!map_data[x + map_width * y]) continue; SDL_RenderFillRect(renderer, &(SDL_FRect) { .x = 2 * ENTITY_SIZE * x - ENTITY_SIZE - camera_x, .y = 2 * ENTITY_SIZE * y - ENTITY_SIZE - camera_y, .w = 2.0 * ENTITY_SIZE, .h = 2.0 * ENTITY_SIZE, }); } for(int i = 0; i < ebufi; i++) { Entity *e = entitybuffer + i; if(!e->active) continue; switch (e->type) { case EPLAYER: SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderRect(renderer, &(SDL_FRect){ .x = e->body.pos[0] - ENTITY_SIZE - camera_x, .y = e->body.pos[1] - ENTITY_SIZE - camera_y, .w = ENTITY_SIZE * 2.0, .h = ENTITY_SIZE * 2.0 }); break; case EGHOST: SDL_SetRenderDrawColor(renderer, 0xAA, 0xAA, 0xAA, 0xFF); SDL_RenderRect(renderer, &(SDL_FRect){ .x = e->body.pos[0] - ENTITY_SIZE - camera_x, .y = e->body.pos[1] - ENTITY_SIZE - camera_y, .w = ENTITY_SIZE * 2.0, .h = ENTITY_SIZE * 2.0 }); break; case EBULLET: SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0x00, 0xFF); SDL_RenderRect(renderer, &(SDL_FRect){ .x = e->body.pos[0] - BULLET_SIZE - camera_x, .y = e->body.pos[1] - BULLET_SIZE - camera_y, .w = BULLET_SIZE * 2.0, .h = BULLET_SIZE * 2.0 }); break; } } } void update(float delta) { # define SPEED 100 const _Bool *kstate = SDL_GetKeyboardState(NULL); Entity *bullet, *other; EntityCollision *coll; float vx, vy; entphysics(delta); while((coll = deqcoll())) { Entity *self = coll->self; Entity *target = coll->target; if(collisionProcs[self->type]) collisionProcs[self->type](self, target); if(collisionProcs[target->type]) collisionProcs[target->type](target, self); } for(int ei = 0; ei < ebufi; ei++) { Entity *e = entitybuffer + ei; if(!e->active) continue; switch(e->type) { case EPLAYER: e->body.vel[0] = 0; e->body.vel[1] = 0; if(kstate[SDL_SCANCODE_W]) e->body.vel[1] -= SPEED; if(kstate[SDL_SCANCODE_A]) e->body.vel[0] -= SPEED; if(kstate[SDL_SCANCODE_S]) e->body.vel[1] += SPEED; if(kstate[SDL_SCANCODE_D]) e->body.vel[0] += SPEED; if(shot) { shot = false; int i; float dx = shotx - e->body.pos[0]; float dy = shoty - e->body.pos[1]; float dd = sqrtf(dx * dx + dy * dy); dx /= dd; dy /= dd; bullet = allocentity(); bullet->type = EBULLET; bullet->time = 0.5; bullet->body.vel[0] = dx * SPEED * 5.0; bullet->body.vel[1] = dy * SPEED * 5.0; bullet->body.pos[0] = e->body.pos[0]; bullet->body.pos[1] = e->body.pos[1]; bullet->body.size[0] = BULLET_SIZE; bullet->body.size[1] = BULLET_SIZE; bullet->active = true; } break; case EGHOST: vx = 0; vy = 0; float dx = player->body.pos[0] - e->body.pos[0]; float dy = player->body.pos[1] - e->body.pos[1]; float dd = sqrtf(dx * dx + dy * dy); dx /= dd; dy /= dd; vx += dx * SPEED; vy += dy * SPEED; for(int j = 0; j < 1024; j++) { other = entitybuffer + j; if(!other->active || other == e || other->type != EGHOST) continue; float dx = other->body.pos[0] - e->body.pos[0]; float dy = other->body.pos[1] - e->body.pos[1]; float dd = sqrtf(dx * dx + dy * dy); dx /= dd; dy /= dd; if(dd < 40) { vx -= dx * 100; vy -= dy * 100; } } e->body.vel[0] = vx; e->body.vel[1] = vy; break; case EBULLET: break; } } } void process_events() { SDL_Event event; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_EVENT_QUIT: running = false; break; case SDL_EVENT_MOUSE_BUTTON_DOWN: shot = true; shotx = event.button.x + camera_x; shoty = event.button.y + camera_y; break; } } } Entity * allocentity(void) { Entity *e; for(e = entitybuffer; e < entitybuffer + ebufi; e++) { if(!e->active) break; } if(e >= entitybuffer + ebufi) { if(ebufi >= 2048) return NULL; e = entitybuffer + ebufi; ebufi++; } memset(e, 0, sizeof(*e)); e->active = true; return e; } void freeentity(Entity *e) { e->active = false; } void entphysics(float delta) { for(int i = 0; i < ebufi; i++) { Entity *e = entitybuffer + i; if(!e->active) continue; for(int y = 0; y < map_height; y++) for(int x = 0; x < map_width; x++) { if(!map_data[x + y * map_width]) continue; Body b = { .pos = { x * ENTITY_SIZE * 2.0, y * ENTITY_SIZE * 2.0, }, .size = { ENTITY_SIZE, ENTITY_SIZE }, }; if(checkcollision(&e->body, &b)) { float p[2]; resolvecoll(&e->body, &b, p); for(int i = 0; i < 2; i++) e->body.pos[i] -= p[i]; if(p[0] != 0.0) { e->body.vel[0] = 0; } else { e->body.vel[1] = 0; } } } for(int j = i; j < ebufi; j++) { Entity *f = entitybuffer + j; if(!f->active) break; if(checkcollision(&e->body, &f->body)) { EntityCollision cl; cl.self = e; cl.target = f; enqcoll(&cl); } } } for(int i = 0; i < ebufi; i++) { Entity *e = entitybuffer + i; if(!e->active) continue; for(int j = 0; j < 2; j++) e->body.pos[j] += e->body.vel[j] * delta; } } void enqcoll(EntityCollision *c) { if(ecollsi >= 2048) return; ecollsi++; ecoll[ecollend++] = *c; if(ecollend >= 2048) ecollend = 0; } EntityCollision * deqcoll(void) { EntityCollision *c; if(ecollsi <= 0) return NULL; ecollsi--; c = ecoll + ecollstart++; if(ecollstart >= 2048) ecollstart = 0; return c; } void bulletcollproc(Entity *self, Entity *target) { if(target->type == EGHOST) { self->active = false; target->active = false; } }