This repository has been archived on 2025-02-04. You can view files and clone it, but cannot push or open issues or pull requests.
helpless/entry_helpless.c

275 lines
No EOL
8.1 KiB
C

#include "util.c"
#include "entity.c"
#include "sprite.c"
#include "gjk.c"
void player_setup(Entity* en) {
en->arch = ARCH_player;
en->renderable = true;
en->is_animate = true;
en->health = 100;
}
void spider_setup(Entity* en) {
en->arch = ARCH_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);
en->hitbox[1] = v2(0, 7);
en->hitbox[2] = v2(20, 0);
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();
// This is how we (optionally) configure the window.
// You can set this at any point in the runtime and it will
// be applied in os_update().
// If you don't care, you can ignore all of this as it all
// has reasonable default values.
window.title = STR("Helpless");
window.scaled_width = 1280; // We need to set the scaled size if we want to handle system scaling (DPI)
window.scaled_height = 720;
window.clear_color = hex_to_rgba(0x222034ff);
window.allow_resize = false;
window.fullscreen = false;
world = alloc(get_heap_allocator(), sizeof(World));
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* player_weapon = entity_create();
weapon_setup(player_weapon, player->primary_weapon);
for (int i = 0; i < 10; i++) {
Entity* spider = entity_create();
spider_setup(spider);
}
float64 zoom = 5.3;
Vector2 camera_pos = v2(0, 0);
float64 last_time = os_get_elapsed_seconds();
while (!window.should_close) {
float64 now_time = os_get_elapsed_seconds();
float64 delta_time = now_time - last_time;
last_time = now_time;
draw_frame.projection = m4_make_orthographic_projection(window.width * -0.5, window.width * 0.5, window.height * -0.5, window.height * 0.5, -1, 10);
// :camera
{
Vector2 target_pos = player->pos;
animate_to_target_v2(&camera_pos, target_pos, delta_time, 7.5f);
draw_frame.camera_xform = m4_identity();
draw_frame.camera_xform = m4_translate(draw_frame.camera_xform, v3(camera_pos.x, camera_pos.y, 0.0));
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')) {
window.fullscreen = !window.fullscreen;
}
if (is_key_just_pressed(KEY_ESCAPE)) {
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;
}
// :generic simulation (i.e. not a specific boss or player)
for (Entity* en = 0; entity_increment(&en);) {
if (en->is_animate && en->health <= 0) {
en->alive = false;
}
// TODO Finish dis
// if (en->is_item && is_key_just_pressed('C')) {
// Vector2* player_hitbox = entity_resolve_hitbox(player);
// Vector2* item_hitbox = entity_resolve_hitbox(en);
// if (gjk(player_hitbox, 4, item_hitbox, 4)) {
// entity_destroy(en);
// }
// }
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);
if (gjk(hitbox, 4, other_hitbox, 4)) {
other_en->health -= en->damage;
}
}
} break;
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) {
en->pos = v2_add(en->pos, v2_mulf(en->projectile_fire_dir, en->projectile_speed * 50.0 * delta_time));
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);
}
}
} else {
entity_destroy(en);
}
}
}
// :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));
Matrix4 xform = m4_scalar(1.0);
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);
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);
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);
}
}
// :ui rendering
{
// TODO Set projection
// TODO Weapon Slots
// TODO Health Bar
}
os_update();
gfx_update();
reset_temporary_storage();
}
return 0;
}