efd/v1
DavoSK 2023-02-02 15:43:56 +01:00
commit 755f7e7a86
16 changed files with 206 additions and 74 deletions

View File

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

View File

@ -100,6 +100,8 @@ typedef struct {
float max_hp;
} Health;
typedef struct { char _unused; } Dead;
typedef struct {
float amt;
} HealthRegen;
@ -140,6 +142,11 @@ typedef struct {
float durability; // 1.0 - 0.0 (0.0 = broken), we can only ever merge items of the same durability
} Item;
typedef struct {
int spritesheet;
int frame;
} Sprite;
typedef struct {
char _unused;
} BlockHarvest;
@ -247,6 +254,7 @@ typedef struct {
X(Input)\
X(ClientInfo)\
X(Health)\
X(Dead)\
X(HealthRegen)\
X(HealDelay)\
X(HealthDecreased)\
@ -270,6 +278,7 @@ typedef struct {
X(Creature)\
X(SeeksFood)\
X(SeeksCompanion)\
X(Sprite)\
X(StreamInfo)
#define X(comp) extern ECS_COMPONENT_DECLARE(comp);

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_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];

View File

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

View File

@ -0,0 +1,75 @@
#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++) {
TICK_VAR(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/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);

View File

@ -44,8 +44,13 @@ pkt_desc pkt_entity_view_desc[] = {
{ PKT_UINT(entity_view, asset) },
{ PKT_UINT(entity_view, progress_active) },
{ PKT_UINT(entity_view, is_producer) },
{ PKT_HALF(entity_view, progress_value) },
{ PKT_KEEP_IF(entity_view, kind, EKIND_SPRITE, 2) },
{ PKT_UINT(entity_view, spritesheet) },
{ PKT_UINT(entity_view, frame) },
{ PKT_KEEP_IF(entity_view, has_items, true, 3) },
{ PKT_UINT(entity_view, has_items) },
{ PKT_UINT(entity_view, selected_item) },

View File

@ -13,6 +13,8 @@
X(EKIND_PLAYER)\
X(EKIND_ITEM)\
X(EKIND_DEVICE)\
X(EKIND_SPRITE)\
X(EKIND_WEAPON)\
X(EKIND_VEHICLE)\
X(EKIND_DEMO_NPC)\
X(EKIND_MONSTER)\
@ -78,6 +80,10 @@ typedef struct entity_view {
bool is_producer;
uint32_t progress_active;
float progress_value;
// sprite index
int spritesheet;
int frame;
// NOTE(zaklaus): inventory
uint8_t has_items;

View File

@ -76,6 +76,12 @@ entity_view* world_build_entity_view(int64_t e) {
view.is_producer = ecs_get(world_ecs(), e, Producer) != 0;
}
if (ecs_get(world_ecs(), e, Sprite)) {
Sprite const* spr = ecs_get(world_ecs(), e, Sprite);
view.spritesheet = spr->spritesheet;
view.frame = spr->frame;
}
view.inside_vehicle = ecs_get(world_ecs(), e, IsInVehicle) != 0 ? true : false;
Inventory* inv = 0;

View File

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

View File

@ -69,6 +69,12 @@ void renderer_draw_entry(uint64_t key, entity_view *data, game_world_render_entr
Color color = data->veh_kind == 0 ? RED : data->veh_kind == 1 ? GREEN : BLUE;
DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, zpl_to_degrees(data->heading), ColorAlpha(color, data->tran_time));
}break;
case EKIND_SPRITE:
case EKIND_WEAPON: {
float x = data->x - 32.f;
float y = data->y - 32.f;
DrawTexturePro(GetSpriteTexture2D(assets_find(data->asset)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
} break;
case EKIND_DEVICE:{
float x = data->x - 32.f;
float y = data->y - 32.f;

View File

@ -72,6 +72,12 @@ void renderer_draw_entry(uint64_t key, entity_view *data, game_world_render_entr
Color color = data->veh_kind == 0 ? RED : data->veh_kind == 1 ? GREEN : BLUE;
DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, zpl_to_degrees(data->heading), ColorAlpha(color, data->tran_time));
}break;
case EKIND_SPRITE:
case EKIND_WEAPON: {
float x = data->x - 32.f;
float y = data->y - 32.f;
DrawTexturePro(GetSpriteTexture2D(assets_find(data->asset)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
} break;
case EKIND_DEVICE:{
Texture2D tex = GetSpriteTexture2D(assets_find(data->asset));
float x = data->x - tex.width/2;

View File

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

View File

@ -57,6 +57,12 @@ void renderer_draw_entry(uint64_t key, entity_view *data, game_world_render_entr
Color color = data->veh_kind == 0 ? RED : data->veh_kind == 1 ? GREEN : BLUE;
DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, zpl_to_degrees(data->heading), ColorAlpha(color, data->tran_time));
}break;
case EKIND_SPRITE:
case EKIND_WEAPON: {
float x = data->x;
float y = data->y;
DrawSpriteEco(&main_sprite_sheet, data->frame, x, y, 0.0f, 2.0f, WHITE);
} break;
case EKIND_DEVICE:{
float x = data->x - 32.f;
float y = data->y - 32.f;