diff --git a/art/gen/lava.png b/art/gen/lava.png new file mode 100644 index 0000000..874681c Binary files /dev/null and b/art/gen/lava.png differ diff --git a/code/game/header/entity_view.h b/code/game/header/entity_view.h index d70850f..60dcc82 100644 --- a/code/game/header/entity_view.h +++ b/code/game/header/entity_view.h @@ -37,6 +37,9 @@ typedef struct entity_view { float tx; float ty; + float hp; + float max_hp; + // TODO(zaklaus): Find a way to stream dynamic arrays uint8_t blocks_used; uint8_t blocks[256]; diff --git a/code/game/header/game.h b/code/game/header/game.h index f892679..9aa9b31 100644 --- a/code/game/header/game.h +++ b/code/game/header/game.h @@ -14,6 +14,7 @@ void game_shutdown(); void game_request_close(); uint8_t game_is_running(); int8_t game_is_networked(); +float game_time(); //~ NOTE(zaklaus): game events void game_input(); diff --git a/code/game/header/world/blocks.h b/code/game/header/world/blocks.h index 6964068..fe7dafa 100644 --- a/code/game/header/world/blocks.h +++ b/code/game/header/world/blocks.h @@ -4,7 +4,8 @@ #define BLOCK_INVALID 0xF typedef enum { - BLOCK_FLAG_COLLISION = (1 << 1) + BLOCK_FLAG_COLLISION = (1 << 1), + BLOCK_FLAG_HAZARD = (1 << 2), } block_flags; #include "blocks_info.h" diff --git a/code/game/header/world/blocks_info.h b/code/game/header/world/blocks_info.h index 0f9a2e2..2daa543 100644 --- a/code/game/header/world/blocks_info.h +++ b/code/game/header/world/blocks_info.h @@ -5,6 +5,7 @@ typedef enum { BLOCK_KIND_GROUND, BLOCK_KIND_DIRT, BLOCK_KIND_WATER, + BLOCK_KIND_LAVA, BLOCK_KIND_WALL, BLOCK_KIND_HILL, BLOCK_KIND_HILL_SNOW, diff --git a/code/game/source/entity_view.c b/code/game/source/entity_view.c index c08a95a..6e28c4c 100644 --- a/code/game/source/entity_view.c +++ b/code/game/source/entity_view.c @@ -15,6 +15,8 @@ pkt_desc pkt_entity_view_desc[] = { { PKT_HALF(entity_view, vy) }, //{ PKT_SKIP_IF(entity_view, blocks_used, 0, 1) }, // NOTE(zaklaus): skip blocks for anything else { PKT_ARRAY(entity_view, blocks) }, + { PKT_HALF(entity_view, hp) }, + { PKT_HALF(entity_view, max_hp) }, { PKT_END }, }; diff --git a/code/game/source/game.c b/code/game/source/game.c index 8f9966d..902215c 100644 --- a/code/game/source/game.c +++ b/code/game/source/game.c @@ -104,6 +104,10 @@ void flecs_dash_init() { ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001}); } +float game_time() { + return zpl_time_rel(); +} + void game_init(game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) { game_mode = play_mode; platform_init(); diff --git a/code/game/source/gen/texgen.c b/code/game/source/gen/texgen.c index 54f8aba..1244a9a 100644 --- a/code/game/source/gen/texgen.c +++ b/code/game/source/gen/texgen.c @@ -34,6 +34,9 @@ Texture2D texgen_build_block(uint32_t biome, uint32_t kind) { case BLOCK_KIND_WATER:{ return LoadImageEco("water"); }break; + case BLOCK_KIND_LAVA:{ + return LoadImageEco("lava"); + }break; } } } diff --git a/code/game/source/platform_raylib.c b/code/game/source/platform_raylib.c index 994117b..b74dbbf 100644 --- a/code/game/source/platform_raylib.c +++ b/code/game/source/platform_raylib.c @@ -190,7 +190,7 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) { uint16_t font_size = (uint16_t)lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom); float font_spacing = 1.1f; float title_bg_offset = 4; - float fixed_title_offset = 2; + float fixed_title_offset = 8; switch (data->kind) { case EKIND_DEMO_NPC: { @@ -207,13 +207,15 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) { case EKIND_PLAYER: { float x = data->x; float y = data->y; + float health = (data->hp / data->max_hp); #if 1 const char *title = TextFormat("Player %d", key); int title_w = MeasureTextEco(title, font_size, font_spacing); DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(BLACK, data->tran_time)); + DrawRectangleEco(x-title_w/2-title_bg_offset/2, y-size-fixed_title_offset, title_w*health+title_bg_offset, font_size*0.2f, ColorAlpha(RED, data->tran_time)); DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(RAYWHITE, data->tran_time), font_spacing); #endif - DrawCircleEco(x, y, size, ColorAlpha(RED, data->tran_time)); + DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time)); }break; default:break; } diff --git a/code/game/source/player.c b/code/game/source/player.c index 12c81e2..e3b5675 100644 --- a/code/game/source/player.c +++ b/code/game/source/player.c @@ -10,6 +10,8 @@ #include "modules/systems.h" #include "zpl.h" +#define PLAYER_MAX_HP 100.0f + uint64_t player_spawn(char *name) { ecs_entity_t e = entity_spawn(NULL, EKIND_PLAYER); @@ -23,6 +25,7 @@ uint64_t player_spawn(char *name) { ecs_add(world_ecs(), e, EcsClient); ecs_set(world_ecs(), e, ClientInfo, {0}); ecs_set(world_ecs(), e, Input, {0}); + ecs_set(world_ecs(), e, Health, {.hp = PLAYER_MAX_HP, .max_hp = PLAYER_MAX_HP}); ecs_add(world_ecs(), e, Player); librg_entity_owner_set(world_tracker(), e, (int64_t)e); diff --git a/code/game/source/world/blocks_list.c b/code/game/source/world/blocks_list.c index 6bc356b..12ebb82 100644 --- a/code/game/source/world/blocks_list.c +++ b/code/game/source/world/blocks_list.c @@ -7,4 +7,5 @@ static block blocks[] = { {.name = "base-hill", .flags = BLOCK_FLAG_COLLISION, .kind = BLOCK_KIND_HILL, .biome = 0, .symbol = '^', .drag = 1.0f }, {.name = "base-hill-snow", .flags = BLOCK_FLAG_COLLISION, .kind = BLOCK_KIND_HILL_SNOW, .biome = 0, .symbol = '*', .drag = 1.0f }, {.name = "base-water", .flags = 0, .kind = BLOCK_KIND_WATER, .biome = 0, .symbol = '~', .drag = 0.11f }, + {.name = "base-lava", .flags = BLOCK_FLAG_HAZARD, .kind = BLOCK_KIND_LAVA, .biome = 0, .symbol = '!', .drag = 6.2f }, }; diff --git a/code/game/source/world/world.c b/code/game/source/world/world.c index 4952e7f..3ef96c7 100644 --- a/code/game/source/world/world.c +++ b/code/game/source/world/world.c @@ -14,30 +14,35 @@ static world_data world = {0}; static Components const *ecs_components; entity_view world_build_entity_view(int64_t e) { - ECS_IMPORT(world_ecs(), Components); + ECS_IMPORT(world.ecs, Components); entity_view view = {0}; - const Classify *classify = ecs_get(world_ecs(), e, Classify); + const Classify *classify = ecs_get(world.ecs, e, Classify); assert(classify); view.kind = classify->id; - const Position *pos = ecs_get(world_ecs(), e, Position); + const Position *pos = ecs_get(world.ecs, e, Position); if (pos) { view.x = pos->x; view.y = pos->y; } - const Velocity *vel = ecs_get(world_ecs(), e, Velocity); + const Velocity *vel = ecs_get(world.ecs, e, Velocity); if (vel) { view.flag |= EFLAG_INTERP; view.vx = vel->x; view.vy = vel->y; } + const Health *health = ecs_get(world.ecs, e, Health); + if (health) { + view.hp = health->hp; + view.max_hp = health->max_hp; + } - if (ecs_get(world_ecs(), e, Chunk)) { - Chunk *chpos = ecs_get_mut(world_ecs(), e, Chunk, 0); + if (ecs_get(world.ecs, e, Chunk)) { + Chunk *chpos = ecs_get_mut(world.ecs, e, Chunk, 0); view.x = chpos->x; view.y = chpos->y; view.blocks_used = 1; diff --git a/code/game/source/world/worldgen/worldgen_test.c b/code/game/source/world/worldgen/worldgen_test.c index f824eff..48f0453 100644 --- a/code/game/source/world/worldgen/worldgen_test.c +++ b/code/game/source/world/worldgen/worldgen_test.c @@ -130,6 +130,7 @@ int32_t worldgen_test(world_data *wld) { uint8_t grnd_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_GROUND); uint8_t dirt_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_DIRT); uint8_t watr_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER); + uint8_t lava_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_LAVA); srand(world->seed); @@ -147,6 +148,13 @@ int32_t worldgen_test(world_data *wld) { } #endif + // lava +#if 1 + for (int i=0; idim), RAND_RANGE(0, world->dim), 4+RAND_RANGE(0,3), 4+RAND_RANGE(0,3), 0.5f, 0.5f, shaper_noise80); + } +#endif + // hills #if 1 diff --git a/code/modules/modules/components.h b/code/modules/modules/components.h index 81b877c..5451806 100644 --- a/code/modules/modules/components.h +++ b/code/modules/modules/components.h @@ -39,6 +39,7 @@ ECS_STRUCT(Health, { // NOTE(zaklaus): Intentionally global, to allow for creative use of damage combos float pain_time; + float heal_time; }); ECS_STRUCT(Classify, { diff --git a/code/modules/source/systems.c b/code/modules/source/systems.c index cbc715d..2db17c4 100644 --- a/code/modules/source/systems.c +++ b/code/modules/source/systems.c @@ -4,6 +4,7 @@ #include "world/world.h" #include "world/blocks.h" #include "profiler.h" +#include "game.h" #define PHY_BLOCK_COLLISION 1 #define PHY_WALK_DRAG 0.12 @@ -168,6 +169,43 @@ void DemoPlaceIceBlock(ecs_iter_t *it) { } } +#define HAZARD_BLOCK_TIME 1.0f +#define HAZARD_BLOCK_DMG 5.0f + +void HurtOnHazardBlock(ecs_iter_t *it) { + Position *p = ecs_column(it, Position, 1); + Health *h = ecs_column(it, Health, 2); + + for (int i = 0; i < it->count; i++) { + world_block_lookup l = world_block_from_realpos(p[i].x, p[i].y); + if (blocks_get_flags(l.block_id) & BLOCK_FLAG_HAZARD) { + if (h->pain_time < game_time()) { + h->pain_time = game_time() + HAZARD_BLOCK_TIME; + h->hp -= HAZARD_BLOCK_DMG; + h->hp = zpl_max(0.0f, h->hp); + } + } + } +} + +#define HP_REGEN_TIME 2.0f +#define HP_REGEN_PAIN_COOLDOWN 5.0f +#define HP_REGEN_RECOVERY 15.0f + +void RegenerateHP(ecs_iter_t *it) { + Health *h = ecs_column(it, Health, 1); + + for (int i = 0; i < it->count; i++) { + if (h->pain_time < game_time() - HP_REGEN_PAIN_COOLDOWN) { + if (h->heal_time < game_time() && h->hp < h->max_hp) { + h->heal_time = game_time() + HP_REGEN_TIME; + h->hp += HP_REGEN_RECOVERY; + h->hp = zpl_min(h->max_hp, h->hp); + } + } + } +} + void SystemsImport(ecs_world_t *ecs) { ECS_MODULE(ecs, Systems); ECS_IMPORT(ecs, Components); @@ -177,6 +215,8 @@ void SystemsImport(ecs_world_t *ecs) { ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.EcsDemoNPC); ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, components.Position, components.Velocity); + ECS_SYSTEM(ecs, HurtOnHazardBlock, EcsOnUpdate, components.Position, components.Health); + ECS_SYSTEM(ecs, RegenerateHP, EcsOnUpdate, components.Health); ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, components.Position, components.Velocity); //ECS_SYSTEM(ecs, PushOutOverlappingEntities, EcsOnValidate, components.Position, Velocity);