From 1c9ca3be5a046964ae0d535fa02a9ea245f8f8c7 Mon Sep 17 00:00:00 2001 From: Abdulmujeeb Raji Date: Fri, 30 Aug 2024 11:21:57 +0100 Subject: [PATCH] Basic HUD, Weapon Control Rework, Hitboxes fixed, Cooldowns added to attacks, Damage is now clamped --- README.md | 2 + assets/m3x6.ttf | Bin 0 -> 10044 bytes entity.c | 32 ++++++-- entry_helpless.c | 192 +++++++++++++++++++++++++---------------------- player.c | 38 ++++++++++ sprite.c | 13 ---- util.c | 22 ++++++ weapon.c | 43 +++++++++++ 8 files changed, 233 insertions(+), 109 deletions(-) create mode 100644 assets/m3x6.ttf create mode 100644 player.c create mode 100644 weapon.c diff --git a/README.md b/README.md index 76229e1..27791d8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +PERSONAL NOTE: Remember to put Daniel Lissen in the the credits for his font. + ooga booga diff --git a/assets/m3x6.ttf b/assets/m3x6.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e0a9664013a09f05ba7bbd450d1a4722684d2a87 GIT binary patch literal 10044 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 literal 0 HcmV?d00001 diff --git a/entity.c b/entity.c index 4ebabd8..86c81c9 100644 --- a/entity.c +++ b/entity.c @@ -21,8 +21,10 @@ typedef struct Entity { bool renderable; Entity_Archetype arch; Vector2 pos; - bool is_animate; - float32 health, damage; + bool is_animate, is_hostile; + float32 enemy_cooldown_secs; + float32 enemy_speed; + float32 max_health, health, damage; Vector2 hitbox[4]; Entity_Archetype primary_weapon; Entity_Archetype secondary_weapon; @@ -87,11 +89,29 @@ bool entity_increment(Entity **entity_ptr) return !!entity; } +Sprite_ID entity_sprite_id_from_arch(Entity_Archetype arch) { + switch (arch) { + case ARCH_player: return SPRITE_player; break; + case ARCH_spider: return SPRITE_spider ; break; + case ARCH_mutant: return SPRITE_mutant ; break; + case ARCH_tree: return SPRITE_tree ; break; + case ARCH_startersword: return SPRITE_startersword ; break; + case ARCH_starterbow: return SPRITE_starterbow ; break; + case ARCH_arrow: return SPRITE_arrow ; break; + default: return 0; + } +} + Vector2* entity_resolve_hitbox(Entity* en) { Vector2* hitbox = alloc(get_temporary_allocator(), sizeof(Vector2) * 4); - hitbox[0] = v2_add(en->hitbox[0], en->pos); - hitbox[1] = v2_add(en->hitbox[1], en->pos); - hitbox[2] = v2_add(en->hitbox[2], en->pos); - hitbox[3] = v2_add(en->hitbox[3], en->pos); + Sprite* sprite = sprite_get(entity_sprite_id_from_arch(en->arch)); + hitbox[0] = v2_add(en->hitbox[0], v2_add(en->pos, v2(sprite->size.x * -0.5, 0))); + hitbox[1] = v2_add(en->hitbox[1], v2_add(en->pos, v2(sprite->size.x * -0.5, 0))); + hitbox[2] = v2_add(en->hitbox[2], v2_add(en->pos, v2(sprite->size.x * -0.5, 0))); + hitbox[3] = v2_add(en->hitbox[3], v2_add(en->pos, v2(sprite->size.x * -0.5, 0))); return hitbox; +} + +void entity_take_damage(Entity* en, float damage) { + en->health = clamp(en->health - damage, 0, en->max_health); } \ No newline at end of file diff --git a/entry_helpless.c b/entry_helpless.c index 4c17b21..059dcdd 100644 --- a/entry_helpless.c +++ b/entry_helpless.c @@ -1,20 +1,19 @@ #include "util.c" -#include "entity.c" #include "sprite.c" +#include "entity.c" #include "gjk.c" - -void player_setup(Entity* en) { - en->arch = ARCH_player; - en->renderable = true; - en->is_animate = true; - en->health = 100; -} +#include "weapon.c" +#include "player.c" void spider_setup(Entity* en) { en->arch = ARCH_spider; en->renderable = true; en->is_animate = true; - en->health = 30; + en->is_hostile = true; + en->max_health = 30; + en->enemy_speed = 5; + en->health = en->max_health; + en->damage = 10; en->pos = v2(get_random_float32_in_range(-200, 200), get_random_float32_in_range(-200, 200)); en->hitbox[0] = v2(0, 0); en->hitbox[1] = v2(0, 7); @@ -22,50 +21,6 @@ void spider_setup(Entity* en) { en->hitbox[3] = v2(20, 7); } -void weapon_setup(Entity* en, Entity_Archetype weapon_type) { - en->arch = weapon_type; - en->renderable = true; - en->is_weapon = true; - switch (weapon_type) { - case ARCH_startersword: { - en->weapon_class = WEAPON_melee; - en->damage = 10; - en->hitbox[0] = v2(0, 0); - en->hitbox[1] = v2(0, 13); - en->hitbox[2] = v2(13, 0); - en->hitbox[3] = v2(13, 13); - en->weapon_speed = 3; - } break; - - case ARCH_starterbow: { - en->weapon_class = WEAPON_ranged; - en->damage = 5; - en->weapon_speed = 3; - } break; - - default: { - assert(false, "Invalid weapon type"); - } break; - } -} - -void arrow_setup(Entity* en, Vector2 start_pos, Vector2 fire_dir, float32 base_dmg) { - en->arch = ARCH_arrow; - en->renderable = true; - en->is_projectile = true; - en->is_animate = false; // idk why this was on in the first place? - en->projectile_range = 200; - en->projectile_fire_dir = fire_dir; - en->projectile_start_pos = start_pos; - en->projectile_speed = 5; - en->pos = start_pos; - en->damage = base_dmg + 5; - en->hitbox[0] = v2(0, 0); - en->hitbox[1] = v2(10, 0); - en->hitbox[2] = v2(0, 3); - en->hitbox[3] = v2(10, 3); -} - int entry(int argc, char **argv) { seed_for_random = rdtsc(); @@ -82,6 +37,7 @@ int entry(int argc, char **argv) { window.fullscreen = false; world = alloc(get_heap_allocator(), sizeof(World)); + memset(world, 0, sizeof(World)); sprite_load(STR("assets/player.png"), SPRITE_player); sprite_load(STR("assets/spider.png"), SPRITE_spider); @@ -89,6 +45,9 @@ int entry(int argc, char **argv) { sprite_load(STR("assets/starterbow.png"), SPRITE_starterbow); sprite_load(STR("assets/arrow.png"), SPRITE_arrow); + Gfx_Font* font = load_font_from_disk(STR("assets/m3x6.ttf"), get_heap_allocator()); + assert(font, "Couldn't load font"); + Entity* player = entity_create(); player_setup(player); player->primary_weapon = ARCH_startersword; @@ -105,6 +64,8 @@ int entry(int argc, char **argv) { float64 zoom = 5.3; Vector2 camera_pos = v2(0, 0); + float real_health_width = 0.0; + float64 last_time = os_get_elapsed_seconds(); while (!window.should_close) { float64 now_time = os_get_elapsed_seconds(); @@ -123,7 +84,7 @@ int entry(int argc, char **argv) { draw_frame.camera_xform = m4_scale(draw_frame.camera_xform, v3(1.0/zoom, 1.0/zoom, 1.0)); } - if (is_key_just_pressed('F')) { + if (is_key_just_pressed(KEY_F11)) { window.fullscreen = !window.fullscreen; } @@ -131,34 +92,11 @@ int entry(int argc, char **argv) { window.should_close = true; } - Vector2 move_axis = v2(0.0, 0.0); - if (is_key_down(KEY_ARROW_UP)) { - move_axis.y += 1.0; - } - if (is_key_down(KEY_ARROW_LEFT)) { - move_axis.x -= 1.0; - } - if (is_key_down(KEY_ARROW_DOWN)) { - move_axis.y -= 1.0; - } - if (is_key_down(KEY_ARROW_RIGHT)) { - move_axis.x += 1.0; - } - move_axis = v2_normalize(move_axis); - - player->pos = v2_add(player->pos, v2_mulf(move_axis, 75 * delta_time)); - player_weapon->weapon_owner_pos = player->pos; - if (move_axis.x != 0 || move_axis.y != 0) { - player_weapon->weapon_dir = move_axis; - } - - // weapon swapping - if (is_key_just_pressed('X')) { - swap(player->primary_weapon, player->secondary_weapon, Entity_Archetype); - entity_destroy(player_weapon); - player_weapon = entity_create(); - weapon_setup(player_weapon, player->primary_weapon); - player_weapon->pos = player->pos; + if (player->alive) { + player_process_input(player, player_weapon, delta_time); + } else { + // TODO add game over state + player_weapon->alive = false; } // :generic simulation (i.e. not a specific boss or player) @@ -166,9 +104,21 @@ int entry(int argc, char **argv) { if (en->is_animate && en->health <= 0) { en->alive = false; } + + if (en->is_hostile) { + en->enemy_cooldown_secs -= 1.0 * delta_time; + if (en->enemy_cooldown_secs <= 0) { + Vector2* player_hitbox = entity_resolve_hitbox(player); + Vector2* hitbox = entity_resolve_hitbox(en); + if (gjk(hitbox, 4, player_hitbox, 4)) { + entity_take_damage(player, en->damage); + en->enemy_cooldown_secs = 4 / en->enemy_speed; + } + } + } // TODO Finish dis - // if (en->is_item && is_key_just_pressed('C')) { + // if (en->is_item && is_key_just_pressed('E')) { // Vector2* player_hitbox = entity_resolve_hitbox(player); // Vector2* item_hitbox = entity_resolve_hitbox(en); @@ -179,7 +129,7 @@ int entry(int argc, char **argv) { if (en->is_weapon) { en->weapon_cooldown_secs -= 1.0 * delta_time; // decreases by 1 every second - if (is_key_just_pressed('Z') && en->weapon_cooldown_secs <= 0) { + if (is_key_down(MOUSE_BUTTON_LEFT) && en->weapon_cooldown_secs <= 0) { en->weapon_cooldown_secs = 1 / (en->weapon_speed); switch (en->weapon_class) { @@ -189,7 +139,7 @@ int entry(int argc, char **argv) { Vector2* other_hitbox = entity_resolve_hitbox(other_en); if (gjk(hitbox, 4, other_hitbox, 4)) { - other_en->health -= en->damage; + entity_take_damage(other_en, en->damage); } } } break; @@ -214,7 +164,7 @@ int entry(int argc, char **argv) { Vector2* other_hitbox = entity_resolve_hitbox(other_en); if (other_en->is_animate && gjk(hitbox, 4, other_hitbox, 4)) { - other_en->health -= en->damage; + entity_take_damage(other_en, en->damage); entity_destroy(en); } } @@ -228,7 +178,7 @@ int entry(int argc, char **argv) { // :entity rendering for (Entity* en = 0; entity_increment(&en);) { if (en->alive && en->renderable) { - Sprite* sprite = sprite_get(sprite_get_id_from_arch(en->arch)); + Sprite* sprite = sprite_get(entity_sprite_id_from_arch(en->arch)); Matrix4 xform = m4_scalar(1.0); if (en->is_projectile) { @@ -260,9 +210,71 @@ int entry(int argc, char **argv) { // :ui rendering { - // TODO Set projection - // TODO Weapon Slots - // TODO Health Bar + float width = window.width; + float height = window.height; + draw_frame.camera_xform = m4_scalar(1.0); + draw_frame.projection = m4_make_orthographic_projection(0.0, width, 0.0, height, -1, 10); + + float weapon_slot_size = 16.0 * zoom; + float weapon_slot_padding = 2.0 * zoom; + + // Primary Weapon Slot + { + Matrix4 xform = m4_scalar(1.0); + xform = m4_translate(xform, v3(weapon_slot_padding, height - weapon_slot_size - weapon_slot_padding, 0.0)); + draw_rect_xform(xform, v2(weapon_slot_size, weapon_slot_size), hex_to_rgba(0x639bffff)); + + Sprite* primary_weapon = sprite_get(entity_sprite_id_from_arch(player->primary_weapon)); + xform = m4_translate(xform, v3((weapon_slot_size - primary_weapon->size.x * zoom) / 2, (weapon_slot_size - primary_weapon->size.y * zoom) / 2, 0.0)); + xform = m4_scale(xform, v3(zoom, zoom, zoom)); + draw_image_xform(primary_weapon->image, xform, primary_weapon->size, COLOR_WHITE); + } + + + // Secondary Weapon Slot + { + Matrix4 xform = m4_scalar(1.0); + xform = m4_translate(xform, v3(weapon_slot_padding, height - 2*weapon_slot_size - 2*weapon_slot_padding, 0.0)); + draw_rect_xform(xform, v2(weapon_slot_size, weapon_slot_size), hex_to_rgba(0x3f3f74ff)); + + Sprite* secondary_weapon = sprite_get(entity_sprite_id_from_arch(player->secondary_weapon)); + xform = m4_translate(xform, v3((weapon_slot_size - secondary_weapon->size.x*zoom) / 2, (weapon_slot_size - secondary_weapon->size.y*zoom) / 2, 0.0)); + xform = m4_scale(xform, v3(zoom, zoom, zoom)); + draw_image_xform(secondary_weapon->image, xform, secondary_weapon->size, COLOR_WHITE); + } + + float health_bar_width = (player->max_health / 2) * zoom; + float health_bar_height = 8.0 * zoom; + float health_bar_padding = 4.0 * zoom; + Vector2 health_bar_pos = v2(weapon_slot_size + health_bar_padding, height - health_bar_height - weapon_slot_padding); + + // Health Bar + { + Matrix4 xform = m4_scalar(1.0); + xform = m4_translate(xform, v3(health_bar_pos.x, health_bar_pos.y, 0.0)); + draw_rect_xform(xform, v2(health_bar_width, health_bar_height), hex_to_rgba(0x7b343aff)); + + float target_width = (player->health / 2) * zoom; + animate_to_target_f32(&real_health_width, target_width, delta_time, 20.0); + draw_rect_xform(xform, v2(real_health_width, health_bar_height), hex_to_rgba(0xd95763ff)); + } + + // Health Text + { + string health_text; + if (player->health >= player->max_health) { + health_text = STR("full"); + } else if (player->health <= 0) { + health_text = STR("dead"); + } else { + health_text = tprint("%d", (int)player->health); + } + Gfx_Text_Metrics health_metrics = measure_text(font, health_text, 48, v2(1, 1)); + + Vector2 health_text_pos = v2(health_bar_pos.x + health_bar_width/2, health_bar_pos.y + health_bar_height/2); + Vector2 justified = v2_sub(health_text_pos, v2_divf(health_metrics.functional_size, 2)); + draw_text(font, health_text, 48, justified, v2(1, 1), COLOR_WHITE); + } } os_update(); diff --git a/player.c b/player.c new file mode 100644 index 0000000..1af5925 --- /dev/null +++ b/player.c @@ -0,0 +1,38 @@ +void player_setup(Entity* en) { + en->arch = ARCH_player; + en->renderable = true; + en->is_animate = true; + en->max_health = 100; + en->health = en->max_health; +} + +void player_process_input(Entity* player, Entity* player_weapon, float delta_time) { + Vector2 move_axis = v2(0.0, 0.0); + if (is_key_down('W')) { + move_axis.y += 1.0; + } + if (is_key_down('A')) { + move_axis.x -= 1.0; + } + if (is_key_down('S')) { + move_axis.y -= 1.0; + } + if (is_key_down('D')) { + move_axis.x += 1.0; + } + move_axis = v2_normalize(move_axis); + + player->pos = v2_add(player->pos, v2_mulf(move_axis, 75 * delta_time)); + player_weapon->weapon_owner_pos = player->pos; + Vector2 mouse_dir = v2_normalize(mouse_world_pos()); + player_weapon->weapon_dir = v2(mouse_dir.x, mouse_dir.y); + + // weapon swapping + if (is_key_just_pressed(MOUSE_BUTTON_RIGHT)) { + swap(player->primary_weapon, player->secondary_weapon, Entity_Archetype); + entity_destroy(player_weapon); + player_weapon = entity_create(); + weapon_setup(player_weapon, player->primary_weapon); + player_weapon->pos = player->pos; + } +} \ No newline at end of file diff --git a/sprite.c b/sprite.c index 98d7284..8ab48bf 100644 --- a/sprite.c +++ b/sprite.c @@ -28,17 +28,4 @@ Sprite* sprite_get(Sprite_ID id) { return &sprites[id]; } return &sprites[0]; -} - -Sprite_ID sprite_get_id_from_arch(Entity_Archetype arch) { - switch (arch) { - case ARCH_player: return SPRITE_player; break; - case ARCH_spider: return SPRITE_spider ; break; - case ARCH_mutant: return SPRITE_mutant ; break; - case ARCH_tree: return SPRITE_tree ; break; - case ARCH_startersword: return SPRITE_startersword ; break; - case ARCH_starterbow: return SPRITE_starterbow ; break; - case ARCH_arrow: return SPRITE_arrow ; break; - default: return 0; - } } \ No newline at end of file diff --git a/util.c b/util.c index edc225d..b05da29 100644 --- a/util.c +++ b/util.c @@ -15,4 +15,26 @@ bool animate_to_target_v2(Vector2* value, Vector2 target, float delta_time, floa bool result_x = animate_to_target_f32(&(value->x), target.x, delta_time, rate); bool result_y = animate_to_target_f32(&(value->y), target.y, delta_time, rate); return result_x && result_y; +} + +Vector2 screen_to_world(Vector2 screen) { + Matrix4 proj = draw_frame.projection; + Matrix4 view = draw_frame.camera_xform; + float window_w = window.width; + float window_h = window.height; + + // Normalize the mouse coordinates + float ndc_x = (screen.x / (window_w * 0.5f)) - 1.0f; + float ndc_y = (screen.y / (window_h * 0.5f)) - 1.0f; + + // Transform to world coordinates + Vector4 world_pos = v4(ndc_x, ndc_y, 0, 1); + world_pos = m4_transform(m4_inverse(proj), world_pos); + world_pos = m4_transform(view, world_pos); + + return world_pos.xy; +} + +Vector2 mouse_world_pos() { + return screen_to_world(v2(input_frame.mouse_x, input_frame.mouse_y)); } \ No newline at end of file diff --git a/weapon.c b/weapon.c new file mode 100644 index 0000000..9032a07 --- /dev/null +++ b/weapon.c @@ -0,0 +1,43 @@ +void weapon_setup(Entity* en, Entity_Archetype weapon_type) { + en->arch = weapon_type; + en->renderable = true; + en->is_weapon = true; + switch (weapon_type) { + case ARCH_startersword: { + en->weapon_class = WEAPON_melee; + en->damage = 10; + en->hitbox[0] = v2(0, 0); + en->hitbox[1] = v2(0, 13); + en->hitbox[2] = v2(13, 0); + en->hitbox[3] = v2(13, 13); + en->weapon_speed = 3; + } break; + + case ARCH_starterbow: { + en->weapon_class = WEAPON_ranged; + en->damage = 5; + en->weapon_speed = 3; + } break; + + default: { + assert(false, "Invalid weapon type"); + } break; + } +} + +void arrow_setup(Entity* en, Vector2 start_pos, Vector2 fire_dir, float32 base_dmg) { + en->arch = ARCH_arrow; + en->renderable = true; + en->is_projectile = true; + en->is_animate = false; // idk why this was on in the first place? + en->projectile_range = 200; + en->projectile_fire_dir = fire_dir; + en->projectile_start_pos = start_pos; + en->projectile_speed = 5; + en->pos = start_pos; + en->damage = base_dmg + 5; + en->hitbox[0] = v2(0, 0); + en->hitbox[1] = v2(10, 0); + en->hitbox[2] = v2(0, 3); + en->hitbox[3] = v2(10, 3); +} \ No newline at end of file