diff --git a/games/.gitignore b/games/.gitignore index 38b8d6fb..771f3677 100644 --- a/games/.gitignore +++ b/games/.gitignore @@ -5,3 +5,4 @@ *.so *.a asteroids +aquatinspitz diff --git a/games/Makefile b/games/Makefile index bf5e9f5e..0bc2eca4 100644 --- a/games/Makefile +++ b/games/Makefile @@ -11,6 +11,7 @@ CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti BINARIES:=\ asteroids \ +aquatinspitz \ LIBS:=-ldispd diff --git a/games/aquatinspitz.cpp b/games/aquatinspitz.cpp new file mode 100644 index 00000000..f88acceb --- /dev/null +++ b/games/aquatinspitz.cpp @@ -0,0 +1,392 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2014. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see . + + aquatinspitz.cpp + Aqua tin spitz! + +*******************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Utility global variables every game will need. +bool game_running = true; +size_t game_width = 1280; +size_t game_height = 720; +const size_t MAX_KEY_NUMBER = 512UL; +bool keys_down[MAX_KEY_NUMBER] = { false }; +bool keys_pending[MAX_KEY_NUMBER] = { false }; +struct timespec key_handled_last[MAX_KEY_NUMBER]; + +// Utility functions every game will need. +bool pop_is_key_just_down(int abskbkey); +static inline uint32_t make_color(uint8_t r, uint8_t g, uint8_t b); + +// Your game is customized from here ... + +struct player +{ + float x; + float y; + int size; +}; + +struct player player; + +struct enemy +{ + float x; + float y; + float vx; + float vy; + int size; + int shift; +}; + +#define NUM_ENEMIES 256 +struct enemy enemies[NUM_ENEMIES]; + +// Prepare the game state for the first round. +void init() +{ + player.x = game_width / 2; + player.y = game_height / 2; + player.size = 24.0; + + for ( size_t i = 0; i < NUM_ENEMIES; i++ ) + { + enemies[i].x = (float) arc4random_uniform(game_width); + enemies[i].y = (float) arc4random_uniform(game_height); + enemies[i].vx = (float) ((int) arc4random_uniform(96) - 48); + enemies[i].vy = (float) ((int) arc4random_uniform(96) - 48); + enemies[i].size = arc4random_uniform(8) + 8; + enemies[i].shift = (int) arc4random_uniform(6) - 3; + if ( enemies[i].shift <= 0 ) + enemies[i].shift -= 1; + } +} + +// Calculate the game state of the next round. +void update(float deltatime) +{ + float player_speed = 64.0f; + float player_velocity_x = 0.0f; + float player_velocity_y = 0.0f; + if ( keys_down[KBKEY_UP] ) + player_velocity_y -= player_speed; + if ( keys_down[KBKEY_DOWN] ) + player_velocity_y += player_speed; + if ( keys_down[KBKEY_LEFT] ) + player_velocity_x -= player_speed; + if ( keys_down[KBKEY_RIGHT] ) + player_velocity_x += player_speed; + player.x += deltatime * player_velocity_x; + player.y += deltatime * player_velocity_y; + + if ( pop_is_key_just_down(KBKEY_SPACE) ) + player.size = 192 - player.size; + + float total_speed = 0.0; + for ( size_t i = 0; i < NUM_ENEMIES; i++ ) + { + struct enemy* enemy = &enemies[i]; + float g = 10000.0; + float dist_sq = (player.x - enemy->x) * (player.x - enemy->x) + + (player.y - enemy->y) * (player.y - enemy->y); + if ( dist_sq < 0.1 ) + dist_sq = 0.1; + float dist = sqrtf(dist_sq); + float f = g * enemy->size * player.size / dist_sq; + float f_x = (player.x - enemy->x) / dist * f; + float f_y = (player.y - enemy->y) / dist * f; + float a_x = f_x / enemy->size; + float a_y = f_y / enemy->size; + enemy->vx += deltatime * a_x; + enemy->vy += deltatime * a_y; + float speed = sqrtf(enemy->vx * enemy->vx + enemy->vy * enemy->vy); + total_speed += speed; + } + + float average_speed = total_speed / NUM_ENEMIES; + float mid_game = game_width / 2.0; + + for ( size_t i = 0; i < NUM_ENEMIES; i++ ) + { + struct enemy* enemy = &enemies[i]; + float speed = sqrtf(enemy->vx * enemy->vx + enemy->vy * enemy->vy); + float ox = enemy->x; + float oy = enemy->y; + float nx = ox + deltatime * enemy->vx; + float ny = oy + deltatime * enemy->vy; + if ( mid_game + enemy->size / 2 < ox && + nx <= mid_game + enemy->size / 2 ) + { + if ( speed < average_speed ) + { + if ( enemy->vx < 0.0 ) + enemy->vx = -enemy->vx; + continue; + } + } + else if ( ox <= mid_game - enemy->size / 2 && + mid_game - enemy->size / 2 < nx ) + { + if ( speed >= average_speed ) + { + if ( enemy->vx > 0.0 ) + enemy->vx = -enemy->vx; + continue; + } + } + enemy->x = nx; + enemy->y = ny; + } + + for ( size_t i = 0; i < NUM_ENEMIES; i++ ) + { + struct enemy* enemy = &enemies[i]; + if ( enemy->x - enemy->size / 2 < 0 ) + { + enemy->x = 0.0f + enemy->size / 2; + if ( enemy->vx < 0.0 ) + enemy->vx = -0.9 * enemy->vx; + } + else if ( game_width < (size_t) (enemy->x + enemy->size / 2) ) + { + enemy->x = (float) game_width - enemy->size / 2; + if ( 0.0 < enemy->vx ) + enemy->vx = -0.9 * enemy->vx; + } + if ( enemy->y - enemy->size / 2 < 0 ) + { + enemy->y = 0.0f + enemy->size / 2; + if ( enemy->vy < 0.0 ) + enemy->vy = -0.9 * enemy->vy; + } + else if ( game_height < (size_t) (enemy->y + enemy->size / 2) ) + { + enemy->y = (float) game_height - enemy->size / 2; + if ( 0.0 < enemy->vy ) + enemy->vy = -0.9 * enemy->vy; + } + } +} + +// Render the game into the framebuffer. +void render(struct dispd_window* window) +{ + struct dispd_framebuffer* window_fb = dispd_begin_render(window); + if ( !window_fb ) + { + error(0, 0, "unable to begin rendering dispd window"); + game_running = false; + return; + } + + uint32_t* fb = (uint32_t*) dispd_get_framebuffer_data(window_fb); + size_t xres = dispd_get_framebuffer_width(window_fb); + size_t yres = dispd_get_framebuffer_height(window_fb); + size_t pitch = dispd_get_framebuffer_pitch(window_fb) / sizeof(uint32_t); + + // Render a colorful background. + for ( size_t y = 0; y < yres; y++ ) + { + for ( size_t x = 0; x < xres; x++ ) + { + uint32_t color = make_color(x * y, y ? x / y : 255, x ^ y); + fb[y * pitch + x] = color; + } + } + + // Render the player. + for ( int t = -player.size / 2; t < player.size / 2; t++ ) + { + if ( player.y + t < 0 ) + continue; + size_t y = (size_t) (player.y + t); + if ( yres <= y ) + continue; + for ( int l = -player.size / 2; l < player.size / 2; l++ ) + { + if ( player.x + l < 0 ) + continue; + size_t x = (size_t) (player.x + l); + if ( xres <= x ) + continue; + uint32_t background = fb[y * pitch + x]; + uint32_t color = ~background; + fb[y * pitch + x] = color; + } + } + + // Render the enemies. + for ( size_t i = 0; i < NUM_ENEMIES; i++ ) + { + struct enemy* enemy = &enemies[i]; + for ( int t = -enemy->size / 2; t < enemy->size / 2; t++ ) + { + if ( enemy->y + t < 0 ) + continue; + size_t y = (size_t) (enemy->y + t); + if ( yres <= y ) + continue; + for ( int l = -enemy->size / 2; l < enemy->size / 2; l++ ) + { + if ( enemy->x + l < 0 ) + continue; + size_t x = (size_t) (enemy->x + l); + if ( xres <= x ) + continue; + uint32_t background = fb[y * pitch + x]; + uint32_t color = enemy->shift < 0 ? background >> -enemy->shift + : background << enemy->shift; + color = ~color; + fb[y * pitch + x] = color; + } + } + } + + dispd_finish_render(window_fb); +} + +// ... to here. No need to edit stuff below. + +// Create a color from rgb values. +static inline uint32_t make_color(uint8_t r, uint8_t g, uint8_t b) +{ + return b << 0UL | g << 8UL | r << 16UL; +} + +// Return if a keystroke is pending. For instance, if you press A on your +// keyboard and keep pressing it, a new A character will appear every time a +// small interval has passed, not just every time the code checks if A is down. +bool pop_is_key_just_down(int abskbkey) +{ + assert(0 <= abskbkey); + if ( MAX_KEY_NUMBER <= (size_t) abskbkey ) + return false; + if ( keys_pending[abskbkey] ) + { + keys_pending[abskbkey] = false; + clock_gettime(CLOCK_MONOTONIC, &key_handled_last[abskbkey]); + return true; + } + if ( !keys_down[abskbkey] ) + return false; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec elapsed = timespec_sub(now, key_handled_last[abskbkey]); + struct timespec repress_delay = timespec_make(0, 100 * 1000 * 1000); + if ( timespec_lt(elapsed, repress_delay) ) + return false; + clock_gettime(CLOCK_MONOTONIC, &key_handled_last[abskbkey]); + return true; +} + +// Read input from the keyboard. +void input() +{ + // Read the keyboard input from the user. + unsigned termmode = TERMMODE_KBKEY | TERMMODE_SIGNAL | TERMMODE_NONBLOCK; + if ( settermmode(0, termmode) ) + error(1, errno, "settermmode"); + uint32_t codepoint; + ssize_t numbytes; + while ( 0 < (numbytes = read(0, &codepoint, sizeof(codepoint))) ) + { + int kbkey = KBKEY_DECODE(codepoint); + if( !kbkey ) + continue; + int abskbkey = (kbkey < 0) ? -kbkey : kbkey; + if ( MAX_KEY_NUMBER <= (size_t) abskbkey ) + continue; + bool is_key_down_event = 0 < kbkey; + if ( !keys_down[abskbkey] && is_key_down_event ) + keys_pending[abskbkey] = true; + keys_down[abskbkey] = is_key_down_event; + } +} + +// Run the game until no longer needed. +void mainloop(struct dispd_window* window) +{ + struct dispd_framebuffer* window_fb = dispd_begin_render(window); + if ( window_fb ) + { + game_width = dispd_get_framebuffer_width(window_fb); + game_height = dispd_get_framebuffer_height(window_fb); + dispd_finish_render(window_fb); + } + + init(); + + struct timespec last_frame_time; + clock_gettime(CLOCK_MONOTONIC, &last_frame_time); + + render(window); + + while ( game_running ) + { + struct timespec current_frame_time; + clock_gettime(CLOCK_MONOTONIC, ¤t_frame_time); + + struct timespec deltatime_ts = + timespec_sub(current_frame_time, last_frame_time); + float deltatime = deltatime_ts.tv_sec + deltatime_ts.tv_nsec / 1E9f; + + input(); + update(deltatime); + render(window); + + last_frame_time = current_frame_time; + } +} + +// Create a display context, run the game, and then cleanly exit. +int main(int argc, char* argv[]) +{ + if ( !dispd_initialize(&argc, &argv) ) + error(1, 0, "couldn't initialize dispd library"); + struct dispd_session* session = dispd_attach_default_session(); + if ( !session ) + error(1, 0, "couldn't attach to dispd default session"); + if ( !dispd_session_setup_game_rgba(session) ) + error(1, 0, "couldn't setup dispd rgba session"); + struct dispd_window* window = dispd_create_window_game_rgba(session); + if ( !window ) + error(1, 0, "couldn't create dispd rgba window"); + + mainloop(window); + + dispd_destroy_window(window); + dispd_detach_session(session); + + return 0; +}