send notification from server

efd/v1
Dominik Madarász 2023-02-02 14:17:53 +01:00
parent 65766d8748
commit 11549eb324
10 changed files with 165 additions and 73 deletions

View File

@ -1,23 +1,30 @@
#define MAX_NOTIFICATIONS_ON_SCREEN 5 #define MAX_NOTIFICATIONS_ON_SCREEN 5
typedef struct { typedef struct {
const char *title; zpl_string title;
const char *text; zpl_string text;
} notification; } notification;
static notification *notifications = 0; static notification *notifications = 0;
static bool show_notification_list = 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) { if (!notifications) {
zpl_array_init(notifications, zpl_heap()); 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 })); zpl_array_append(notifications, ((notification) { title, text }));
} }
void notification_clear(void) { 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); zpl_array_clear(notifications);
} }
@ -53,6 +60,8 @@ void notification_draw(void) {
nk_label_wrap(game_ui, notif->text); nk_label_wrap(game_ui, notif->text);
if (nk_button_label(game_ui, "OK")) { 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); zpl_array_remove_at(notifications, i);
} }
nk_tree_pop(game_ui); nk_tree_pop(game_ui);
@ -77,6 +86,8 @@ void notification_draw(void) {
nk_label_wrap(game_ui, notif->text); nk_label_wrap(game_ui, notif->text);
if (nk_button_label(game_ui, "OK")) { 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; zpl_array_remove_at(notifications, i); --i;
} }
} }

View File

@ -100,6 +100,8 @@ typedef struct {
float max_hp; float max_hp;
} Health; } Health;
typedef struct { char _unused; } Dead;
typedef struct { typedef struct {
float amt; float amt;
} HealthRegen; } HealthRegen;
@ -241,6 +243,7 @@ typedef struct {
X(Input)\ X(Input)\
X(ClientInfo)\ X(ClientInfo)\
X(Health)\ X(Health)\
X(Dead)\
X(HealthRegen)\ X(HealthRegen)\
X(HealDelay)\ X(HealDelay)\
X(HealthDecreased)\ X(HealthDecreased)\

View File

@ -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;
}

View File

@ -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);

View File

@ -8,6 +8,7 @@
#include "packets/pkt_01_welcome.h" #include "packets/pkt_01_welcome.h"
#include "packets/pkt_send_keystate.h" #include "packets/pkt_send_keystate.h"
#include "packets/pkt_send_librg_update.h" #include "packets/pkt_send_librg_update.h"
#include "packets/pkt_send_notif.h"
#include "packets/pkt_switch_viewer.h" #include "packets/pkt_switch_viewer.h"
#define PKT_HEADER_ELEMENTS 3 #define PKT_HEADER_ELEMENTS 3
@ -19,6 +20,7 @@ pkt_handler pkt_handlers[] = {
{.id = MSG_ID_SEND_KEYSTATE, .handler = pkt_send_keystate_handler}, {.id = MSG_ID_SEND_KEYSTATE, .handler = pkt_send_keystate_handler},
{.id = MSG_ID_SEND_BLOCKPOS, .handler = pkt_send_blockpos_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]; uint8_t pkt_buffer[PKT_BUFSIZ];

View File

@ -10,6 +10,7 @@ typedef enum {
MSG_ID_SEND_KEYSTATE, MSG_ID_SEND_KEYSTATE,
MSG_ID_SEND_BLOCKPOS, MSG_ID_SEND_BLOCKPOS,
MSG_ID_SWITCH_VIEWER, MSG_ID_SWITCH_VIEWER,
MSG_ID_SEND_NOTIFICATION,
MSG_NEXT_FREE_ID, MSG_NEXT_FREE_ID,
MAX_PACKETS = 256, MAX_PACKETS = 256,
} pkt_messages; } pkt_messages;

View File

@ -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);
}
}
}

View File

