game: health, pain and regen
parent
5212aedf92
commit
2370bd4ea0
Binary file not shown.
After Width: | Height: | Size: 115 KiB |
|
@ -37,6 +37,9 @@ typedef struct entity_view {
|
||||||
float tx;
|
float tx;
|
||||||
float ty;
|
float ty;
|
||||||
|
|
||||||
|
float hp;
|
||||||
|
float max_hp;
|
||||||
|
|
||||||
// TODO(zaklaus): Find a way to stream dynamic arrays
|
// TODO(zaklaus): Find a way to stream dynamic arrays
|
||||||
uint8_t blocks_used;
|
uint8_t blocks_used;
|
||||||
uint8_t blocks[256];
|
uint8_t blocks[256];
|
||||||
|
|
|
@ -14,6 +14,7 @@ void game_shutdown();
|
||||||
void game_request_close();
|
void game_request_close();
|
||||||
uint8_t game_is_running();
|
uint8_t game_is_running();
|
||||||
int8_t game_is_networked();
|
int8_t game_is_networked();
|
||||||
|
float game_time();
|
||||||
|
|
||||||
//~ NOTE(zaklaus): game events
|
//~ NOTE(zaklaus): game events
|
||||||
void game_input();
|
void game_input();
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
#define BLOCK_INVALID 0xF
|
#define BLOCK_INVALID 0xF
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BLOCK_FLAG_COLLISION = (1 << 1)
|
BLOCK_FLAG_COLLISION = (1 << 1),
|
||||||
|
BLOCK_FLAG_HAZARD = (1 << 2),
|
||||||
} block_flags;
|
} block_flags;
|
||||||
|
|
||||||
#include "blocks_info.h"
|
#include "blocks_info.h"
|
||||||
|
|
|
@ -5,6 +5,7 @@ typedef enum {
|
||||||
BLOCK_KIND_GROUND,
|
BLOCK_KIND_GROUND,
|
||||||
BLOCK_KIND_DIRT,
|
BLOCK_KIND_DIRT,
|
||||||
BLOCK_KIND_WATER,
|
BLOCK_KIND_WATER,
|
||||||
|
BLOCK_KIND_LAVA,
|
||||||
BLOCK_KIND_WALL,
|
BLOCK_KIND_WALL,
|
||||||
BLOCK_KIND_HILL,
|
BLOCK_KIND_HILL,
|
||||||
BLOCK_KIND_HILL_SNOW,
|
BLOCK_KIND_HILL_SNOW,
|
||||||
|
|
|
@ -15,6 +15,8 @@ pkt_desc pkt_entity_view_desc[] = {
|
||||||
{ PKT_HALF(entity_view, vy) },
|
{ PKT_HALF(entity_view, vy) },
|
||||||
//{ PKT_SKIP_IF(entity_view, blocks_used, 0, 1) }, // NOTE(zaklaus): skip blocks for anything else
|
//{ PKT_SKIP_IF(entity_view, blocks_used, 0, 1) }, // NOTE(zaklaus): skip blocks for anything else
|
||||||
{ PKT_ARRAY(entity_view, blocks) },
|
{ PKT_ARRAY(entity_view, blocks) },
|
||||||
|
{ PKT_HALF(entity_view, hp) },
|
||||||
|
{ PKT_HALF(entity_view, max_hp) },
|
||||||
{ PKT_END },
|
{ PKT_END },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,10 @@ void flecs_dash_init() {
|
||||||
ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001});
|
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) {
|
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;
|
game_mode = play_mode;
|
||||||
platform_init();
|
platform_init();
|
||||||
|
|
|
@ -34,6 +34,9 @@ Texture2D texgen_build_block(uint32_t biome, uint32_t kind) {
|
||||||
case BLOCK_KIND_WATER:{
|
case BLOCK_KIND_WATER:{
|
||||||
return LoadImageEco("water");
|
return LoadImageEco("water");
|
||||||
}break;
|
}break;
|
||||||
|
case BLOCK_KIND_LAVA:{
|
||||||
|
return LoadImageEco("lava");
|
||||||
|
}break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
uint16_t font_size = (uint16_t)lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom);
|
||||||
float font_spacing = 1.1f;
|
float font_spacing = 1.1f;
|
||||||
float title_bg_offset = 4;
|
float title_bg_offset = 4;
|
||||||
float fixed_title_offset = 2;
|
float fixed_title_offset = 8;
|
||||||
|
|
||||||
switch (data->kind) {
|
switch (data->kind) {
|
||||||
case EKIND_DEMO_NPC: {
|
case EKIND_DEMO_NPC: {
|
||||||
|
@ -207,13 +207,15 @@ void DEBUG_draw_entities(uint64_t key, entity_view * data) {
|
||||||
case EKIND_PLAYER: {
|
case EKIND_PLAYER: {
|
||||||
float x = data->x;
|
float x = data->x;
|
||||||
float y = data->y;
|
float y = data->y;
|
||||||
|
float health = (data->hp / data->max_hp);
|
||||||
#if 1
|
#if 1
|
||||||
const char *title = TextFormat("Player %d", key);
|
const char *title = TextFormat("Player %d", key);
|
||||||
int title_w = MeasureTextEco(title, font_size, font_spacing);
|
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-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);
|
DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(RAYWHITE, data->tran_time), font_spacing);
|
||||||
#endif
|
#endif
|
||||||
DrawCircleEco(x, y, size, ColorAlpha(RED, data->tran_time));
|
DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time));
|
||||||
}break;
|
}break;
|
||||||
default:break;
|
default:break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include "modules/systems.h"
|
#include "modules/systems.h"
|
||||||
#include "zpl.h"
|
#include "zpl.h"
|
||||||
|
|
||||||
|
#define PLAYER_MAX_HP 100.0f
|
||||||
|
|
||||||
uint64_t player_spawn(char *name) {
|
uint64_t player_spawn(char *name) {
|
||||||
ecs_entity_t e = entity_spawn(NULL, EKIND_PLAYER);
|
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_add(world_ecs(), e, EcsClient);
|
||||||
ecs_set(world_ecs(), e, ClientInfo, {0});
|
ecs_set(world_ecs(), e, ClientInfo, {0});
|
||||||
ecs_set(world_ecs(), e, Input, {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);
|
ecs_add(world_ecs(), e, Player);
|
||||||
|
|
||||||
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
|
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
|
||||||
|
|
|
@ -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", .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-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-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 },
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,30 +14,35 @@ static world_data world = {0};
|
||||||
static Components const *ecs_components;
|
static Components const *ecs_components;
|
||||||
|
|
||||||
entity_view world_build_entity_view(int64_t e) {
|
entity_view world_build_entity_view(int64_t e) {
|
||||||
ECS_IMPORT(world_ecs(), Components);
|
ECS_IMPORT(world.ecs, Components);
|
||||||
entity_view view = {0};
|
entity_view view = {0};
|
||||||
|
|
||||||
const Classify *classify = ecs_get(world_ecs(), e, Classify);
|
const Classify *classify = ecs_get(world.ecs, e, Classify);
|
||||||
assert(classify);
|
assert(classify);
|
||||||
|
|
||||||
view.kind = classify->id;
|
view.kind = classify->id;
|
||||||
|
|
||||||
const Position *pos = ecs_get(world_ecs(), e, Position);
|
const Position *pos = ecs_get(world.ecs, e, Position);
|
||||||
if (pos) {
|
if (pos) {
|
||||||
view.x = pos->x;
|
view.x = pos->x;
|
||||||
view.y = pos->y;
|
view.y = pos->y;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Velocity *vel = ecs_get(world_ecs(), e, Velocity);
|
const Velocity *vel = ecs_get(world.ecs, e, Velocity);
|
||||||
if (vel) {
|
if (vel) {
|
||||||
view.flag |= EFLAG_INTERP;
|
view.flag |= EFLAG_INTERP;
|
||||||
view.vx = vel->x;
|
view.vx = vel->x;
|
||||||
view.vy = vel->y;
|
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)) {
|
if (ecs_get(world.ecs, e, Chunk)) {
|
||||||
Chunk *chpos = ecs_get_mut(world_ecs(), e, Chunk, 0);
|
Chunk *chpos = ecs_get_mut(world.ecs, e, Chunk, 0);
|
||||||
view.x = chpos->x;
|
view.x = chpos->x;
|
||||||
view.y = chpos->y;
|
view.y = chpos->y;
|
||||||
view.blocks_used = 1;
|
view.blocks_used = 1;
|
||||||
|
|
|
@ -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 grnd_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_GROUND);
|
||||||
uint8_t dirt_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_DIRT);
|
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 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);
|
srand(world->seed);
|
||||||
|
|
||||||
|
@ -147,6 +148,13 @@ int32_t worldgen_test(world_data *wld) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// lava
|
||||||
|
#if 1
|
||||||
|
for (int i=0; i<RAND_RANGE(48, 62); i++) {
|
||||||
|
world_fill_rect_anchor(lava_id, RAND_RANGE(0, world->dim), RAND_RANGE(0, world->dim), 4+RAND_RANGE(0,3), 4+RAND_RANGE(0,3), 0.5f, 0.5f, shaper_noise80);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// hills
|
// hills
|
||||||
#if 1
|
#if 1
|
||||||
|
|
|
@ -39,6 +39,7 @@ ECS_STRUCT(Health, {
|
||||||
|
|
||||||
// NOTE(zaklaus): Intentionally global, to allow for creative use of damage combos
|
// NOTE(zaklaus): Intentionally global, to allow for creative use of damage combos
|
||||||
float pain_time;
|
float pain_time;
|
||||||
|
float heal_time;
|
||||||
});
|
});
|
||||||
|
|
||||||
ECS_STRUCT(Classify, {
|
ECS_STRUCT(Classify, {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "world/world.h"
|
#include "world/world.h"
|
||||||
#include "world/blocks.h"
|
#include "world/blocks.h"
|
||||||
#include "profiler.h"
|
#include "profiler.h"
|
||||||
|
#include "game.h"
|
||||||
|
|
||||||
#define PHY_BLOCK_COLLISION 1
|
#define PHY_BLOCK_COLLISION 1
|
||||||
#define PHY_WALK_DRAG 0.12
|
#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) {
|
void SystemsImport(ecs_world_t *ecs) {
|
||||||
ECS_MODULE(ecs, Systems);
|
ECS_MODULE(ecs, Systems);
|
||||||
ECS_IMPORT(ecs, Components);
|
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, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.EcsDemoNPC);
|
||||||
|
|
||||||
ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, components.Position, components.Velocity);
|
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, IntegratePositions, EcsOnValidate, components.Position, components.Velocity);
|
||||||
//ECS_SYSTEM(ecs, PushOutOverlappingEntities, EcsOnValidate, components.Position, Velocity);
|
//ECS_SYSTEM(ecs, PushOutOverlappingEntities, EcsOnValidate, components.Position, Velocity);
|
||||||
|
|
Loading…
Reference in New Issue