diff --git a/code/foundation/src/gui/notifications.c b/code/foundation/src/gui/notifications.c index 93c1559..b04e5e6 100644 --- a/code/foundation/src/gui/notifications.c +++ b/code/foundation/src/gui/notifications.c @@ -1,23 +1,30 @@ #define MAX_NOTIFICATIONS_ON_SCREEN 5 typedef struct { - const char *title; - const char *text; + zpl_string title; + zpl_string text; } notification; static notification *notifications = 0; static bool show_notification_list = 0; -void notification_push(const char* title, const char* text) { +void notification_push(const char* title1, const char* text1) { if (!notifications) { zpl_array_init(notifications, zpl_heap()); } + zpl_string title = zpl_string_make(zpl_heap(), title1); + zpl_string text = zpl_string_make(zpl_heap(), text1); + zpl_array_append(notifications, ((notification) { title, text })); } void notification_clear(void) { + for (zpl_isize i = 0; i < zpl_array_count(notifications); i++) { + zpl_string_free(notifications[i].title); + zpl_string_free(notifications[i].text); + } zpl_array_clear(notifications); } @@ -53,6 +60,8 @@ void notification_draw(void) { nk_label_wrap(game_ui, notif->text); if (nk_button_label(game_ui, "OK")) { + zpl_string_free(notifications[i].title); + zpl_string_free(notifications[i].text); zpl_array_remove_at(notifications, i); } nk_tree_pop(game_ui); @@ -77,6 +86,8 @@ void notification_draw(void) { nk_label_wrap(game_ui, notif->text); if (nk_button_label(game_ui, "OK")) { + zpl_string_free(notifications[i].title); + zpl_string_free(notifications[i].text); zpl_array_remove_at(notifications, i); --i; } } diff --git a/code/foundation/src/models/components.h b/code/foundation/src/models/components.h index d7661cd..4e1cfeb 100644 --- a/code/foundation/src/models/components.h +++ b/code/foundation/src/models/components.h @@ -100,6 +100,8 @@ typedef struct { float max_hp; } Health; +typedef struct { char _unused; } Dead; + typedef struct { float amt; } HealthRegen; @@ -241,6 +243,7 @@ typedef struct { X(Input)\ X(ClientInfo)\ X(Health)\ + X(Dead)\ X(HealthRegen)\ X(HealDelay)\ X(HealthDecreased)\ diff --git a/code/foundation/src/packets/pkt_send_notif.c b/code/foundation/src/packets/pkt_send_notif.c new file mode 100644 index 0000000..f405106 --- /dev/null +++ b/code/foundation/src/packets/pkt_send_notif.c @@ -0,0 +1,35 @@ +#include "packets/pkt_send_notif.h" +#include "pkt/packet.h" +#include "world/world.h" +#include "core/game.h" +#include "world/entity_view.h" +#include "core/camera.h" +#include "models/prefabs/player.h" + +#include "models/components.h" +#include "systems/systems.h" + +// client +#include "gui/notifications.h" + +pkt_desc pkt_send_notification_desc[] = { + { PKT_ARRAY(pkt_send_notification, title) }, + { PKT_ARRAY(pkt_send_notification, text) }, + { PKT_END }, +}; + +size_t pkt_notification_send(uint64_t peer_id, uint16_t view_id, const char *title, const char *text) { + pkt_send_notification table = { 0 }; + zpl_strncpy(table.title, title, sizeof(table.title)); + zpl_strncpy(table.text, text, sizeof(table.text)); + return pkt_world_write(MSG_ID_SEND_NOTIFICATION, pkt_table_encode(pkt_send_notification_desc, PKT_STRUCT_PTR(&table)), 1, view_id, (void*)peer_id, 0); +} + +int32_t pkt_send_notification_handler(pkt_header *header) { + pkt_send_notification table; + PKT_IF(pkt_msg_decode(header, pkt_send_notification_desc, pkt_pack_desc_args(pkt_send_notification_desc), PKT_STRUCT_PTR(&table))); + + notification_push(table.title, table.text); + + return 0; +} diff --git a/code/foundation/src/packets/pkt_send_notif.h b/code/foundation/src/packets/pkt_send_notif.h new file mode 100644 index 0000000..3220084 --- /dev/null +++ b/code/foundation/src/packets/pkt_send_notif.h @@ -0,0 +1,14 @@ +#pragma once +#include "platform/system.h" +#include "pkt/packet_utils.h" + +typedef struct { + char title[64]; + char text[1024]; +} pkt_send_notification; + +size_t pkt_notification_send(uint64_t peer_id, uint16_t view_id, const char *title, const char *text); +extern pkt_desc pkt_send_notification_desc[]; + +PKT_HANDLER_PROC(pkt_send_notification_handler); + diff --git a/code/foundation/src/pkt/packet.c b/code/foundation/src/pkt/packet.c index 61533cb..9223b2b 100644 --- a/code/foundation/src/pkt/packet.c +++ b/code/foundation/src/pkt/packet.c @@ -8,6 +8,7 @@ #include "packets/pkt_01_welcome.h" #include "packets/pkt_send_keystate.h" #include "packets/pkt_send_librg_update.h" +#include "packets/pkt_send_notif.h" #include "packets/pkt_switch_viewer.h" #define PKT_HEADER_ELEMENTS 3 @@ -18,7 +19,8 @@ pkt_handler pkt_handlers[] = { {.id = MSG_ID_LIBRG_UPDATE, .handler = pkt_send_librg_update_handler}, {.id = MSG_ID_SEND_KEYSTATE, .handler = pkt_send_keystate_handler}, {.id = MSG_ID_SEND_BLOCKPOS, .handler = pkt_send_blockpos_handler}, - {.id = MSG_ID_SWITCH_VIEWER, .handler = pkt_switch_viewer_handler}, + {.id = MSG_ID_SWITCH_VIEWER, .handler = pkt_switch_viewer_handler}, + {.id = MSG_ID_SEND_NOTIFICATION, .handler = pkt_send_notification_handler}, }; uint8_t pkt_buffer[PKT_BUFSIZ]; diff --git a/code/foundation/src/pkt/packet.h b/code/foundation/src/pkt/packet.h index 5100875..956bada 100644 --- a/code/foundation/src/pkt/packet.h +++ b/code/foundation/src/pkt/packet.h @@ -9,7 +9,8 @@ typedef enum { MSG_ID_LIBRG_UPDATE, MSG_ID_SEND_KEYSTATE, MSG_ID_SEND_BLOCKPOS, - MSG_ID_SWITCH_VIEWER, + MSG_ID_SWITCH_VIEWER, + MSG_ID_SEND_NOTIFICATION, MSG_NEXT_FREE_ID, MAX_PACKETS = 256, } pkt_messages; diff --git a/code/foundation/src/systems/modules/system_health.c b/code/foundation/src/systems/modules/system_health.c new file mode 100644 index 0000000..2d3d5bc --- /dev/null +++ b/code/foundation/src/systems/modules/system_health.c @@ -0,0 +1,76 @@ +#define HAZARD_BLOCK_DMG 5.0f + +void HurtOnHazardBlock(ecs_iter_t *it) { + Position *p = ecs_field(it, Position, 1); + Health *h = ecs_field(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.bid) & BLOCK_FLAG_HAZARD) { + h->hp -= HAZARD_BLOCK_DMG; + h->hp = zpl_max(0.0f, h->hp); + ecs_add(it->world, it->entities[i], HealthDecreased); + } + } +} + +//#define HP_REGEN_PAIN_COOLDOWN 5.0f + +void RegenerateHP(ecs_iter_t *it) { + Health *h = ecs_field(it, Health, 1); + HealthRegen *r = ecs_field(it, HealthRegen, 2); + + for (int i = 0; i < it->count; i++) { + // TODO delay regen on hurt + if (h[i].hp < h[i].max_hp) { + h[i].hp += r->amt; + h[i].hp = zpl_min(h[i].max_hp, h[i].hp); + entity_wake(it->entities[i]); + } + } +} + +void OnHealthChangePutDelay(ecs_iter_t *it) { + for (int i = 0; i < it->count; i++) { + ecs_set(it->world, it->entities[i], HealDelay, { .delay = 10 }); + ecs_remove(it->world, it->entities[i], HealthDecreased); + } +} + +void OnHealthChangeCheckDead(ecs_iter_t *it) { + for (int i = 0; i < it->count; i++) { + const Health *hp = ecs_get(it->world, it->entities[i], Health); + + if (hp && hp->hp <= 0.0f) { + ecs_add(it->world, it->entities[i], Dead); + } + } +} + +void OnDead(ecs_iter_t *it) { + for (int i = 0; i < it->count; i++) { + const ClientInfo *ci = ecs_get(it->world, it->entities[i], ClientInfo); + Input *pi = ecs_get_mut_if_ex(it->world, it->entities[i], Input); + + if (ci) { + pkt_notification_send(0, 0, "Someone died!", zpl_bprintf("Player %d has died!", it->entities[i])); + } + + if (pi) { + pi->is_blocked = 1; + } + } +} + +void TickDownHealDelay(ecs_iter_t *it) { + HealDelay *h = ecs_field(it, HealDelay, 1); + + for (int i = 0; i < it->count; i++) { + --h[i].delay; + + if (h[i].delay == 0) { + ecs_remove(it->world, it->entities[i], HealDelay); + } + } +} + diff --git a/code/foundation/src/systems/systems.c b/code/foundation/src/systems/systems.c index bbcc563..12bd7cf 100644 --- a/code/foundation/src/systems/systems.c +++ b/code/foundation/src/systems/systems.c @@ -8,6 +8,8 @@ #include "core/game.h" #include "core/rules.h" +#include "packets/pkt_send_notif.h" + ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(0) #define CUTE_C2_IMPLEMENTATION #include "tinyc2.h" @@ -22,6 +24,7 @@ ecs_query_t *ecs_rigidbodies = 0; ecs_entity_t ecs_timer = 0; #include "modules/system_onfoot.c" +#include "modules/system_health.c" #include "modules/system_demo.c" #include "modules/system_vehicle.c" #include "modules/system_items.c" @@ -263,57 +266,6 @@ void IntegratePositions(ecs_iter_t *it) { } } -#define HAZARD_BLOCK_DMG 5.0f - -void HurtOnHazardBlock(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Health *h = ecs_field(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.bid) & BLOCK_FLAG_HAZARD) { - h->hp -= HAZARD_BLOCK_DMG; - h->hp = zpl_max(0.0f, h->hp); - ecs_add(it->world, it->entities[i], HealthDecreased); - } - } -} - -//#define HP_REGEN_PAIN_COOLDOWN 5.0f - -void RegenerateHP(ecs_iter_t *it) { - Health *h = ecs_field(it, Health, 1); - HealthRegen *r = ecs_field(it, HealthRegen, 2); - - for (int i = 0; i < it->count; i++) { - // TODO delay regen on hurt - if (h[i].hp < h[i].max_hp) { - h[i].hp += r->amt; - h[i].hp = zpl_min(h[i].max_hp, h[i].hp); - entity_wake(it->entities[i]); - } - } -} - -void OnHealthChangePutDelay(ecs_iter_t *it) { - for (int i = 0; i < it->count; i++) { - ecs_set(it->world, it->entities[i], HealDelay, { .delay = 10 }); - ecs_remove(it->world, it->entities[i], HealthDecreased); - } -} - -void TickDownHealDelay(ecs_iter_t *it) { - HealDelay *h = ecs_field(it, HealDelay, 1); - - for (int i = 0; i < it->count; i++) { - --h[i].delay; - - if (h[i].delay == 0) { - ecs_remove(it->world, it->entities[i], HealDelay); - } - } -} - void ResetActivators(ecs_iter_t *it) { Input *in = ecs_field(it, Input, 1); @@ -399,25 +351,29 @@ void SystemsImport(ecs_world_t *ecs) { ecs_rigidbodies = ecs_query_new(ecs, "components.Position, components.Velocity, components.PhysicsBody"); ECS_SYSTEM(ecs, EnableWorldEdit, EcsOnLoad); - ECS_SYSTEM(ecs, MovementImpulse, EcsOnLoad, components.Input, components.Velocity, components.Position, !components.IsInVehicle); - ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.DemoNPC); - ECS_SYSTEM(ecs, ApplyWorldDragOnVelocity, EcsOnUpdate, components.Position, components.Velocity); + // health ECS_SYSTEM_TICKED_EX(ecs, HurtOnHazardBlock, EcsOnUpdate, 20.0f, components.Position, components.Health); ECS_SYSTEM_TICKED_EX(ecs, RegenerateHP, EcsOnUpdate, 40.0f, components.Health, components.HealthRegen, !components.HealDelay); ECS_SYSTEM_TICKED_EX(ecs, TickDownHealDelay, EcsOnUpdate, 20.0f, components.HealDelay); - ECS_SYSTEM(ecs, VehicleHandling, EcsOnUpdate, components.Vehicle, components.Position, components.Velocity); - ECS_OBSERVER(ecs, OnHealthChangePutDelay, EcsOnAdd, components.HealthDecreased); + ECS_OBSERVER(ecs, OnHealthChangeCheckDead, EcsOnAdd, components.HealthDecreased); + ECS_OBSERVER(ecs, OnDead, EcsOnAdd, components.Dead); + // collisions and movement physics + ECS_SYSTEM(ecs, ApplyWorldDragOnVelocity, EcsOnUpdate, components.Position, components.Velocity); + ECS_SYSTEM(ecs, VehicleHandling, EcsOnUpdate, components.Vehicle, components.Position, components.Velocity); ECS_SYSTEM(ecs, BodyCollisions, EcsOnUpdate, components.Position, components.Velocity, components.PhysicsBody); ECS_SYSTEM(ecs, BlockCollisions, EcsOnValidate, components.Position, components.Velocity); ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, components.Position, components.Velocity); + // vehicles ECS_SYSTEM(ecs, EnterVehicle, EcsPostUpdate, components.Input, components.Position, !components.IsInVehicle); ECS_SYSTEM(ecs, LeaveVehicle, EcsPostUpdate, components.Input, components.IsInVehicle, components.Velocity); - ECS_SYSTEM(ecs, PlayerClosestInteractable, EcsPostUpdate, components.Input); + // player interaction + ECS_SYSTEM(ecs, MovementImpulse, EcsOnLoad, components.Input, components.Velocity, components.Position, !components.IsInVehicle); + ECS_SYSTEM(ecs, PlayerClosestInteractable, EcsPostUpdate, components.Input); ECS_SYSTEM(ecs, PickItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle); ECS_SYSTEM(ecs, DropItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle); ECS_SYSTEM(ecs, SwapItems, EcsPostUpdate, components.Input, components.Inventory); @@ -426,18 +382,22 @@ void SystemsImport(ecs_world_t *ecs) { ECS_SYSTEM(ecs, CraftItem, EcsPostUpdate, components.Input, !components.IsInVehicle); ECS_SYSTEM(ecs, InspectContainers, EcsPostUpdate, components.Input, !components.IsInVehicle); + // logistics and production ECS_SYSTEM_TICKED(ecs, HarvestIntoContainers, EcsPostUpdate, components.ItemContainer, components.Position, !components.BlockHarvest); ECS_SYSTEM_TICKED(ecs, ProduceItems, EcsPostUpdate, components.ItemContainer, components.Producer, components.Position, components.Device); ECS_SYSTEM_TICKED_EX(ecs, PushItemsOnNodes, EcsPostUpdate, 20, components.ItemContainer, components.Position, components.Device, components.ItemRouter); ECS_SYSTEM_TICKED(ecs, BuildBlueprints, EcsPostUpdate, components.Blueprint, components.Device, components.Position); + // demo creature sim ECS_SYSTEM_TICKED(ecs, CreatureCheckNeeds, EcsPostUpdate, components.Creature); ECS_SYSTEM_TICKED(ecs, CreatureSeekFood, EcsPostUpdate, components.Creature, components.Position, components.Velocity, components.SeeksFood, !components.SeeksCompanion); ECS_SYSTEM_TICKED(ecs, CreatureSeekCompanion, EcsPostUpdate, components.Creature, components.Position, components.Velocity, components.SeeksCompanion, !components.SeeksFood); ECS_SYSTEM(ecs, CreatureRoamAround, EcsPostUpdate, components.Velocity, components.Creature, !components.SeeksFood, !components.SeeksCompanion); + // player input reset ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input); + // cleanup systems ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle); ECS_SYSTEM(ecs, ThrowItemsOut, EcsUnSet, components.ItemContainer, components.Position); diff --git a/code/games/minimal/src/platform.c b/code/games/minimal/src/platform.c index 5d06551..6e06546 100644 --- a/code/games/minimal/src/platform.c +++ b/code/games/minimal/src/platform.c @@ -102,6 +102,7 @@ void platform_render() { } debug_draw(); + notification_draw(); game_draw_ui(); } EndDrawing(); diff --git a/code/games/survival/src/platform.c b/code/games/survival/src/platform.c index b17265a..6970200 100644 --- a/code/games/survival/src/platform.c +++ b/code/games/survival/src/platform.c @@ -98,17 +98,6 @@ void platform_render() { game_world_view_active_entity_map(do_entity_fadeinout); } - // HACK run once when player is dead - { - static char done = 0; - camera cam = camera_get(); - entity_view *e = game_world_view_active_get_entity(cam.ent_id); - if (e && e->hp <= 0.0f && !done) { - done = 1; - notification_push("DEAD", "YOU ARE DEAD!"); - } - } - assets_frame(); BeginDrawing();