@ -8,6 +8,8 @@
#include "core/game.h" #include "core/game.h"
#include "core/rules.h" #include "core/rules.h"
#include "packets/pkt_send_notif.h"
ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(0) ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(0)
#define CUTE_C2_IMPLEMENTATION #define CUTE_C2_IMPLEMENTATION
#include "tinyc2.h" #include "tinyc2.h"
@ -22,6 +24,7 @@ ecs_query_t *ecs_rigidbodies = 0;
ecs_entity_t ecs_timer = 0; ecs_entity_t ecs_timer = 0;
#include "modules/system_onfoot.c" #include "modules/system_onfoot.c"
#include "modules/system_health.c"
#include "modules/system_demo.c" #include "modules/system_demo.c"
#include "modules/system_vehicle.c" #include "modules/system_vehicle.c"
#include "modules/system_items.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) { void ResetActivators(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1); Input *in = ecs_field(it, Input, 1);
@ -399,24 +351,28 @@ void SystemsImport(ecs_world_t *ecs) {
ecs_rigidbodies = ecs_query_new(ecs, "components.Position, components.Velocity, components.PhysicsBody"); ecs_rigidbodies = ecs_query_new(ecs, "components.Position, components.Velocity, components.PhysicsBody");
ECS_SYSTEM(ecs, EnableWorldEdit, EcsOnLoad); 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, 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, RegenerateHP, EcsOnUpdate, 40.0f, components.Health, components.HealthRegen, !components.HealDelay);
ECS_SYSTEM_TICKED_EX(ecs, TickDownHealDelay, EcsOnUpdate, 20.0f, 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, 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, BodyCollisions, EcsOnUpdate, components.Position, components.Velocity, components.PhysicsBody);
ECS_SYSTEM(ecs, BlockCollisions, EcsOnValidate, components.Position, components.Velocity); ECS_SYSTEM(ecs, BlockCollisions, EcsOnValidate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, IntegratePositions, 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, EnterVehicle, EcsPostUpdate, components.Input, components.Position, !components.IsInVehicle);
ECS_SYSTEM(ecs, LeaveVehicle, EcsPostUpdate, components.Input, components.IsInVehicle, components.Velocity); ECS_SYSTEM(ecs, LeaveVehicle, EcsPostUpdate, components.Input, components.IsInVehicle, components.Velocity);
// 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, PlayerClosestInteractable, EcsPostUpdate, components.Input);
ECS_SYSTEM(ecs, PickItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle); 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, DropItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
@ -426,18 +382,22 @@ void SystemsImport(ecs_world_t *ecs) {
ECS_SYSTEM(ecs, CraftItem, EcsPostUpdate, components.Input, !components.IsInVehicle); ECS_SYSTEM(ecs, CraftItem, EcsPostUpdate, components.Input, !components.IsInVehicle);
ECS_SYSTEM(ecs, InspectContainers, 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, 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(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_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); 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, CreatureCheckNeeds, EcsPostUpdate, components.Creature);
ECS_SYSTEM_TICKED(ecs, CreatureSeekFood, EcsPostUpdate, components.Creature, components.Position, components.Velocity, components.SeeksFood, !components.SeeksCompanion); 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_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); ECS_SYSTEM(ecs, CreatureRoamAround, EcsPostUpdate, components.Velocity, components.Creature, !components.SeeksFood, !components.SeeksCompanion);
// player input reset
ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input); ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input);
// cleanup systems
ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle); ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle);
ECS_SYSTEM(ecs, ThrowItemsOut, EcsUnSet, components.ItemContainer, components.Position); ECS_SYSTEM(ecs, ThrowItemsOut, EcsUnSet, components.ItemContainer, components.Position);

View File

@ -102,6 +102,7 @@ void platform_render() {
} }
debug_draw(); debug_draw();
notification_draw();
game_draw_ui(); game_draw_ui();
} }
EndDrawing(); EndDrawing();

View File

@ -98,17 +98,6 @@ void platform_render() {
game_world_view_active_entity_map(do_entity_fadeinout); 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(); assets_frame();
BeginDrawing(); BeginDrawing();