diff --git a/README.md b/README.md index cf7032d..f0ee3ac 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ `jancity` is a cross-platform, tile-based sandbox video game. +![Screenshot](jancity.png) + Mostly written during the Global Game Jam 2024 event, reusing its engine from [`rts`](https://gitea.privatedns.org/xavi/rts). diff --git a/jancity.png b/jancity.png new file mode 100644 index 0000000..26d04dd Binary files /dev/null and b/jancity.png differ diff --git a/res/CMakeLists.txt b/res/CMakeLists.txt index 65f1ccd..e063d17 100644 --- a/res/CMakeLists.txt +++ b/res/CMakeLists.txt @@ -128,10 +128,143 @@ sprite(NAME unit6 CY 48 TRANSPARENT TRUE) +sprite(NAME sel_down_left + X 384 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME sel_down_right + X 384 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME sel_mid_down + X 384 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME sel_mid_top + X 384 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME sel_mid_v + X 384 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME sel_up_left + X 384 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME sel_up_right + X 384 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME checkbox + X 384 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME line_edit_left + X 0 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME line_edit_mid + X 0 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT FALSE) + +sprite(NAME line_edit_right + X 0 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME car1 + X 0 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME pavement + X 0 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT FALSE) + +sprite(NAME car2 + X 0 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME building1 + X 0 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + +sprite(NAME building2 + X 0 + Y 0 + BPP 4 + CX 0 + CY 0 + TRANSPARENT TRUE) + level(NAME city1) container(NAME jancity SPRITES + building1 + building2 + car1 + car2 + checkbox grass sidewalk roof1 @@ -148,4 +281,15 @@ container(NAME jancity unit4 unit5 unit6 + sel_down_left + sel_down_right + sel_mid_down + sel_mid_top + sel_mid_v + sel_up_left + sel_up_right + line_edit_left + line_edit_mid + line_edit_right + pavement ) diff --git a/res/btn_left.bmp b/res/btn_left.bmp deleted file mode 100644 index 7d8ee4e..0000000 Binary files a/res/btn_left.bmp and /dev/null differ diff --git a/res/btn_left_24.bmp b/res/btn_left_24.bmp index 36e2370..2ab8bf0 100644 Binary files a/res/btn_left_24.bmp and b/res/btn_left_24.bmp differ diff --git a/res/btn_mid.bmp b/res/btn_mid.bmp deleted file mode 100644 index 801e0d5..0000000 Binary files a/res/btn_mid.bmp and /dev/null differ diff --git a/res/btn_mid_24.bmp b/res/btn_mid_24.bmp index f7a0ca0..fb2e82f 100644 Binary files a/res/btn_mid_24.bmp and b/res/btn_mid_24.bmp differ diff --git a/res/btn_right.bmp b/res/btn_right.bmp deleted file mode 100644 index ae7cc85..0000000 Binary files a/res/btn_right.bmp and /dev/null differ diff --git a/res/btn_right_24.bmp b/res/btn_right_24.bmp index 26da50f..c56b308 100644 Binary files a/res/btn_right_24.bmp and b/res/btn_right_24.bmp differ diff --git a/res/btn_small.bmp b/res/btn_small.bmp new file mode 100644 index 0000000..43dacf6 Binary files /dev/null and b/res/btn_small.bmp differ diff --git a/res/btn_small_24.bmp b/res/btn_small_24.bmp new file mode 100644 index 0000000..0f19504 Binary files /dev/null and b/res/btn_small_24.bmp differ diff --git a/res/car1_24.bmp b/res/car1_24.bmp new file mode 100644 index 0000000..cb7475f Binary files /dev/null and b/res/car1_24.bmp differ diff --git a/res/car2_24.bmp b/res/car2_24.bmp new file mode 100644 index 0000000..6ca8f3d Binary files /dev/null and b/res/car2_24.bmp differ diff --git a/res/checkbox_24.bmp b/res/checkbox_24.bmp new file mode 100644 index 0000000..00bfe00 Binary files /dev/null and b/res/checkbox_24.bmp differ diff --git a/res/city1.txt b/res/city1.txt index 35aa10f..88ec6cc 100644 --- a/res/city1.txt +++ b/res/city1.txt @@ -1,21 +1,31 @@ -24 16 +24 24 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 +0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 +0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 +0053 0054 0054 0054 0054 0054 0054 0054 0053 0054 0054 0054 0054 0054 0054 0054 0054 0054 0054 0053 0054 0054 0054 0054 +0048 0049 0049 0049 0049 0049 0049 0049 0048 0049 0049 0049 0049 0049 0049 0049 0049 0049 0049 0048 0049 0049 0049 0049 +0055 0056 0056 0056 0056 0056 0056 0056 0055 0056 0056 0056 0056 0056 0056 0056 0050 005e 0051 0055 0056 0056 0056 0056 +0001 0001 0001 0001 0001 0001 0001 0001 0001 0001 0001 0001 0001 0001 0001 0002 004a 004b 004c 0000 0001 0001 0001 0001 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 +0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 0009 000a 0057 0058 0059 0008 0000 0000 0000 0000 +0030 0031 0031 0032 0000 0000 0000 0000 0030 0031 0031 0032 0000 0000 0009 000a 0057 0058 0059 0008 0000 0000 0000 0000 +0038 0039 0039 003a 0000 0000 0000 0000 0038 0039 0039 003a 0000 0000 0009 000a 0057 0058 0059 0008 0000 0000 0000 0000 +0040 0041 0041 0042 0000 0000 0000 0000 0040 0041 0041 0042 0000 0000 0009 000a 0057 0058 0059 0008 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 -0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 +0062 0063 0064 0062 0a00 0a00 0a00 0a00 0062 0063 0064 0062 0a00 0a00 0a00 0a00 0057 0058 0059 0008 0a00 0a00 0a00 0a00 +0069 006a 006c 0069 0a00 0a00 0a00 0a00 0069 006a 006c 0069 0a00 0a00 0a00 0a00 0057 0058 0059 0008 0a00 0a00 0a00 0a00 +0070 0071 0073 0070 0a00 0a00 0a00 0a00 0070 0071 0073 0070 0a00 0a00 0a00 0a00 0057 0058 0059 0008 0a00 0a00 0a00 0a00 +0077 0078 007a 0077 0a00 0a00 0a00 0a00 0077 0078 007a 0077 0a00 0a00 0a00 0a00 0057 0058 0059 0008 0a00 0a00 0a00 0a00 + +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 + +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 +0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 0a00 diff --git a/res/font.bmp b/res/font.bmp new file mode 100644 index 0000000..7fc1c36 Binary files /dev/null and b/res/font.bmp differ diff --git a/res/font_24.bmp b/res/font_24.bmp new file mode 100644 index 0000000..b12a8ce Binary files /dev/null and b/res/font_24.bmp differ diff --git a/res/functions.cmake b/res/functions.cmake index 723e922..2a7e7c3 100644 --- a/res/functions.cmake +++ b/res/functions.cmake @@ -35,6 +35,7 @@ function(sprite) VERBATIM) add_custom_target(${SPRITE_NAME}_img DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${SPRITE_NAME}) + add_dependencies(${PROJECT_NAME} ${SPRITE_NAME}_img) add_dependencies(${SPRITE_NAME}_img tools) endif() endfunction() @@ -73,6 +74,7 @@ function(sound) VERBATIM) add_custom_target(${SOUND_NAME}_snd DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${SOUND_NAME}) + add_dependencies(${PROJECT_NAME} ${SOUND_NAME}_snd) add_dependencies(${SOUND_NAME}_snd tools) endif() endfunction() diff --git a/res/line_edit_left_24.bmp b/res/line_edit_left_24.bmp new file mode 100644 index 0000000..dd977f6 Binary files /dev/null and b/res/line_edit_left_24.bmp differ diff --git a/res/line_edit_mid_24.bmp b/res/line_edit_mid_24.bmp new file mode 100644 index 0000000..ca934a0 Binary files /dev/null and b/res/line_edit_mid_24.bmp differ diff --git a/res/line_edit_right_24.bmp b/res/line_edit_right_24.bmp new file mode 100644 index 0000000..f9cf4e0 Binary files /dev/null and b/res/line_edit_right_24.bmp differ diff --git a/res/sel_down_left_24.bmp b/res/sel_down_left_24.bmp new file mode 100644 index 0000000..98caa7f Binary files /dev/null and b/res/sel_down_left_24.bmp differ diff --git a/res/sel_down_right_24.bmp b/res/sel_down_right_24.bmp new file mode 100644 index 0000000..01db4c2 Binary files /dev/null and b/res/sel_down_right_24.bmp differ diff --git a/res/sel_mid_down_24.bmp b/res/sel_mid_down_24.bmp new file mode 100644 index 0000000..20a2f71 Binary files /dev/null and b/res/sel_mid_down_24.bmp differ diff --git a/res/sel_mid_top_24.bmp b/res/sel_mid_top_24.bmp new file mode 100644 index 0000000..79c3a5b Binary files /dev/null and b/res/sel_mid_top_24.bmp differ diff --git a/res/sel_mid_v_24.bmp b/res/sel_mid_v_24.bmp new file mode 100644 index 0000000..e9093ae Binary files /dev/null and b/res/sel_mid_v_24.bmp differ diff --git a/res/sel_up_left_24.bmp b/res/sel_up_left_24.bmp new file mode 100644 index 0000000..65cb7d3 Binary files /dev/null and b/res/sel_up_left_24.bmp differ diff --git a/res/sel_up_right_24.bmp b/res/sel_up_right_24.bmp new file mode 100644 index 0000000..2bdefd3 Binary files /dev/null and b/res/sel_up_right_24.bmp differ diff --git a/res/unit5_24.bmp b/res/unit5_24.bmp index 5d9225a..5447f6e 100644 Binary files a/res/unit5_24.bmp and b/res/unit5_24.bmp differ diff --git a/src/building/inc/building.h b/src/building/inc/building.h index 7c7d45f..88840f5 100644 --- a/src/building/inc/building.h +++ b/src/building/inc/building.h @@ -33,7 +33,7 @@ void building_set_alive_cb(void (*f)(const struct util_rect *dim, bool alive, vo int building_render(const struct building *b, const struct camera *cam, bool sel); const char *building_str(const struct building *b); -extern struct sprite building_sprites[MAX_BUILDING_TYPES]; +extern struct sprite building_sprites[1]; #ifdef __cplusplus } diff --git a/src/building/src/building.c b/src/building/src/building.c index 826ebe1..ee1ee84 100644 --- a/src/building/src/building.c +++ b/src/building/src/building.c @@ -4,7 +4,7 @@ #include #include -struct sprite building_sprites[MAX_BUILDING_TYPES]; +struct sprite building_sprites[1]; int building_render(const struct building *const b, const struct camera *const cam, const bool sel) diff --git a/src/camera/inc/camera.h b/src/camera/inc/camera.h index 27efe2e..96a64d5 100644 --- a/src/camera/inc/camera.h +++ b/src/camera/inc/camera.h @@ -18,7 +18,7 @@ struct camera long w, h; } dim; - int x, y, x_speed, y_speed; + long x, y, x_speed, y_speed; unsigned xt, yt; bool pan; @@ -28,6 +28,7 @@ struct camera enum { CURSOR_STATE_IDLE, + CURSOR_STATE_HOVERING, CURSOR_STATE_PRESSED } state; struct diff --git a/src/camera/src/camera.c b/src/camera/src/camera.c index 9d3d062..5e658a5 100644 --- a/src/camera/src/camera.c +++ b/src/camera/src/camera.c @@ -55,9 +55,13 @@ int cursor_render(const struct cursor *const c) case CURSOR_STATE_IDLE: break; - case CURSOR_STATE_PRESSED: + case CURSOR_STATE_HOVERING: s->u += CAMERA_CURSOR_WIDTH; break; + + case CURSOR_STATE_PRESSED: + s->u += CAMERA_CURSOR_WIDTH * 2; + break; } const int ret = sprite_sort(s); @@ -89,23 +93,24 @@ void cursor_init(struct cursor *const c) void camera_update_pos(struct camera *const cam) { - const int x = cam->x + cam->x_speed; + const struct camera_dim *const d = &cam->dim; - cam->x = x; + if (!d->w || !d->h) + return; + + cam->x += cam->x_speed; if (cam->x > 0) cam->x = 0; - else if (cam->x < -cam->dim.w) - cam->x = -cam->dim.w; + else if (cam->x < -d->w + screen_w) + cam->x = -d->w + screen_w; - const int y = cam->y + cam->y_speed; - - cam->y = y; + cam->y += cam->y_speed; if (cam->y > 0) cam->y = 0; - else if (cam->y < -cam->dim.h) - cam->y = -cam->dim.h; + else if (cam->y < -d->h + screen_h) + cam->y = -d->h + screen_h; } bool camera_translate(const struct camera *const cam, const struct util_rect *const dim, diff --git a/src/camera/src/mouse.c b/src/camera/src/mouse.c index 30fedb9..e7b144b 100644 --- a/src/camera/src/mouse.c +++ b/src/camera/src/mouse.c @@ -13,9 +13,14 @@ static void cursor_update(struct cursor *const c, const struct mouse *const m) c->x = m->x; c->y = m->y; - c->state = mouse_pressed(m, MOUSE_BUTTON_LEFT) || - mouse_pressed(m, MOUSE_BUTTON_RIGHT) ? - CURSOR_STATE_PRESSED: CURSOR_STATE_IDLE; + + if (mouse_pressed(m, MOUSE_BUTTON_LEFT) + || mouse_pressed(m, MOUSE_BUTTON_RIGHT)) + c->state = CURSOR_STATE_PRESSED; + else if (m->hovering) + c->state = CURSOR_STATE_HOVERING; + else + c->state = CURSOR_STATE_IDLE; } static void update_speed(struct camera *const cam, const struct mouse *const m) @@ -24,34 +29,55 @@ static void update_speed(struct camera *const cam, const struct mouse *const m) { MAX_SPEED = 10, STEP = 1, - THRESHOLD_X = CAMERA_CURSOR_WIDTH / 2, - THRESHOLD_Y = CAMERA_CURSOR_HEIGHT / 2 + T_STEP = 10, + THRESHOLD_X = CAMERA_CURSOR_WIDTH, + THRESHOLD_Y = CAMERA_CURSOR_HEIGHT }; const struct cursor *const c = &cam->cursor; if (c->x >= screen_w - THRESHOLD_X) { - if (cam->x_speed - STEP > -MAX_SPEED) - cam->x_speed -= STEP; + if (++cam->xt >= T_STEP) + { + cam->xt = 0; + + if (cam->x_speed - STEP > -MAX_SPEED) + cam->x_speed -= STEP; + } } else if (c->x < THRESHOLD_X) { - if (cam->x_speed + STEP < MAX_SPEED) - cam->x_speed += STEP; + if (++cam->xt >= T_STEP) + { + cam->xt = 0; + + if (cam->x_speed + STEP < MAX_SPEED) + cam->x_speed += STEP; + } } else cam->x_speed = 0; if (c->y >= screen_h - THRESHOLD_Y) { - if (cam->y_speed - STEP > -MAX_SPEED) - cam->y_speed -= STEP; + if (++cam->yt >= T_STEP) + { + cam->yt = 0; + + if (cam->y_speed - STEP > -MAX_SPEED) + cam->y_speed -= STEP; + } } else if (c->y < THRESHOLD_Y) { - if (cam->y_speed + STEP < MAX_SPEED) - cam->y_speed += STEP; + if (++cam->yt >= T_STEP) + { + cam->yt = 0; + + if (cam->y_speed + STEP < MAX_SPEED) + cam->y_speed += STEP; + } } else cam->y_speed = 0; diff --git a/src/camera/src/pad.c b/src/camera/src/pad.c index f3af527..b5dac1a 100644 --- a/src/camera/src/pad.c +++ b/src/camera/src/pad.c @@ -48,7 +48,7 @@ static void update_speed(struct camera *const cam, { MAX_SPEED = 10, STEP = 1, - T_STEP = 3 + T_STEP = 2 }; const struct cursor *const c = &cam->cursor; diff --git a/src/game/src/res.c b/src/game/src/res.c index dead212..acbe174 100644 --- a/src/game/src/res.c +++ b/src/game/src/res.c @@ -108,6 +108,102 @@ static const struct container c[] = .path = "unit6", .type = CONTAINER_TYPE_SPRITE, .data.sprite = &unit_sprites[UNIT_6_SPRITE] + }, + + { + .path = "sel_down_left", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_DOWN_LEFT] + }, + + { + .path = "sel_down_right", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_DOWN_RIGHT] + }, + + { + .path = "sel_mid_down", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID_DOWN] + }, + + { + .path = "sel_mid_top", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID_TOP] + }, + + { + .path = "sel_mid_v", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_MID_VERT] + }, + + { + .path = "sel_up_left", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_UP_LEFT] + }, + + { + .path = "sel_up_right", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_rounded_rect_sprites[GUI_ROUNDED_RECT_UP_RIGHT] + }, + + { + .path = "checkbox", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_checkbox_sprite + }, + + { + .path = "line_edit_left", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_line_edit_sprites[GUI_LINE_EDIT_LEFT] + }, + + { + .path = "line_edit_mid", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_line_edit_sprites[GUI_LINE_EDIT_MID] + }, + + { + .path = "line_edit_right", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &gui_line_edit_sprites[GUI_LINE_EDIT_RIGHT] + }, + + { + .path = "car1", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &unit_sprites[UNIT_CAR_1_SPRITE] + }, + + { + .path = "pavement", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &terrain_sprites[PAVEMENT] + }, + + { + .path = "car2", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &unit_sprites[UNIT_CAR_2_SPRITE] + }, + + { + .path = "building1", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &terrain_sprites[BUILDING1], + }, + + { + .path = "building2", + .type = CONTAINER_TYPE_SPRITE, + .data.sprite = &terrain_sprites[BUILDING2], } }; diff --git a/src/gui/inc/gui/rounded_rect.h b/src/gui/inc/gui/rounded_rect.h index 13242f4..b027b67 100644 --- a/src/gui/inc/gui/rounded_rect.h +++ b/src/gui/inc/gui/rounded_rect.h @@ -15,6 +15,7 @@ struct gui_rounded_rect { struct gui_common common; unsigned short w, h; + unsigned char r, g, b; bool adjust; }; @@ -27,13 +28,15 @@ enum GUI_ROUNDED_RECT_MID_VERT, GUI_ROUNDED_RECT_DOWN_LEFT, GUI_ROUNDED_RECT_DOWN_RIGHT, - GUI_ROUNDED_RECT_MID, + GUI_ROUNDED_RECT_MID_TOP, + GUI_ROUNDED_RECT_MID_DOWN, MAX_GUI_ROUNDED_RECT_SPRITES }; extern struct sprite gui_rounded_rect_sprites[MAX_GUI_ROUNDED_RECT_SPRITES]; +/* Pad one line to avoid redefinition. */ UTIL_STATIC_ASSERT(!offsetof(struct gui_rounded_rect, common), "unexpected offset for struct gui_rounded_rect"); diff --git a/src/gui/src/rounded_rect.c b/src/gui/src/rounded_rect.c index 4ae3780..f37dcae 100644 --- a/src/gui/src/rounded_rect.c +++ b/src/gui/src/rounded_rect.c @@ -134,30 +134,36 @@ static int render_rect(const struct gui_rounded_rect *const r, rect_get_or_ret(sel, -1); semitrans_rect_init(sel); - const struct sprite *const mid = &refs[GUI_ROUNDED_RECT_MID], + const struct sprite *const mid = &refs[GUI_ROUNDED_RECT_MID_TOP], *const vert = &refs[GUI_ROUNDED_RECT_MID_VERT]; sel->x = x + vert->w; sel->y = y + mid->h; sel->w = r->w - (vert->w * 2); sel->h = r->h - (mid->h * 2); - sel->r = 72; - sel->g = 66; - sel->b = 56; + sel->r = r->r; + sel->g = r->g; + sel->b = r->b; rect_sort(sel); return 0; } static int render_mid(const struct gui_rounded_rect *const r, - const short x, const short y) + const short x, const short y, const struct sprite *const s) { - const short mid_w = refs[GUI_ROUNDED_RECT_MID].w, + const short mid_w = s->w, top_w = refs[GUI_ROUNDED_RECT_UP_LEFT].w, w = r->w - (top_w * 2), rem_mid = w % mid_w, whole_mid = w / mid_w, n_mid = rem_mid ? whole_mid + 1 : whole_mid; + if (n_mid < 0) + { + fprintf(stderr, "%s: invalid dimensions\n", __func__); + return -1; + } + for (struct { size_t i; @@ -166,7 +172,7 @@ static int render_mid(const struct gui_rounded_rect *const r, { sprite_get_or_ret(m, -1); - if (sprite_clone(&refs[GUI_ROUNDED_RECT_MID], m)) + if (sprite_clone(s, m)) return -1; m->x = a.x; @@ -186,13 +192,15 @@ static int render_mid(const struct gui_rounded_rect *const r, static int render_midtop(const struct gui_rounded_rect *const r, const short x, const short y) { - return render_mid(r, x, y); + return render_mid(r, x, y, &refs[GUI_ROUNDED_RECT_MID_TOP]); } static int render_middown(const struct gui_rounded_rect *const r, const short x, const short y) { - return render_mid(r, x, y + r->h - refs[GUI_ROUNDED_RECT_MID].h); + const struct sprite *const s = &refs[GUI_ROUNDED_RECT_MID_DOWN]; + + return render_mid(r, x, y + r->h - s->h, s); } static int render(const struct gui_common *const g) @@ -242,8 +250,8 @@ static void add_child(struct gui_common *const parent, short w, h; const short ref_w = refs[GUI_ROUNDED_RECT_MID_VERT].w, - ref_h = refs[GUI_ROUNDED_RECT_MID].h, - min_w = ref_w * 2 + refs[GUI_ROUNDED_RECT_MID].w, + ref_h = refs[GUI_ROUNDED_RECT_MID_TOP].h, + min_w = ref_w * 2 + refs[GUI_ROUNDED_RECT_MID_TOP].w, min_h = ref_h * 2 + refs[GUI_ROUNDED_RECT_MID_VERT].h; child->cb->get_dim(child, &w, &h); diff --git a/src/instance/CMakeLists.txt b/src/instance/CMakeLists.txt index 2e777a9..3651400 100644 --- a/src/instance/CMakeLists.txt +++ b/src/instance/CMakeLists.txt @@ -1,3 +1,3 @@ add_library(instance "src/instance.c") target_include_directories(instance PUBLIC "inc") -target_link_libraries(instance PUBLIC camera gfx util) +target_link_libraries(instance PUBLIC camera gfx util PRIVATE gui) diff --git a/src/instance/src/instance.c b/src/instance/src/instance.c index 1e9e039..91683f5 100644 --- a/src/instance/src/instance.c +++ b/src/instance/src/instance.c @@ -19,47 +19,18 @@ void instance_cyclic(void) line_g_flip ^= true; } -static int draw_sel(const struct instance *const i, const short x, const short y) -{ - enum {R = 0, G = 255, B = 0}; - - stp_4line_get_or_ret(l, -1); - stp_4line_init(l); - l->x = l->vertices[2].x = l->vertices[3].x = x; - l->y = l->vertices[0].y = l->vertices[3].y = y; - l->vertices[0].x = l->vertices[1].x = x + i->r.w; - l->vertices[1].y = l->vertices[2].y = y + i->r.h; - l->r = R; - l->g = line_g; - l->b = B >> 2; - - for (size_t i = 0; i < sizeof l->vertices / sizeof *l->vertices; i++) - { - struct stp_4line_vtx *const v = &l->vertices[i]; - - v->r = R; - v->b = B; - } - - l->vertices[0].g = l->vertices[2].g = UCHAR_MAX - line_g; - l->vertices[1].g = l->vertices[3].g = line_g; - stp_4line_sort(l); - stp_4line_get_or_ret(ll, -1); - *ll = *l; - ll->x = ll->vertices[2].x = ll->vertices[3].x = l->x ? l->x - 1 : 0; - ll->y = ll->vertices[0].y = ll->vertices[3].y = l->y ? l->y - 1 : 0; - ll->vertices[0].x = ll->vertices[1].x = l->vertices[0].x + 1; - ll->vertices[1].y = ll->vertices[2].y = l->vertices[1].y + 1; - stp_4line_sort(ll); - return 0; -} - -static void render_sprite(struct sprite *const s, +static int render_sprite(struct sprite *const s, const struct instance_render_off *const off, const short x, const short y) { s->x = off ? x + off->x : x; s->y = off ? y + off->y : y; - sprite_sort(s); + + const int ret = sprite_sort(s); + + if (ret) + fprintf(stderr, "%s: sprite_sort failed\n", __func__); + + return ret; } static void render_quad(const struct instance_render_quad *const rq, @@ -99,9 +70,6 @@ int instance_render(const struct instance_render_cfg *const cfg) { const struct instance_render_off *const off = cfg->off; - if (cfg->sel && draw_sel(i, x, y)) - return -1; - switch (cfg->prim_type) { case INSTANCE_RENDER_CFG_SPRITE: @@ -120,10 +88,5 @@ int instance_render(const struct instance_render_cfg *const cfg) int instance_render_target(const struct instance *const i, const struct camera *const cam) { - short x, y; - - if (camera_translate(cam, &i->r, &x, &y)) - return draw_sel(i, x, y); - - return -1; + return 0; } diff --git a/src/menu/src/gamecfg_menu.c b/src/menu/src/gamecfg_menu.c index 85a99d2..d85e7f6 100644 --- a/src/menu/src/gamecfg_menu.c +++ b/src/menu/src/gamecfg_menu.c @@ -736,9 +736,10 @@ int menu_gamecfg(struct menu_common *const c, struct net_host *const h, if (start) { - struct game_cfg cfg = + const struct game_cfg cfg = { - .p = &c->p + .p = &c->p, + .map = "city1.txt" }; return game(&cfg); diff --git a/src/menu/src/main_menu.c b/src/menu/src/main_menu.c index a7b9e73..8beed92 100644 --- a/src/menu/src/main_menu.c +++ b/src/menu/src/main_menu.c @@ -4,14 +4,27 @@ #include #include #include +#include #include #include +enum role +{ + CREDITS_NAME_COPYRIGHT, + CREDITS_ROLE_GFX, + CREDITS_ROLE_PROGRAMMING, + CREDITS_ROLE_ENGINE, + CREDITS_ROLE_LANGUAGE, + + MAX_CREDIT_ROLES +}; + struct main_menu { bool start, settings, exit; struct gui_button play, settings_btn, exit_btn; struct gui_container cnt; + struct gui_label title, roles[MAX_CREDIT_ROLES]; }; static int update(struct menu_common *const c, void *const arg) @@ -34,6 +47,32 @@ static int render(const struct menu_common *const c, void *const arg) return 0; } +static void update_roles(struct main_menu *const m) +{ + static const char *const roles[] = + { + [CREDITS_NAME_COPYRIGHT] = "(C) 2024 Xavier Del Campo Romero", + [CREDITS_ROLE_GFX] = "Gfx: Kenney, Ivan Voirol", + [CREDITS_ROLE_PROGRAMMING] = "Coding: me", + [CREDITS_ROLE_ENGINE] = "Engine: also me lol", + [CREDITS_ROLE_LANGUAGE] = "Written in good old C" + }; + + UTIL_STATIC_ASSERT(sizeof roles / sizeof *roles + == sizeof m->roles / sizeof *m->roles, + "unexpected sizes"); + + for (size_t i = 0; i < sizeof roles / sizeof *roles; i++) + { + struct gui_label *const l = &m->roles[i]; + + gui_label_init(l); + l->common.hcentered = true; + l->text = roles[i]; + gui_add_child(&m->cnt.common, &l->common); + } +} + int menu_main(struct menu_common *const c) { do @@ -50,6 +89,15 @@ int menu_main(struct menu_common *const c) c->spacing = 4; } + { + struct gui_label *const l = &m.title; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Jancity, a city sandbox"; + gui_add_child(&m.cnt.common, &l->common); + } + { struct gui_button *const b = &m.play; @@ -87,6 +135,8 @@ int menu_main(struct menu_common *const c) gui_add_child(&m.cnt.common, &b->common); } + update_roles(&m); + while (!m.start && !m.settings && !c->p.common.exit) { if (menu_update(c, update, render, &m)) diff --git a/src/menu/src/menu.c b/src/menu/src/menu.c index cc508e7..682c7f2 100644 --- a/src/menu/src/menu.c +++ b/src/menu/src/menu.c @@ -34,9 +34,15 @@ int menu_update(struct menu_common *const c, rect_sort(r); if (render && render(c, arg)) + { + fprintf(stderr, "%s: render cb failed\n", __func__); return -1; + } else if (input_render(&c->in, &c->p)) + { + fprintf(stderr, "%s: input_render failed\n", __func__); return -1; + } switch (c->p.common.type) { @@ -53,7 +59,10 @@ int menu_update(struct menu_common *const c, } if (gfx_draw()) + { + fprintf(stderr, "%s: gfx_draw failed\n", __func__); return -1; + } return 0; } @@ -72,11 +81,16 @@ int menu(void) peripheral_init(&cfg, &c.p); settings_load("settings.ini", &c.s); - const struct game_cfg gcfg = +#if 1 + + struct game_cfg gcfg = { - .p = &c.p, - .map = "city1.txt" + .map = "city1.txt", + .p = &c.p }; return game(&gcfg); +#else + return menu_main(&c); +#endif } diff --git a/src/mouse/inc/mouse.h b/src/mouse/inc/mouse.h index 55d0227..44c4f5c 100644 --- a/src/mouse/inc/mouse.h +++ b/src/mouse/inc/mouse.h @@ -18,6 +18,7 @@ struct mouse { short x, y, dx, dy; int mask, oldmask; + bool first_clicked, hovering; }; void mouse_init(struct mouse *m); diff --git a/src/mouse/sdl-1.2/src/mouse.c b/src/mouse/sdl-1.2/src/mouse.c index 35fc030..dc10b0b 100644 --- a/src/mouse/sdl-1.2/src/mouse.c +++ b/src/mouse/sdl-1.2/src/mouse.c @@ -3,13 +3,21 @@ #include #include #include +#include #include +#include static void mouse_click(const SDL_MouseButtonEvent *const ev, struct mouse *const m) { int mask; + if (!m->first_clicked) + { + srand(time(NULL)); + m->first_clicked = true; + } + switch (ev->button) { case 1: diff --git a/src/peripheral/inc/peripheral.h b/src/peripheral/inc/peripheral.h index b686eba..c4156db 100644 --- a/src/peripheral/inc/peripheral.h +++ b/src/peripheral/inc/peripheral.h @@ -25,7 +25,7 @@ union peripheral struct peripheral_common { enum peripheral_type type; - bool exit; + bool init, exit; } common; struct peripheral_pad diff --git a/src/player/CMakeLists.txt b/src/player/CMakeLists.txt index 3297619..b6c645a 100644 --- a/src/player/CMakeLists.txt +++ b/src/player/CMakeLists.txt @@ -18,4 +18,5 @@ target_link_libraries(player util PRIVATE pad - gui) + gui + terrain) diff --git a/src/player/inc/human_player.h b/src/player/inc/human_player.h index 9cfd498..19c4670 100644 --- a/src/player/inc/human_player.h +++ b/src/player/inc/human_player.h @@ -2,6 +2,9 @@ #define HUMAN_PLAYER_H #include +#include +#include +#include #include #include #include @@ -34,6 +37,15 @@ struct human_player union peripheral *periph; struct input in; + struct human_player_item + { + bool show; + struct gui_button show_btn; + struct gui_rounded_rect rr; + struct gui_container topcnt; + struct gui_button add_walker, add_car, exit; + } item; + struct sel_instance { enum sel_type diff --git a/src/player/inc/player.h b/src/player/inc/player.h index cc191ca..06463f9 100644 --- a/src/player/inc/player.h +++ b/src/player/inc/player.h @@ -18,7 +18,7 @@ typedef unsigned player_team; enum { - PLAYER_MAX_UNITS = 5, + PLAYER_MAX_UNITS = 10, PLAYER_MAX_BUILDINGS = 5 }; diff --git a/src/player/src/human_player.c b/src/player/src/human_player.c index 6083ef3..b47f4dc 100644 --- a/src/player/src/human_player.c +++ b/src/player/src/human_player.c @@ -279,8 +279,7 @@ static void update_selected(struct human_player *const h) { struct sel_instance *const si = &h->sel[i]; - if (si->d.i - && (!si->d.i->alive || si->type == INSTANCE_TYPE_UNIT)) + if (si->d.i && !si->d.i->alive) { si->d.i = NULL; h->n_sel--; @@ -357,10 +356,26 @@ static void update_from_touch(struct human_player *const h, } } +static bool hovering_units(const struct human_player *const h) +{ + const struct player *const pl = &h->pl; + + for (size_t i = 0; i < sizeof pl->units / sizeof *pl->units; i++) + { + const struct unit *const u = &pl->units[i]; + const struct instance *const in = &u->instance; + + if (in->alive && cursor_collision(&h->cam, &in->r)) + return true; + } + + return false; +} + static void update_from_keyboard_mouse(struct human_player *const h, struct player_others *const o) { - const struct mouse *const m = &h->periph->kbm.mouse; + struct mouse *const m = &h->periph->kbm.mouse; const struct keyboard *const k = &h->periph->kbm.keyboard; const struct input *const in = &h->in; @@ -377,6 +392,8 @@ static void update_from_keyboard_mouse(struct human_player *const h, } else if (input_mouse_justreleased(in, m, MOUSE_BUTTON_RIGHT)) move_units(h, o); + else + m->hovering = hovering_units(h); } void human_player_update(struct human_player *const h, diff --git a/src/player/src/human_player_gui.c b/src/player/src/human_player_gui.c index e985d9c..ce995c1 100644 --- a/src/player/src/human_player_gui.c +++ b/src/player/src/human_player_gui.c @@ -10,16 +10,146 @@ #include #include #include +#include #include #include #include #include #include -enum {X_OFF = 8, Y_OFF = 8, HP_Y = 32}; +enum {X_OFF = 8, Y_OFF = 8}; + +static void on_item_pressed(void *const arg) +{ + bool *const show = arg; + + *show ^= true; +} + +static void on_add_car(void *const arg) +{ + struct human_player *const h = arg; + struct human_player_item *const it = &h->item; + const struct unit_cfg cfg = + { + .type = UNIT_CFG_TYPE_CAR, + .x = rand() % (MAP_W - 32), + .y = rand() % (MAP_H - 32), + }; + + player_create_unit(&cfg, &h->pl); + it->show = false; +} + +static void on_add_walker(void *const arg) +{ + struct human_player *const h = arg; + struct human_player_item *const it = &h->item; + const struct unit_cfg cfg = + { + .type = UNIT_CFG_TYPE_WALKER, + .x = rand() % (MAP_W - 32), + .y = rand() % (MAP_H - 32), + }; + + player_create_unit(&cfg, &h->pl); + it->show = false; +} + +static void on_exit(void *const arg) +{ + struct human_player *const h = arg; + + h->periph->common.exit = true; +} + +static void update_items(struct human_player *const h) +{ + struct human_player_item *const it = &h->item; + + { + struct gui_rounded_rect *const r = &it->rr; + + gui_rounded_rect_init(r); + r->common.hcentered = r->common.vcentered = true; + r->adjust = true; + } + + { + struct gui_container *const c = &it->topcnt; + + gui_container_init(c); + c->common.vcentered = true; + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 4; + gui_add_child(&it->rr.common, &c->common); + } + + { + struct gui_button *const b = &it->add_car; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.hcentered = true; + b->u.type1.w = 80; + b->u.type1.label.text = "Add car"; + b->on_pressed = on_add_car; + b->arg = h; + gui_add_child(&it->topcnt.common, &b->common); + } + + { + struct gui_button *const b = &it->add_walker; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.w = 80; + b->common.hcentered = true; + b->u.type1.label.text = "Add walker"; + b->on_pressed = on_add_walker; + b->arg = h; + gui_add_child(&it->topcnt.common, &b->common); + } + + { + struct gui_button *const b = &it->exit; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.w = 80; + b->common.hcentered = true; + b->u.type1.label.text = "Exit"; + b->on_pressed = on_exit; + b->arg = h; + gui_add_child(&it->topcnt.common, &b->common); + } + + struct camera cam = + { + .cursor = h->cam.cursor + }; + + gui_update(&it->rr.common, h->periph, &cam, &h->in); +} void human_player_gui_update(struct human_player *const h) { + struct human_player_item *const it = &h->item; + struct gui_button *const b = &it->show_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_SPRITE); + b->on_pressed = on_item_pressed; + b->arg = &it->show; + b->u.sprite.s = &gui_button_sprites[GUI_BUTTON_SMALL]; + b->common.x = screen_w - b->u.sprite.s->w; + + if (it->show) + update_items(h); + + struct camera cam = + { + .cursor = h->cam.cursor + }; + + gui_update(&b->common, h->periph, &cam, &h->in); } static int render_sel_single_building(const struct human_player *const h, @@ -41,14 +171,13 @@ static int render_sel_single_unit(const struct human_player *const h, const struct sel_instance *const sel, struct gui_common *const r) { const struct unit *const u = sel->d.u; - enum {CARRY_X = 96, CARRY_Y = 8}; struct gui_label ul; gui_label_init(&ul); - ul.common.x = X_OFF; - ul.common.y = Y_OFF; - ul.text = unit_str(u); + ul.common.hcentered = true; + ul.common.vcentered = true; + ul.text = u->name; gui_add_child(r, &ul.common); return gui_render(r); @@ -99,37 +228,44 @@ static int render_sel_multiple(const struct human_player *const h, static int render_top(const struct human_player *const h) { - struct gui_button b; - - gui_button_init(&b, GUI_BUTTON_TYPE_SPRITE); - b.on_pressed = NULL; - b.arg = NULL; - b.u.sprite.s = &gui_button_sprites[GUI_BUTTON_SMALL]; - b.common.x = screen_w - b.u.sprite.s->w; - - return gui_render(&b.common); + return gui_render(&h->item.show_btn.common); } static int render_sel(const struct human_player *const h) { + const short wlim = 120; struct gui_rounded_rect r; + short exp_w; gui_rounded_rect_init(&r); - r.common.x = 16; - r.w = screen_w - (r.common.x * 2); - r.h = screen_h / 4; + r.common.hcentered = true; + exp_w = screen_w - 32; + r.w = exp_w > wlim ? wlim : exp_w; + r.h = 40; + r.r = 10; + r.g = 137; + r.b = 225; r.common.y = screen_h - r.h; return h->n_sel == 1 ? render_sel_single(h, &r.common) : render_sel_multiple(h, &r.common); } +static int render_items(const struct human_player *const h) +{ + const struct human_player_item *const it = &h->item; + + return gui_render(&it->rr.common); +} + int human_player_gui_render(const struct human_player *const h) { if (render_top(h)) return -1; else if (h->n_sel && render_sel(h)) return -1; + else if (h->item.show && render_items(h)) + return -1; return 0; } diff --git a/src/player/src/player.c b/src/player/src/player.c index 5a69763..a3b0a52 100644 --- a/src/player/src/player.c +++ b/src/player/src/player.c @@ -1,6 +1,7 @@ #include #include #include +#include #include void player_update(struct player *const p) @@ -61,6 +62,22 @@ int player_create_building(const struct building_cfg *const cfg, struct player * int player_init(const struct player_cfg *const cfg, struct player *const pl) { + for (size_t i = 0; i < sizeof pl->units / sizeof *pl->units; i++) + { + const struct unit_cfg ucfg = + { + .type = rand() & 1 ? UNIT_CFG_TYPE_WALKER : UNIT_CFG_TYPE_CAR, + .x = rand() % (MAP_W - 32), + .y = rand() % (MAP_H - 32), + }; + + if (player_create_unit(&ucfg, pl)) + { + fprintf(stderr, "%s: player_create_unit failed\n", __func__); + return -1; + } + } + pl->alive = true; pl->color = cfg->color; pl->team = cfg->team; diff --git a/src/terrain/inc/terrain.h b/src/terrain/inc/terrain.h index 7997025..dc29883 100644 --- a/src/terrain/inc/terrain.h +++ b/src/terrain/inc/terrain.h @@ -13,7 +13,7 @@ extern "C" enum { - MAP_TILES = 120, + MAP_TILES = 24, TERRAIN_SZ = 16, MAP_W = MAP_TILES * TERRAIN_SZ, MAP_H = MAP_TILES * TERRAIN_SZ @@ -39,7 +39,9 @@ enum GRASS, ROOF1, ROOF2, + PAVEMENT, BUILDING1, + BUILDING2, MAX_TERRAIN_SPRITES }; diff --git a/src/terrain/privinc/terrain_tiles.h b/src/terrain/privinc/terrain_tiles.h index 962bafe..89b8457 100644 --- a/src/terrain/privinc/terrain_tiles.h +++ b/src/terrain/privinc/terrain_tiles.h @@ -15,7 +15,6 @@ enum SIDEWALK_2_NW, SIDEWALK_2_NE, SIDEWALK_3_NW, - SIDEWALK_3_N, SIDEWALK_3_NE, SIDEWALK_4_N, @@ -25,7 +24,6 @@ enum SIDEWALK_2_SW, SIDEWALK_2_SE, SIDEWALK_3_SW, - SIDEWALK_3_S, SIDEWALK_3_SE, SIDEWALK_4_C, @@ -49,7 +47,6 @@ enum ROOF1_2_NE, ROOF1_3_NW, ROOF1_3_N, - ROOF1_3_NE, ROOF1_4_N, ROOF1_1_W, @@ -58,7 +55,6 @@ enum ROOF1_2_SW, ROOF1_2_SE, ROOF1_3_SW, - ROOF1_3_S, ROOF1_3_SE, ROOF1_4_C, @@ -82,7 +78,6 @@ enum ROOF2_2_NE, ROOF2_3_NW, ROOF2_3_N, - ROOF2_3_NE, ROOF2_4_N, ROOF2_1_W, @@ -91,7 +86,6 @@ enum ROOF2_2_SW, ROOF2_2_SE, ROOF2_3_SW, - ROOF2_3_S, ROOF2_3_SE, ROOF2_4_C, @@ -105,7 +99,111 @@ enum ROOF2_4_S, ROOF2_START = ROOF2_1_NW, - ROOF2_END = ROOF2_4_S + ROOF2_END = ROOF2_4_S, + + /* PAVEMENT */ + PAVEMENT_CW_H_MID, + PAVEMENT_STR_MID_H_1, + PAVEMENT_CW_V_LEFT, + PAVEMENT_CW_V_MID, + PAVEMENT_CW_V_RIGHT, + PAVEMENT_RND_NW, + PAVEMENT_RND_NE, + PAVEMENT_SP_NW, + PAVEMENT_SP_NE, + PAVEMENT_1, + PAVEMENT_P, + PAVEMENT_CW_H_TOP, + PAVEMENT_STR_H_TOP, + + PAVEMENT_CW_H_DOWN, + PAVEMENT_STR_H_DOWN, + PAVEMENT_STR_V_LEFT, + PAVEMENT_STR_V_MID, + PAVEMENT_STR_V_RIGHT, + PAVEMENT_RND_SW, + PAVEMENT_RND_SE, + PAVEMENT_SP_SW, + PAVEMENT_SP_SE, + PAVEMENT_2, + PAVEMENT_BIKE, + PAVEMENT_PLUS, + PAVEMENT_EMPTY, + + PAVEMENT_START = PAVEMENT_CW_H_MID, + PAVEMENT_END = PAVEMENT_EMPTY, + + /* BUILDING1 */ + BUILDING1_1_N, + BUILDING1_2_NW, + BUILDING1_2_N, + BUILDING1_2_NE, + BUILDING1_3_NW, + BUILDING1_3_N, + BUILDING1_3_NE, + + BUILDING1_1_C_1, + BUILDING1_2_W_1, + BUILDING1_2_C_1, + BUILDING1_2_E_1, + BUILDING1_3_W_1, + BUILDING1_3_C_1, + BUILDING1_3_E_1, + + BUILDING1_1_C_2, + BUILDING1_2_W_2, + BUILDING1_2_C_2, + BUILDING1_2_E_2, + BUILDING1_3_W_2, + BUILDING1_3_C_2, + BUILDING1_3_E_2, + + BUILDING1_1_S, + BUILDING1_2_SW, + BUILDING1_2_S, + BUILDING1_2_SE, + BUILDING1_3_SW, + BUILDING1_3_S, + BUILDING1_3_SE, + + BUILDING1_START = BUILDING1_1_N, + BUILDING1_END = BUILDING1_2_SE, + + /* BUILDING2 */ + BUILDING2_1_N, + BUILDING2_2_NW, + BUILDING2_2_N, + BUILDING2_2_NE, + BUILDING2_3_NW, + BUILDING2_3_N, + BUILDING2_3_NE, + + BUILDING2_1_C_1, + BUILDING2_2_W_1, + BUILDING2_2_C_1, + BUILDING2_2_E_1, + BUILDING2_3_W_1, + BUILDING2_3_C_1, + BUILDING2_3_E_1, + + BUILDING2_1_C_2, + BUILDING2_2_W_2, + BUILDING2_2_C_2, + BUILDING2_2_E_2, + BUILDING2_3_W_2, + BUILDING2_3_C_2, + BUILDING2_3_E_2, + + BUILDING2_1_S, + BUILDING2_2_SW, + BUILDING2_2_S, + BUILDING2_2_SE, + BUILDING2_3_SW, + BUILDING2_3_S, + BUILDING2_3_SE, + + BUILDING2_START = BUILDING2_1_N, + BUILDING2_END = BUILDING2_3_SE }; enum @@ -119,7 +217,6 @@ enum GRASS_2_NW, GRASS_2_NE, GRASS_3_NW, - GRASS_3_N, GRASS_3_NE, GRASS_4_N, @@ -129,7 +226,6 @@ enum GRASS_2_SW, GRASS_2_SE, GRASS_3_SW, - GRASS_3_S, GRASS_3_SE, GRASS_4_C, diff --git a/src/terrain/src/init.c b/src/terrain/src/init.c index 88140d8..c16859c 100644 --- a/src/terrain/src/init.c +++ b/src/terrain/src/init.c @@ -133,6 +133,8 @@ int terrain_init(const char *const path, struct terrain_map *const map) FILE *const f = fopen(path, "rb"); unsigned w, h; + *map = (const struct terrain_map){0}; + if (!f) { fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno)); diff --git a/src/terrain/src/render.c b/src/terrain/src/render.c index 8b35d95..bfa1e74 100644 --- a/src/terrain/src/render.c +++ b/src/terrain/src/render.c @@ -22,7 +22,7 @@ struct tile static int render_tile(const unsigned char id, const short x, const short y, const struct tile *const tiles, const size_t n) { - for (size_t i = 0; i < n / n; i++) + for (size_t i = 0; i < n; i++) { const struct tile *const rt = &tiles[i]; @@ -37,7 +37,8 @@ static int render_tile(const unsigned char id, const short x, } const unsigned char pos = id - rt->start; - const short tx = pos % TERRAIN_SZ, ty = pos / TERRAIN_SZ; + const short tx = pos % (s->w / TERRAIN_SZ), + ty = (pos * TERRAIN_SZ) / s->w; s->x = x; s->y = y; @@ -67,7 +68,10 @@ static int render_ground(const struct terrain_tile *const t, const short x, #define TILE(t) {.s = &terrain_sprites[t], .start = t##_START, .end = t##_END} TILE(SIDEWALK), TILE(ROOF1), - TILE(ROOF2) + TILE(ROOF2), + TILE(PAVEMENT), + TILE(BUILDING1), + TILE(BUILDING2) #undef TILE }; diff --git a/src/unit/inc/unit.h b/src/unit/inc/unit.h index 8d3e1bf..c9abafe 100644 --- a/src/unit/inc/unit.h +++ b/src/unit/inc/unit.h @@ -33,17 +33,14 @@ struct unit { struct instance instance; enum unit_type type; + const char *name; enum unit_dir { - UNIT_DIR_N, - UNIT_DIR_NE, - UNIT_DIR_E, - UNIT_DIR_SE, - UNIT_DIR_S, - UNIT_DIR_SW, UNIT_DIR_W, - UNIT_DIR_NW + UNIT_DIR_S, + UNIT_DIR_N, + UNIT_DIR_E } dir; enum unit_state state; @@ -61,7 +58,12 @@ UTIL_STATIC_ASSERT(!offsetof(struct unit, instance), "must be at offset zero"); struct unit_cfg { - enum unit_type type; + enum + { + UNIT_CFG_TYPE_WALKER, + UNIT_CFG_TYPE_CAR + } type; + unsigned long x, y; }; @@ -72,7 +74,6 @@ bool unit_target_valid(const struct unit *u, const struct unit_target *t); void unit_set_target(struct unit *u, const struct unit_target *t); void unit_move_to(struct unit *u, unsigned long x, unsigned long y); void unit_update(struct unit *u); -const char *unit_str(const struct unit *u); enum { @@ -82,6 +83,8 @@ enum UNIT_4_SPRITE, UNIT_5_SPRITE, UNIT_6_SPRITE, + UNIT_CAR_1_SPRITE, + UNIT_CAR_2_SPRITE, MAX_UNIT_SPRITES }; diff --git a/src/unit/inc/unit_type.h b/src/unit/inc/unit_type.h index f023784..ea59856 100644 --- a/src/unit/inc/unit_type.h +++ b/src/unit/inc/unit_type.h @@ -3,6 +3,15 @@ enum unit_type { + UNIT_TYPE_1, + UNIT_TYPE_2, + UNIT_TYPE_3, + UNIT_TYPE_4, + UNIT_TYPE_5, + UNIT_TYPE_6, + UNIT_TYPE_CAR_1, + UNIT_TYPE_CAR_2, + MAX_UNIT_TYPES }; diff --git a/src/unit/src/unit.c b/src/unit/src/unit.c index 1bb1e0f..43dcc86 100644 --- a/src/unit/src/unit.c +++ b/src/unit/src/unit.c @@ -11,7 +11,7 @@ struct sprite unit_sprites[MAX_UNIT_SPRITES]; struct sound unit_sounds[MAX_UNIT_SOUNDS]; -enum {N_FRAMES = 5}; +static const unsigned char anim[] = {0, 1, 0, 2}; static void move_unit(struct unit *const u, const fix16_t sx, const fix16_t sy) { @@ -21,38 +21,18 @@ static void move_unit(struct unit *const u, const fix16_t sx, const fix16_t sy) u->ry -= sy; break; - case UNIT_DIR_NE: - u->rx += sx; - u->ry -= sy; - break; - case UNIT_DIR_E: u->rx += sx; break; - case UNIT_DIR_SE: - u->rx += sx; - u->ry += sy; - break; - case UNIT_DIR_S: u->ry += sy; break; - case UNIT_DIR_SW: - u->rx -= sx; - u->ry += sy; - break; - case UNIT_DIR_W: u->rx -= sx; break; - case UNIT_DIR_NW: - u->rx -= sx; - u->ry -= sy; - break; - default: break; } @@ -61,17 +41,51 @@ static void move_unit(struct unit *const u, const fix16_t sx, const fix16_t sy) static void get_speed(const struct unit *const u, fix16_t *const x, fix16_t *const y) { - static const struct speed + struct speed { fix16_t x, y; - } speed[1]; + } s; + + switch (u->type) + { + case UNIT_TYPE_1: + case UNIT_TYPE_2: + case UNIT_TYPE_3: + case UNIT_TYPE_4: + case UNIT_TYPE_5: + case UNIT_TYPE_6: + { + static const struct speed ref = + { + .x = FIX16_C_FROM_FLOAT(0.4), + .y = FIX16_C_FROM_FLOAT(0.4) + }; + + s = ref; + } + break; + + case UNIT_TYPE_CAR_1: + case UNIT_TYPE_CAR_2: + { + static const struct speed ref = + { + .x = FIX16_C_FROM_FLOAT(1.5), + .y = FIX16_C_FROM_FLOAT(1.5) + }; + + s = ref; + } + + case MAX_UNIT_TYPES: + break; + } - const struct speed *const s = &speed[u->type]; const int dx = abs(u->rx - u->tx); const int dy = abs(u->ry - u->ty); - *x = dx < s->x ? dx : s->x; - *y = dy < s->y ? dy : s->y; + *x = dx < s.x ? dx : s.x; + *y = dy < s.y ? dy : s.y; } static enum unit_dir get_direction(const struct unit *const u) @@ -79,18 +93,7 @@ static enum unit_dir get_direction(const struct unit *const u) const fix16_t x = u->rx, y = u->ry, tx = u->tx, ty = u->ty; enum unit_dir dir = 0; - if (x != tx && y != ty) - { - if (x > tx && y > ty) - dir = UNIT_DIR_NW; - else if (x < tx && y > ty) - dir = UNIT_DIR_NE; - else if (x < tx && y < ty) - dir = UNIT_DIR_SE; - else - dir = UNIT_DIR_SW; - } - else if (x != tx) + if (x != tx) { if (x > tx) dir = UNIT_DIR_W; @@ -155,7 +158,7 @@ static bool must_move(const struct unit *const u) void unit_update(struct unit *const u) { - const struct instance *const i = &u->instance; + struct instance *const i = &u->instance; if (!i->alive) return; @@ -170,13 +173,13 @@ void unit_update(struct unit *const u) get_speed(u, &x_step, &y_step); move_unit(u, x_step, y_step); - enum {FRAME_RATE = 6}; + enum {FRAME_RATE = 10}; if (++u->frame.t >= FRAME_RATE) { u->frame.t = 0; - if (++u->frame.i >= N_FRAMES) + if (++u->frame.i >= sizeof anim / sizeof *anim) u->frame.i = 0; } @@ -185,8 +188,8 @@ void unit_update(struct unit *const u) else u->frame.i = 0; - u->instance.r.x = fix16_to_int(u->rx); - u->instance.r.y = fix16_to_int(u->ry); + i->r.x = fix16_to_int(u->rx); + i->r.y = fix16_to_int(u->ry); } void unit_set_target(struct unit *const u, const struct unit_target *const t) @@ -209,198 +212,101 @@ void unit_move_to(struct unit *const u, const unsigned long x, const unsigned lo u->ty = y > y_off ? fix16_from_int(y - y_off) : 0; } -static int get_ux(const struct unit *const u) +static void adjust_walker(const struct unit *const u, struct sprite *const s, + struct instance_render_off *const off) +{ + s->w = u->instance.r.w; + s->h = u->instance.r.h; + s->u += u->dir * s->w; + s->v += anim[u->frame.i] * s->h; + off->x = 0; + off->y = -2; +} + +static void adjust_car(const struct unit *const u, struct sprite *const s, + struct instance_render_off *const off) { switch (u->dir) { - case UNIT_DIR_N: - return 0; - - case UNIT_DIR_NE: - /* Fall through. */ - case UNIT_DIR_NW: - return 1; - - case UNIT_DIR_E: - /* Fall through. */ case UNIT_DIR_W: - return 2; - - case UNIT_DIR_SE: - /* Fall through. */ - case UNIT_DIR_SW: - return 3; + s->w = s->h = 32; + s->u += 32; + off->x = -5; + break; case UNIT_DIR_S: - return 4; + s->w = 16; + s->h = 32; + off->x = 2; + break; - default: + case UNIT_DIR_N: + s->u += 16; + s->w = 16; + s->h = 32; + off->x = 2; + break; + + case UNIT_DIR_E: + s->w = s->h = 32; + s->u += 64; + off->x = -5; break; } - return -1; + off->y = -10; } -typedef const struct +static void adjust_sprite(const struct unit *const u, struct sprite *const s, + struct instance_render_off *const off) { - const struct sprite *s; - char xo, x[N_FRAMES], w[N_FRAMES]; - short y; - short h; -} anim_dim; - -static anim_dim *peasant_anim(const struct unit *const u) -{ -#if 0 - static anim_dim t[] = + switch (u->type) { - { - .s = &unit_sprites[UNIT_SPRITE_N], - .xo = 5, - .x = {0, 1, 1, 2, 1}, - .w = {25, 22, 24, 22, 24}, - .y = 2, - .h = 31 - }, - - { - .s = &unit_sprites[UNIT_SPRITE_NE], - .xo = 11, - .x = {0, -4, -1, -4, -2}, - .w = {18, 26, 22, 23, 20}, - .y = 2, - .h = 31 - }, - - { - .s = &unit_sprites[UNIT_SPRITE_E], - .xo = 10, - .x = {0, -6, -1, -6, -3}, - .w = {14, 26, 17, 24, 19}, - .y = 2, - .h = 33 - }, - - { - .s = &unit_sprites[UNIT_SPRITE_SE], - .xo = 6, - .x = {0, 1, 2, 0, 0}, - .w = {20, 22, 18, 21, 21}, - .y = 2, - .h = 31 - }, - - { - .s = &unit_sprites[UNIT_SPRITE_S], - .xo = 7, - .x = {0, 1, 0, 0, 0}, - .w = {24, 23, 24, 23, 24}, - .y = 2, - .h = 33 - } - }; - - const int ux = get_ux(u); - - if (ux < 0) - return NULL; - - return &t[ux]; -#else - return NULL; -#endif -} - -struct render_cfg -{ - struct instance_render_off off; - struct instance_render_quad qcfg; -}; - -static anim_dim *peasant_quad(const struct unit *const u) -{ - return peasant_anim(u); -} - -static void adjust_quad(const struct unit *const u, anim_dim *const dim, - struct render_cfg *const rcfg) -{ - const unsigned char n = u->frame.i; - short u_off = 0; - - for (unsigned char i = 0; i < n; i++) - u_off += dim->w[i]; - - struct instance_render_quad *const qcfg = &rcfg->qcfg; - - qcfg->u = u_off; - qcfg->w = dim->w[n]; - qcfg->h = dim->h; - - struct instance_render_off *const off = &rcfg->off; - - off->x = dim->xo; - off->y = dim->y; - - switch (u->dir) - { - case UNIT_DIR_SW: - /* Fall through. */ - case UNIT_DIR_W: - /* Fall through. */ - case UNIT_DIR_NW: - qcfg->xflip = true; - off->x += dim->x[n]; + case UNIT_TYPE_1: + case UNIT_TYPE_2: + case UNIT_TYPE_3: + case UNIT_TYPE_4: + case UNIT_TYPE_5: + case UNIT_TYPE_6: + adjust_walker(u, s, off); break; - default: - qcfg->xflip = false; - off->x += dim->x[n]; + case UNIT_TYPE_CAR_1: + case UNIT_TYPE_CAR_2: + adjust_car(u, s, off); + break; + + case MAX_UNIT_TYPES: break; } } -static int unit_quad(const struct unit *const u, struct render_cfg *const rcfg) -{ - struct instance_render_quad *const qcfg = &rcfg->qcfg; - - static anim_dim *(*const f[1])(const struct unit *); - - anim_dim *const dim = f[u->type](u); - - if (!dim) - return -1; - - adjust_quad(u, dim, rcfg); - - if (quad_from_sprite(dim->s, qcfg->q)) - return -1; - - return 0; -} - int unit_render(const struct unit *const u, const struct camera *const cam, const bool sel) { if (!u->instance.alive) return 0; - struct render_cfg rcfg; + sprite_get_or_ret(s, -1); - quad_get_or_ret(q, -1); - rcfg.qcfg.q = q; + struct instance_render_off off; - if (unit_quad(u, &rcfg)) + if (sprite_clone(&unit_sprites[u->type], s)) + { + fprintf(stderr, "%s: sprite_clone failed\n", __func__); return -1; + } + + adjust_sprite(u, s, &off); const struct instance_render_cfg cfg = { .i = &u->instance, - .prim_type = INSTANCE_RENDER_CFG_QUAD, - .prim = {.quad = &rcfg.qcfg}, + .prim_type = INSTANCE_RENDER_CFG_SPRITE, + .prim.s = s, + .off = &off, .cam = cam, - .sel = sel, - .off = &rcfg.off + .sel = sel }; return instance_render(&cfg); @@ -409,40 +315,159 @@ int unit_render(const struct unit *const u, const struct camera *const cam, static void get_dimensions(const enum unit_type type, short *const w, short *const h) { - static const struct dim + switch (type) { - short w, h; - } dim[1]; + case UNIT_TYPE_1: + case UNIT_TYPE_2: + case UNIT_TYPE_3: + case UNIT_TYPE_4: + case UNIT_TYPE_5: + case UNIT_TYPE_6: + *w = *h = 16; + break; - const struct dim *const d = &dim[type]; - *w = d->w; - *h = d->h; + case UNIT_TYPE_CAR_1: + case UNIT_TYPE_CAR_2: + *w = *h = 22; + break; + + case MAX_UNIT_TYPES: + break; + } +} + +static const char *get_walker_name(const enum unit_type type) +{ + enum {FEMALE, MALE} gender = 0; + + switch (type) + { + case UNIT_TYPE_2: + case UNIT_TYPE_5: + gender = FEMALE; + break; + + case UNIT_TYPE_1: + case UNIT_TYPE_3: + case UNIT_TYPE_4: + case UNIT_TYPE_6: + gender = MALE; + break; + + default: + return NULL; + } + + static const char *const female[] = + { + "Amelia", + "Asher", + "Aurora", + "Ava", + "Charlotte", + "Dana", + "Ellie", + "Emma", + "Ezra", + "Haley", + "Harper", + "Isabella", + "Luna", + "Mire", + "Olivia", + "Sophia", + }; + + static const char *const male[] = + { + "Oliver", + "Xavi", + "Benjamin", + "Elijah", + "Ethan", + "Jack", + "James", + "Jan", + "Kyte", + "Leo", + "Levi", + "Liam", + "Luca", + "Lucas", + "Michael", + "Noah", + }; + + switch (gender) + { + case FEMALE: + { + const size_t i = rand() % (sizeof female / sizeof *female); + + return female[i]; + } + + case MALE: + { + const size_t i = rand() % (sizeof male / sizeof *male); + + return male[i]; + } + } + + return NULL; +} + +static const char *get_name(const enum unit_type type) +{ + switch (type) + { + case UNIT_TYPE_1: + case UNIT_TYPE_2: + case UNIT_TYPE_3: + case UNIT_TYPE_4: + case UNIT_TYPE_5: + case UNIT_TYPE_6: + return get_walker_name(type); + + case UNIT_TYPE_CAR_1: + case UNIT_TYPE_CAR_2: + return "Car"; + + case MAX_UNIT_TYPES: + break; + } + + return NULL; } void unit_create(const struct unit_cfg *const cfg, struct unit *const u) { struct instance *const i = &u->instance; + enum unit_type type = 0; + + switch (cfg->type) + { + case UNIT_CFG_TYPE_WALKER: + type = UNIT_TYPE_1 + (rand() % (UNIT_TYPE_6 + 1)); + break; + + case UNIT_CFG_TYPE_CAR: + type = UNIT_TYPE_CAR_1 + (rand() + % (UNIT_TYPE_CAR_2 - UNIT_TYPE_CAR_1 + 1)); + break; + } *u = (const struct unit) { - .instance = - { - .alive = true, - }, - - .type = cfg->type, + .instance.alive = true, .dir = UNIT_DIR_S, .rx = fix16_from_int(cfg->x), - .ry = fix16_from_int(cfg->y) + .ry = fix16_from_int(cfg->y), + .type = type, + .name = get_name(type) }; - get_dimensions(cfg->type, &i->r.w, &i->r.h); + get_dimensions(u->type, &i->r.w, &i->r.h); unit_stop(u); } - -const char *unit_str(const struct unit *const u) -{ - static const char *const str[1]; - - return str[u->type]; -}