From 702a64d0e3f20cd84a58fa0ea90668ef9490e31f Mon Sep 17 00:00:00 2001 From: Abdulmujeeb Raji Date: Thu, 29 Aug 2024 17:49:37 +0100 Subject: [PATCH] Basic Weapon System --- assets/arrow.png | Bin 0 -> 110 bytes assets/starterbow.png | Bin 0 -> 166 bytes entity.c | 55 +++++++++++- entry_helpless.c | 196 +++++++++++++++++++++++++++--------------- gjk.c | 2 +- sprite.c | 2 + 6 files changed, 182 insertions(+), 73 deletions(-) create mode 100644 assets/arrow.png create mode 100644 assets/starterbow.png diff --git a/assets/arrow.png b/assets/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..d126381341135dd99d4fa046c06fd01d96bc8c92 GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^AT~1x8<1ROJFgT-F%}28J29*~C-V}>(eQL}4B?oW z{Nw-s|MhG(3EytL{Vyp21c_YgEw>+poDq&Ye86E%Cxcg&@R$E_Ywdt~7(8A5T-G@y GGywoB;3B&K literal 0 HcmV?d00001 diff --git a/assets/starterbow.png b/assets/starterbow.png new file mode 100644 index 0000000000000000000000000000000000000000..90b37daf1da1e16acaea2c2801d5d2bcfa3504dc GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}37#&FAsQ2p zUO32mz<`7Kz>e&V?A0Yg-(JYgJ^eRSaM6SKzdl70tPhWEeYz)4M^V9(;r-*<3l=RZ zt3MYc-}5+D+5P{+M195uhm&{lYfE2!=D*^Bp=7)C?PKdAn?$E29^(&A_qq_165?&t;ucLK6T`bUmm5 literal 0 HcmV?d00001 diff --git a/entity.c b/entity.c index e5a90e9..fb32daf 100644 --- a/entity.c +++ b/entity.c @@ -1,10 +1,19 @@ +typedef enum Weapon_Class { + WEAPON_nil = 0, + WEAPON_melee = 1, + WEAPON_ranged = 2, + WEAPON_magic = 3, +} Weapon_Class; + typedef enum Entity_Archetype { ARCH_nil = 0, ARCH_player = 1, ARCH_spider = 2, ARCH_mutant = 3, ARCH_tree = 4, - ARCH_weapon = 5, + ARCH_startersword = 5, + ARCH_starterbow = 6, + ARCH_arrow = 7, } Entity_Archetype; typedef struct Entity { @@ -13,11 +22,25 @@ typedef struct Entity { Entity_Archetype arch; Vector2 pos; Sprite_ID sprite_id; + bool is_animate; float32 health, damage; Vector2 hitbox[4]; + Entity_Archetype primary_weapon; + Entity_Archetype secondary_weapon; + bool is_weapon; + Weapon_Class weapon_class; Vector2 weapon_owner_pos; Vector2 weapon_dir; float32 weapon_rads; + float32 weapon_speed; + float32 weapon_cooldown_secs; + bool is_item; + Vector2 item_inv_size; + bool is_projectile; + float32 projectile_range; + Vector2 projectile_fire_dir; + Vector2 projectile_start_pos; + float32 projectile_speed; } Entity; #define MAX_ENTITIES 1024 @@ -42,4 +65,34 @@ Entity* entity_create() { void entity_destroy(Entity* entity) { memset(entity, 0, sizeof(Entity)); +} + +bool entity_increment(Entity **entity_ptr) +{ + Entity *entity = *entity_ptr; + int start_index = 0; + if(entity != 0) + { + start_index = (entity - world->entities) + 1; + } + entity = 0; + for(int idx = start_index; idx < MAX_ENTITIES; idx += 1) + { + entity = &world->entities[idx]; + if(entity->alive) + { + break; + } + } + *entity_ptr = entity; + return !!entity; +} + +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); + return hitbox; } \ No newline at end of file diff --git a/entry_helpless.c b/entry_helpless.c index d5c1c15..8a6cebc 100644 --- a/entry_helpless.c +++ b/entry_helpless.c @@ -7,6 +7,7 @@ void player_setup(Entity* en) { en->arch = ARCH_player; en->sprite_id = SPRITE_player; en->renderable = true; + en->is_animate = true; en->health = 100; } @@ -14,6 +15,7 @@ void spider_setup(Entity* en) { en->arch = ARCH_spider; en->sprite_id = SPRITE_spider; en->renderable = true; + en->is_animate = true; en->health = 30; en->pos = v2(get_random_float32_in_range(-200, 200), get_random_float32_in_range(-200, 200)); en->hitbox[0] = v2(0, 0); @@ -22,15 +24,50 @@ void spider_setup(Entity* en) { en->hitbox[3] = v2(20, 7); } -void startersword_setup(Entity* en) { - en->arch = ARCH_weapon; - en->sprite_id = SPRITE_startersword; +void weapon_setup(Entity* en, Entity_Archetype weapon_type) { + en->arch = weapon_type; en->renderable = true; - en->damage = 10; + en->is_weapon = true; + switch (weapon_type) { + case ARCH_startersword: { + en->sprite_id = SPRITE_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->sprite_id = SPRITE_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->sprite_id = SPRITE_arrow; + en->renderable = true; + en->is_projectile = true; + 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(0, 13); - en->hitbox[2] = v2(13, 0); - en->hitbox[3] = v2(13, 13); + en->hitbox[1] = v2(10, 0); + en->hitbox[2] = v2(0, 3); + en->hitbox[3] = v2(10, 3); } int entry(int argc, char **argv) { @@ -53,12 +90,16 @@ int entry(int argc, char **argv) { sprite_load(STR("assets/player.png"), SPRITE_player); sprite_load(STR("assets/spider.png"), SPRITE_spider); sprite_load(STR("assets/startersword.png"), SPRITE_startersword); + sprite_load(STR("assets/starterbow.png"), SPRITE_starterbow); + sprite_load(STR("assets/arrow.png"), SPRITE_arrow); Entity* player = entity_create(); player_setup(player); + player->primary_weapon = ARCH_startersword; + player->secondary_weapon = ARCH_starterbow; - Entity* startersword = entity_create(); - startersword_setup(startersword); + Entity* player_weapon = entity_create(); + weapon_setup(player_weapon, player->primary_weapon); for (int i = 0; i < 10; i++) { Entity* spider = entity_create(); @@ -110,91 +151,104 @@ int entry(int argc, char **argv) { move_axis = v2_normalize(move_axis); player->pos = v2_add(player->pos, v2_mulf(move_axis, 75 * delta_time)); - startersword->weapon_owner_pos = player->pos; + player_weapon->weapon_owner_pos = player->pos; if (move_axis.x != 0 || move_axis.y != 0) { - startersword->weapon_dir = move_axis; + 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; } // :generic simulation (i.e. not a specific boss or player) - for (int i = 0; i < MAX_ENTITIES; i++) { - Entity* en = &world->entities[i]; - if (en->alive) { - switch (en->arch) { - case ARCH_weapon: { - if (is_key_just_pressed('Z')) { - for (int i = 0; i < MAX_ENTITIES; i++) { - Entity* other_en = &world->entities[i]; + for (Entity* en = 0; entity_increment(&en);) { + if (en->is_animate && en->health <= 0) { + en->alive = false; + } - Vector2 hitbox[4] = { - v2_add(en->hitbox[0], en->pos), - v2_add(en->hitbox[1], en->pos), - v2_add(en->hitbox[2], en->pos), - v2_add(en->hitbox[3], en->pos), - }; + 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) { + en->weapon_cooldown_secs = 1 / (en->weapon_speed); + switch (en->weapon_class) { + case WEAPON_melee: { + Vector2* hitbox = entity_resolve_hitbox(en); + for (Entity* other_en = 0; entity_increment(&other_en);) { + Vector2* other_hitbox = entity_resolve_hitbox(other_en); - Vector2 other_hitbox[4] = { - v2_add(other_en->hitbox[0], other_en->pos), - v2_add(other_en->hitbox[1], other_en->pos), - v2_add(other_en->hitbox[2], other_en->pos), - v2_add(other_en->hitbox[3], other_en->pos), - }; - - if (other_en->alive && gjk(hitbox, 4, other_hitbox, 4)) { + if (gjk(hitbox, 4, other_hitbox, 4)) { other_en->health -= en->damage; } } - } - } break; + } break; - default: { - if (en->health <= 0) { - en->alive = false; + case WEAPON_ranged: { + // TODO add other ammo, also add an actual limit once inventory is done + Entity* arrow = entity_create(); + arrow_setup(arrow, en->pos, en->weapon_dir, en->damage); + } break; + + default: break; + } + } + } + + if (en->is_projectile) { + if (en->pos.x < en->projectile_start_pos.x + en->projectile_range && + en->pos.y < en->projectile_start_pos.y + en->projectile_range) { + Vector2* hitbox = entity_resolve_hitbox(en); + for (Entity* other_en = 0; entity_increment(&other_en);) { + 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_destroy(en); } - } break; + } + + en->pos = v2_add(en->pos, v2_mulf(en->projectile_fire_dir, en->projectile_speed * 50.0 * delta_time)); + } else { + entity_destroy(en); } } } // :rendering // TODO add a layer system, so that you place render instructions anywhere - for (int i = 0; i < MAX_ENTITIES; i++) { - Entity* en = &world->entities[i]; + for (Entity* en = 0; entity_increment(&en);) { if (en->alive && en->renderable) { - switch (en->arch) { - case ARCH_weapon: { - Sprite* sprite = sprite_get(en->sprite_id); + Sprite* sprite = sprite_get(en->sprite_id); + Matrix4 xform = m4_scalar(1.0); - // move the weapon away from its owner - Vector2 dist_from_owner = v2_mulf(sprite->size, 1.25f); - dist_from_owner.x = max(20, dist_from_owner.x); - dist_from_owner.y = max(20, dist_from_owner.y); + if (en->is_projectile) { + // face where the projectile is going + xform = m4_translate(xform, v3(en->pos.x, en->pos.y, 0)); + xform = m4_rotate_z(xform, atan2(en->projectile_fire_dir.x, en->projectile_fire_dir.y) - 90 * RAD_PER_DEG); + } else if (en->is_weapon) { + // move the weapon away from its owner + Vector2 dist_from_owner = v2_mulf(sprite->size, 1.25f); + dist_from_owner.x = max(20, dist_from_owner.x); + dist_from_owner.y = max(20, dist_from_owner.y); - Vector2 target_pos = v2_add(en->weapon_owner_pos, v2_mul(en->weapon_dir, dist_from_owner)); - animate_to_target_v2(&(en->pos), target_pos, delta_time, 30.0f); + Vector2 target_pos = v2_add(en->weapon_owner_pos, v2_mul(en->weapon_dir, dist_from_owner)); + animate_to_target_v2(&(en->pos), target_pos, delta_time, 30.0f); - float32 target_rads = atan2(en->weapon_dir.x, en->weapon_dir.y) - 45 * RAD_PER_DEG; - animate_to_target_f32(&(en->weapon_rads), target_rads, delta_time, 15.0f); + float32 target_rads = atan2(en->weapon_dir.x, en->weapon_dir.y) - 45 * RAD_PER_DEG; + animate_to_target_f32(&(en->weapon_rads), target_rads, delta_time, 15.0f); - Matrix4 xform = m4_scalar(1.0); - xform = m4_translate(xform, v3(en->pos.x, en->pos.y, 0)); - xform = m4_rotate_z(xform, en->weapon_rads); - - xform = m4_translate(xform, v3(sprite->size.x * -0.5, sprite->size.y * -0.5, 0)); - draw_image_xform(sprite->image, xform, sprite->size, COLOR_WHITE); - - } break; - - default: { - Sprite* sprite = sprite_get(en->sprite_id); - - Matrix4 xform = m4_scalar(1.0); - xform = m4_translate(xform, v3(en->pos.x, en->pos.y, 0)); - - xform = m4_translate(xform, v3(sprite->size.x * -0.5, sprite->size.y * -0.5, 0)); - draw_image_xform(sprite->image, xform, sprite->size, COLOR_WHITE); - - } break; + xform = m4_translate(xform, v3(en->pos.x, en->pos.y, 0)); + xform = m4_rotate_z(xform, en->weapon_rads); + } else { + xform = m4_translate(xform, v3(en->pos.x, en->pos.y, 0)); } + + xform = m4_translate(xform, v3(sprite->size.x * -0.5, sprite->size.y * -0.5, 0)); + draw_image_xform(sprite->image, xform, sprite->size, COLOR_WHITE); } } diff --git a/gjk.c b/gjk.c index 4f140bc..c4dc06d 100644 --- a/gjk.c +++ b/gjk.c @@ -1,4 +1,4 @@ -// Created by Igor Kroitor on 29/12/15. +// Created by Igor Kroitor on 29/12/15, Modifiied by Abdulmujeeb Raji on 29/08/2024 //----------------------------------------------------------------------------- // Gilbert-Johnson-Keerthi (GJK) collision detection algorithm in 2D diff --git a/sprite.c b/sprite.c index 18cfbe6..2b0365d 100644 --- a/sprite.c +++ b/sprite.c @@ -9,6 +9,8 @@ typedef enum Sprite_ID { SPRITE_mutant, SPRITE_tree, SPRITE_startersword, + SPRITE_starterbow, + SPRITE_arrow, SPRITE_MAX, } Sprite_ID; Sprite sprites[SPRITE_MAX];