diff --git a/assets/fish/angryfish.png b/assets/fish/angryfish.png new file mode 100644 index 0000000..e8fe4e6 Binary files /dev/null and b/assets/fish/angryfish.png differ diff --git a/assets/fish/fish.png b/assets/fish/fish.png new file mode 100644 index 0000000..e917cdf Binary files /dev/null and b/assets/fish/fish.png differ diff --git a/meson.build b/meson.build index efef5a0..3981489 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,10 @@ -project('rotcpu', 'cpp') +project('fish-simulator', 'c') sdl2_dep = dependency('sdl2') +sdl2_image_dep = dependency('sdl2_image') executable( - 'rotcpuemu', - './src/main.cpp', - dependencies : [sdl2_dep] + 'fish-simulator', + './src/main.c', + dependencies : [sdl2_dep, sdl2_image_dep] ) diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..710ab7e --- /dev/null +++ b/src/main.c @@ -0,0 +1,252 @@ +#include "SDL.h" +#include "SDL_events.h" +#include "SDL_rect.h" +#include "SDL_render.h" +#include "SDL_image.h" +#include "SDL_timer.h" +#include "SDL_video.h" + +#define FISHES_MAX 1024 +#define SIMULATION_DELTA_TIME (1.0f / 60.0f) + +enum fish_type { + FISH_TYPE_NORMAL, + FISH_TYPE_ANGRY +}; + +enum fish_state { + FISH_STATE_NONE, + FISH_STATE_INIT, + FISH_STATE_WANDER_PICK_POINT, + FISH_STATE_WANDER_TRANSITION, + FISH_STATE_WANDER_DONE +}; + +struct fish { + int x, y, w, h; + enum fish_type type; + enum fish_state state; + + /* state arguments */ + float wander_start_x, wander_start_y, wander_target_x, wander_target_y, wander_step; /* FISH_STATE_WANDER_* */ +}; + +struct app { + struct fish *fishes[FISHES_MAX]; + int fishes_count; + SDL_Window *window; + SDL_Renderer *renderer; + + SDL_Texture *fish_texture; + SDL_Texture *angry_fish_texture; +}; + +float lerp(float a, float b, float t) +{ + return a + (b - a) * t; +} + +void scc(int code) +{ + if (code < 0) { + fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError()); + exit(1); + } +} + +void *scp(void *ptr) +{ + if (ptr == NULL) { + fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError()); + exit(1); + } + + return ptr; +} + +int fishes_add(struct app *app, int x, int y, int w, int h, enum fish_type type) +{ + struct fish *new_fish = malloc(sizeof(struct fish)); + new_fish->x = x; + new_fish->y = y; + new_fish->w = w; + new_fish->h = h; + new_fish->type = type; + new_fish->state = FISH_STATE_INIT; + + for (int i = 0; i < FISHES_MAX; i++) { + if (app->fishes[i] == NULL) { + app->fishes[i] = new_fish; + app->fishes_count++; + return i; + } + } + + return -1; +} + +void fish_update(const struct app *app, struct fish *fish, float delta_time) +{ + switch (fish->state) + { + case FISH_STATE_INIT: { + fish->state = FISH_STATE_WANDER_PICK_POINT; + } break; + + case FISH_STATE_WANDER_PICK_POINT: { + int width, height; + SDL_GetWindowSize(app->window, &width, &height); + + fish->wander_target_x = rand() % width; + fish->wander_target_y = rand() % height; + fish->wander_start_x = fish->x; + fish->wander_start_y = fish->y; + fish->wander_step = 0.1; + fish->state = FISH_STATE_WANDER_TRANSITION; + } break; + + case FISH_STATE_WANDER_TRANSITION: { + if (fish->x != fish->wander_target_x) { + fish->x = lerp(fish->wander_start_x, fish->wander_target_x, fish->wander_step); + } + if (fish->y != fish->wander_target_y) { + fish->y = lerp(fish->wander_start_y, fish->wander_target_y, fish->wander_step); + } + if (fish->wander_step >= 1.0) { + fish->state = FISH_STATE_WANDER_DONE; + } + fish->wander_step += 0.05 * delta_time; + } break; + + case FISH_STATE_WANDER_DONE: { + fish->state = FISH_STATE_INIT; + } break; + + case FISH_STATE_NONE: /* through */ + default: + break; + } + +} + +void fish_draw(const struct app *app, const struct fish *fish) +{ + SDL_Rect rect = { + .x = fish->x, + .y = fish->y, + .w = fish->w, + .h = fish->h + }; + SDL_Texture *texture = fish->type == FISH_TYPE_ANGRY ? app->angry_fish_texture : app->fish_texture; + SDL_RenderCopy(app->renderer, texture, NULL, &rect); +} + +struct app *app_new() +{ + struct app *app = malloc(sizeof(struct app)); + + scc(SDL_Init(SDL_INIT_VIDEO)); + scc(SDL_CreateWindowAndRenderer(320, 240, SDL_WINDOW_RESIZABLE, &app->window, &app->renderer)); + app->fish_texture = scp(IMG_LoadTexture(app->renderer, "./assets/fish/fish.png")); + app->angry_fish_texture = scp(IMG_LoadTexture(app->renderer, "./assets/fish/angryfish.png")); + fishes_add(app, 20, 20, 128, 128, FISH_TYPE_NORMAL); + fishes_add(app, 20, 150, 128, 128, FISH_TYPE_NORMAL); + fishes_add(app, 150, 150, 128, 128, FISH_TYPE_ANGRY); + + return app; +} + +void app_update(struct app *app, float delta_time) +{ + /* fishes */ + { + int found_fishes = 0; + for (int i = 0; i < FISHES_MAX; i++) { + if (app->fishes[i] != NULL) { + found_fishes++; + fish_update(app, app->fishes[i], delta_time); + } + if (found_fishes >= app->fishes_count) { + break; + } + } + } +} + +void app_draw(struct app *app) +{ + /* background */ + { + SDL_SetRenderDrawColor(app->renderer, 0x11, 0x65, 0xBA, 0xff); + SDL_RenderClear(app->renderer); + } + + /* fishes */ + { + int found_fishes = 0; + for (int i = 0; i < FISHES_MAX; i++) { + if (app->fishes[i] != NULL) { + found_fishes++; + fish_draw(app, app->fishes[i]); + } + if (found_fishes >= app->fishes_count) { + break; + } + } + } +} + +void app_loop(struct app *app) { + SDL_Event event; + Uint32 prev_ticks = SDL_GetTicks(); + float lag_sec = 0; + while (1) { + Uint32 current_ticks = SDL_GetTicks(); + lag_sec += (float) (current_ticks - prev_ticks) / 1000.0f; + SDL_PollEvent(&event); + if (event.type == SDL_QUIT) { + break; + } + + { + SDL_Delay(1); + while (lag_sec >= SIMULATION_DELTA_TIME) { + app_update(app, SIMULATION_DELTA_TIME); + lag_sec -= SIMULATION_DELTA_TIME; + } + } + + app_draw(app); + + SDL_RenderPresent(app->renderer); + } +} + +void app_free(struct app *app) +{ + int found_fishes = 0; + for (int i = 0; i < FISHES_MAX; i++) { + if (app->fishes[i] != NULL) { + free(app->fishes[i]); + found_fishes++; + } + if (found_fishes >= app->fishes_count) { + break; + } + } + SDL_DestroyRenderer(app->renderer); + SDL_DestroyWindow(app->window); + SDL_DestroyTexture(app->fish_texture); + free(app); +} + +int main(int argc, char *argv[]) +{ + struct app *app = app_new(); + app_loop(app); + app_free(app); + + SDL_Quit(); + + return 0; +} diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 312a071..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "SDL.h" - -void scc(int code) -{ - if (code < 0) { - fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError()); - exit(1); - } -} - -void *scp(void *ptr) -{ - if (ptr == NULL) { - fprintf(stderr, "SDL ERROR: %s\n", SDL_GetError()); - exit(1); - } - - return ptr; -} - -int main(int argc, char *argv[]) -{ - SDL_Window *window; - SDL_Renderer *renderer; - SDL_Surface *surface; - SDL_Event event; - - scc(SDL_Init(SDL_INIT_VIDEO)); - scc(SDL_CreateWindowAndRenderer(320, 240, SDL_WINDOW_RESIZABLE, &window, &renderer)); - - while (1) { - SDL_PollEvent(&event); - if (event.type == SDL_QUIT) { - break; - } - SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00); - SDL_RenderClear(renderer); - SDL_RenderPresent(renderer); - } - - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - - SDL_Quit(); - - return 0; -}