Basic Weapon System
This commit is contained in:
parent
9a838cd105
commit
702a64d0e3
6 changed files with 182 additions and 73 deletions
BIN
assets/arrow.png
Normal file
BIN
assets/arrow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 110 B |
BIN
assets/starterbow.png
Normal file
BIN
assets/starterbow.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 166 B |
55
entity.c
55
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;
|
||||
}
|
196
entry_helpless.c
196
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
2
gjk.c
2
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
|
||||
|
|
2
sprite.c
2
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];
|
||||
|
|
Reference in a new issue