Merge branch 'master' of github.com:zpl-c/eco2d
commit
a7c060660e
|
@ -1,39 +1,39 @@
|
|||
#pragma once
|
||||
#include "system.h"
|
||||
|
||||
#define ASSET_INVALID 0xFF
|
||||
|
||||
typedef enum {
|
||||
// NOTE(zaklaus): Debug
|
||||
ASSET_DEBUG_TILE,
|
||||
|
||||
// NOTE(zaklaus): entities
|
||||
ASSET_PLAYER,
|
||||
ASSET_THING,
|
||||
|
||||
// NOTE(zaklaus): items
|
||||
ASSET_DEMO_ICEMAKER,
|
||||
|
||||
MAX_ASSETS,
|
||||
FORCE_ASSET_UINT16 = UINT16_MAX
|
||||
} asset_id;
|
||||
|
||||
typedef enum {
|
||||
AKIND_TEXTURE,
|
||||
AKIND_SOUND,
|
||||
|
||||
FORCE_AKIND_UINT8 = UINT8_MAX
|
||||
} asset_kind;
|
||||
|
||||
int32_t assets_setup(void);
|
||||
void assets_destroy(void);
|
||||
|
||||
uint16_t assets_find(asset_id id);
|
||||
|
||||
asset_kind assets_get_kind(uint16_t id);
|
||||
void *assets_get_snd(uint16_t id);
|
||||
void *assets_get_tex(uint16_t id);
|
||||
|
||||
// NOTE(zaklaus): client only
|
||||
#define ASSET_SRC_RECT() ((Rectangle){0, 0, 64, 64})
|
||||
#define ASSET_DST_RECT(x,y) ((Rectangle){x, y, 64, 64})
|
||||
#pragma once
|
||||
#include "system.h"
|
||||
|
||||
#define ASSET_INVALID 0xFF
|
||||
|
||||
typedef enum {
|
||||
// NOTE(zaklaus): Debug
|
||||
ASSET_DEBUG_TILE,
|
||||
|
||||
// NOTE(zaklaus): entities
|
||||
ASSET_PLAYER,
|
||||
ASSET_THING,
|
||||
|
||||
// NOTE(zaklaus): items
|
||||
ASSET_DEMO_ICEMAKER,
|
||||
|
||||
MAX_ASSETS,
|
||||
FORCE_ASSET_UINT16 = UINT16_MAX
|
||||
} asset_id;
|
||||
|
||||
typedef enum {
|
||||
AKIND_TEXTURE,
|
||||
AKIND_SOUND,
|
||||
|
||||
FORCE_AKIND_UINT8 = UINT8_MAX
|
||||
} asset_kind;
|
||||
|
||||
int32_t assets_setup(void);
|
||||
void assets_destroy(void);
|
||||
|
||||
uint16_t assets_find(asset_id id);
|
||||
|
||||
asset_kind assets_get_kind(uint16_t id);
|
||||
void *assets_get_snd(uint16_t id);
|
||||
void *assets_get_tex(uint16_t id);
|
||||
|
||||
// NOTE(zaklaus): client only
|
||||
#define ASSET_SRC_RECT() ((Rectangle){0, 0, 64, 64})
|
||||
#define ASSET_DST_RECT(x,y) ((Rectangle){x, y, 64, 64})
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include "assets.h"
|
||||
|
||||
static asset assets[] = {
|
||||
{
|
||||
.id = ASSET_DEBUG_TILE,
|
||||
.kind = AKIND_TEXTURE,
|
||||
},
|
||||
{
|
||||
.id = ASSET_DEMO_ICEMAKER,
|
||||
.kind = AKIND_TEXTURE,
|
||||
}
|
||||
};
|
||||
#include "assets.h"
|
||||
|
||||
static asset assets[] = {
|
||||
{
|
||||
.id = ASSET_DEBUG_TILE,
|
||||
.kind = AKIND_TEXTURE,
|
||||
},
|
||||
{
|
||||
.id = ASSET_DEMO_ICEMAKER,
|
||||
.kind = AKIND_TEXTURE,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
#include "zpl.h"
|
||||
#include "camera.h"
|
||||
#include "platform.h"
|
||||
#include "entity_view.h"
|
||||
#include "game.h"
|
||||
|
||||
#define CAMERA_LERP_FACTOR 11.2f
|
||||
|
||||
static camera main_camera;
|
||||
|
||||
void camera_reset(void) {
|
||||
zpl_zero_item(&main_camera);
|
||||
main_camera.mode = CAMERA_MODE_STATIONARY;
|
||||
main_camera.first_time = true;
|
||||
}
|
||||
|
||||
void camera_update(void) {
|
||||
switch (main_camera.mode) {
|
||||
case CAMERA_MODE_FOLLOW: {
|
||||
world_view *world = game_world_view_get_active();
|
||||
if (!world) break;
|
||||
entity_view *view = entity_view_get(&world->entities, main_camera.ent_id);
|
||||
if (!view) break;
|
||||
float smooth_ms = zpl_clamp((float)platform_frametime(), 0.0f, 1.0f);
|
||||
|
||||
main_camera.x = zpl_lerp(main_camera.x, view->x, CAMERA_LERP_FACTOR*smooth_ms);
|
||||
main_camera.y = zpl_lerp(main_camera.y, view->y, CAMERA_LERP_FACTOR*smooth_ms);
|
||||
|
||||
if (main_camera.first_time) {
|
||||
main_camera.first_time = false;
|
||||
main_camera.x = view->x;
|
||||
main_camera.y = view->y;
|
||||
}
|
||||
}break;
|
||||
|
||||
default: {
|
||||
|
||||
}break;
|
||||
}
|
||||
}
|
||||
void camera_set_follow(uint64_t ent_id) {
|
||||
main_camera.mode = CAMERA_MODE_FOLLOW;
|
||||
main_camera.ent_id = ent_id;
|
||||
}
|
||||
void camera_set_pos(double x, double y) {
|
||||
main_camera.mode = CAMERA_MODE_STATIONARY;
|
||||
main_camera.x = x;
|
||||
main_camera.y = y;
|
||||
}
|
||||
camera camera_get(void) {
|
||||
return main_camera;
|
||||
}
|
||||
#include "zpl.h"
|
||||
#include "camera.h"
|
||||
#include "platform.h"
|
||||
#include "entity_view.h"
|
||||
#include "game.h"
|
||||
|
||||
#define CAMERA_LERP_FACTOR 11.2f
|
||||
|
||||
static camera main_camera;
|
||||
|
||||
void camera_reset(void) {
|
||||
zpl_zero_item(&main_camera);
|
||||
main_camera.mode = CAMERA_MODE_STATIONARY;
|
||||
main_camera.first_time = true;
|
||||
}
|
||||
|
||||
void camera_update(void) {
|
||||
switch (main_camera.mode) {
|
||||
case CAMERA_MODE_FOLLOW: {
|
||||
world_view *world = game_world_view_get_active();
|
||||
if (!world) break;
|
||||
entity_view *view = entity_view_get(&world->entities, main_camera.ent_id);
|
||||
if (!view) break;
|
||||
float smooth_ms = zpl_clamp((float)platform_frametime(), 0.0f, 1.0f);
|
||||
|
||||
main_camera.x = zpl_lerp(main_camera.x, view->x, CAMERA_LERP_FACTOR*smooth_ms);
|
||||
main_camera.y = zpl_lerp(main_camera.y, view->y, CAMERA_LERP_FACTOR*smooth_ms);
|
||||
|
||||
if (main_camera.first_time) {
|
||||
main_camera.first_time = false;
|
||||
main_camera.x = view->x;
|
||||
main_camera.y = view->y;
|
||||
}
|
||||
}break;
|
||||
|
||||
default: {
|
||||
|
||||
}break;
|
||||
}
|
||||
}
|
||||
void camera_set_follow(uint64_t ent_id) {
|
||||
main_camera.mode = CAMERA_MODE_FOLLOW;
|
||||
main_camera.ent_id = ent_id;
|
||||
}
|
||||
void camera_set_pos(double x, double y) {
|
||||
main_camera.mode = CAMERA_MODE_STATIONARY;
|
||||
main_camera.x = x;
|
||||
main_camera.y = y;
|
||||
}
|
||||
camera camera_get(void) {
|
||||
return main_camera;
|
||||
}
|
||||
|
|
|
@ -1,409 +1,409 @@
|
|||
#include "debug_ui.h"
|
||||
#include "debug_draw.h"
|
||||
#include "raylib.h"
|
||||
#include "vehicle.h"
|
||||
#include "camera.h"
|
||||
#include "world/world.h"
|
||||
#include "game.h"
|
||||
#include "sfd.h"
|
||||
|
||||
#include "modules/components.h"
|
||||
|
||||
typedef enum {
|
||||
DITEM_RAW,
|
||||
DITEM_GAP,
|
||||
DITEM_TEXT,
|
||||
DITEM_BUTTON,
|
||||
DITEM_SLIDER,
|
||||
DITEM_LIST,
|
||||
DITEM_COND,
|
||||
DITEM_END,
|
||||
|
||||
DITEM_FORCE_UINT8 = UINT8_MAX
|
||||
} debug_kind;
|
||||
|
||||
typedef struct {
|
||||
float x, y;
|
||||
} debug_draw_result;
|
||||
|
||||
#define DBG_FONT_SIZE 22
|
||||
#define DBG_FONT_SPACING DBG_FONT_SIZE * 1.2f
|
||||
#define DBG_START_XPOS 15
|
||||
#define DBG_START_YPOS 200
|
||||
#define DBG_LIST_XPOS_OFFSET 10
|
||||
#define DBG_SHADOW_OFFSET_XPOS 1
|
||||
#define DBG_SHADOW_OFFSET_YPOS 1
|
||||
#define DBG_CTRL_HANDLE_DIM 10
|
||||
#define DBG_GAP_HEIGHT DBG_FONT_SPACING * 0.5f
|
||||
|
||||
static uint8_t is_shadow_rendered;
|
||||
static uint8_t is_debug_open = 1;
|
||||
static uint8_t is_handle_ctrl_held;
|
||||
static float debug_xpos = DBG_START_XPOS;
|
||||
static float debug_ypos = DBG_START_YPOS;
|
||||
|
||||
typedef struct debug_item {
|
||||
debug_kind kind;
|
||||
char const *name;
|
||||
float name_width;
|
||||
uint8_t skip;
|
||||
|
||||
union {
|
||||
union {
|
||||
char const *text;
|
||||
uint64_t val;
|
||||
};
|
||||
|
||||
struct {
|
||||
struct debug_item *items;
|
||||
uint8_t is_collapsed;
|
||||
uint8_t is_sp_only;
|
||||
} list;
|
||||
|
||||
struct {
|
||||
float val, min, max;
|
||||
void (*on_change)(float);
|
||||
} slider;
|
||||
|
||||
void (*on_click)(void);
|
||||
|
||||
uint8_t (*on_success)(void);
|
||||
};
|
||||
|
||||
debug_draw_result (*proc)(struct debug_item*, float, float);
|
||||
} debug_item;
|
||||
|
||||
static void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color);
|
||||
static int UIMeasureText(const char *text, int fontSize);
|
||||
|
||||
#include "debug_replay.c"
|
||||
|
||||
#include "debug_ui_actions.c"
|
||||
#include "debug_ui_widgets.c"
|
||||
|
||||
static debug_item items[] = {
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "general",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime },
|
||||
{ .kind = DITEM_TEXT, .name = "pos", .proc = DrawCameraPos },
|
||||
{ .kind = DITEM_TEXT, .name = "zoom", .proc = DrawZoom },
|
||||
{ .kind = DITEM_END },
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "world simulation",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_COND, .on_success = CondIsWorldRunning },
|
||||
{ .kind = DITEM_BUTTON, .name = "pause", .on_click = ActWorldToggleSim },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondIsWorldPaused, .skip = 6 },
|
||||
{ .kind = DITEM_BUTTON, .name = "resume", .on_click = ActWorldToggleSim },
|
||||
|
||||
{ .kind = DITEM_GAP },
|
||||
|
||||
{ .kind = DITEM_TEXT, .name = "step size", .proc = DrawWorldStepSize },
|
||||
{ .kind = DITEM_BUTTON, .name = "single-step", .on_click = ActWorldStep },
|
||||
{ .kind = DITEM_BUTTON, .name = "increment step size", .on_click = ActWorldIncrementSimStepSize },
|
||||
{ .kind = DITEM_BUTTON, .name = "decrement step size", .on_click = ActWorldDecrementSimStepSize },
|
||||
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_sp_only = true,
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "debug actions",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_BUTTON, .name = "spawn car", .on_click = ActSpawnCar },
|
||||
{ .kind = DITEM_BUTTON, .name = "place ice rink", .on_click = ActPlaceIceRink },
|
||||
{ .kind = DITEM_BUTTON, .name = "erase world changes", .on_click = ActEraseWorldChanges },
|
||||
{ .kind = DITEM_BUTTON, .name = "spawn circling driver", .on_click = ActSpawnCirclingDriver },
|
||||
{ .kind = DITEM_BUTTON, .name = "spawn icemaker item", .on_click = ActSpawnIcemaker },
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "demo npcs",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_TEXT, .name = "npcs", .proc = DrawDemoNPCCount },
|
||||
{ .kind = DITEM_BUTTON, .name = "spawn 1000 npcs", .on_click = ActSpawnDemoNPCs },
|
||||
{ .kind = DITEM_BUTTON, .name = "remove all demo npcs", .on_click = ActDespawnDemoNPCs },
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_collapsed = true
|
||||
}
|
||||
},
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_sp_only = true,
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "replay system",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_TEXT, .name = "macro", .proc = DrawReplayFileName },
|
||||
{ .kind = DITEM_TEXT, .name = "samples", .proc = DrawReplaySamples },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayDataPresentAndNotPlaying },
|
||||
{ .kind = DITEM_BUTTON, .name = "replay", .on_click = ActReplayRun },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayStatusOff },
|
||||
{ .kind = DITEM_BUTTON, .name = "record", .on_click = ActReplayBegin },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayStatusOn },
|
||||
{ .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayIsPlaying },
|
||||
{ .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayIsNotPlayingOrRecordsNotClear },
|
||||
{ .kind = DITEM_BUTTON, .name = "clear", .on_click = ActReplayClear },
|
||||
|
||||
{ .kind = DITEM_GAP },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayIsNotPlaying, .skip = 4 },
|
||||
{ .kind = DITEM_BUTTON, .name = "new", .on_click = ActReplayNew },
|
||||
{ .kind = DITEM_BUTTON, .name = "load", .on_click = ActReplayLoad },
|
||||
{ .kind = DITEM_BUTTON, .name = "save", .on_click = ActReplaySave },
|
||||
{ .kind = DITEM_BUTTON, .name = "save as...", .on_click = ActReplaySaveAs },
|
||||
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_sp_only = true,
|
||||
.is_collapsed = true,
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "profilers",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_RAW, .val = PROF_MAIN_LOOP, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_TEXT, .name = "unmeasured time", .proc = DrawUnmeasuredTime },
|
||||
{ .kind = DITEM_RAW, .val = PROF_WORLD_WRITE, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_RAW, .val = PROF_RENDER, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_collapsed = 1
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_BUTTON,
|
||||
.name = "exit game",
|
||||
.on_click = ActExitGame,
|
||||
},
|
||||
{.kind = DITEM_END},
|
||||
};
|
||||
|
||||
debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool is_shadow) {
|
||||
is_shadow_rendered = is_shadow;
|
||||
for (debug_item *it = list; it->kind != DITEM_END; it += 1) {
|
||||
switch (it->kind) {
|
||||
case DITEM_GAP: {
|
||||
ypos += DBG_GAP_HEIGHT;
|
||||
}break;
|
||||
case DITEM_COND: {
|
||||
ZPL_ASSERT(it->on_success);
|
||||
|
||||
if (!it->on_success()) {
|
||||
it += it->skip ? it->skip : 1;
|
||||
}
|
||||
}break;
|
||||
case DITEM_LIST: {
|
||||
// NOTE(zaklaus): calculate and cache name width for future use
|
||||
if (it->name_width == 0) {
|
||||
it->name_width = UIMeasureText(it->name, DBG_FONT_SIZE);
|
||||
}
|
||||
Color color = RAYWHITE;
|
||||
if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color)) {
|
||||
it->list.is_collapsed = !it->list.is_collapsed;
|
||||
}
|
||||
|
||||
UIDrawText(it->name, xpos, ypos, DBG_FONT_SIZE, color);
|
||||
ypos += DBG_FONT_SPACING;
|
||||
if (it->list.is_collapsed) break;
|
||||
if (it->list.is_sp_only && game_get_kind() != GAMEKIND_SINGLE) break;
|
||||
debug_draw_result res = debug_draw_list(it->list.items, xpos+DBG_LIST_XPOS_OFFSET, ypos, is_shadow);
|
||||
ypos = res.y;
|
||||
}break;
|
||||
|
||||
case DITEM_TEXT: {
|
||||
char const *text = TextFormat("%s: ", it->name);
|
||||
if (it->name_width == 0) {
|
||||
it->name_width = UIMeasureText(text, DBG_FONT_SIZE);
|
||||
}
|
||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
|
||||
ZPL_ASSERT(it->proc);
|
||||
|
||||
debug_draw_result res = it->proc(it, xpos + it->name_width, ypos);
|
||||
ypos = res.y;
|
||||
}break;
|
||||
|
||||
case DITEM_RAW: {
|
||||
ZPL_ASSERT(it->proc);
|
||||
|
||||
debug_draw_result res = it->proc(it, xpos, ypos);
|
||||
ypos = res.y;
|
||||
}break;
|
||||
|
||||
case DITEM_BUTTON: {
|
||||
char const *text = TextFormat("> %s", it->name);
|
||||
if (it->name_width == 0) {
|
||||
it->name_width = UIMeasureText(text, DBG_FONT_SIZE);
|
||||
}
|
||||
Color color = RAYWHITE;
|
||||
if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color) && it->on_click) {
|
||||
it->on_click();
|
||||
}
|
||||
|
||||
if (!it->on_click) {
|
||||
color = GRAY;
|
||||
}
|
||||
|
||||
debug_draw_result res = DrawColoredText(xpos, ypos, text, color);
|
||||
ypos = res.y;
|
||||
}break;
|
||||
|
||||
case DITEM_SLIDER: {
|
||||
ZPL_ASSERT(it->slider.min != it->slider.max);
|
||||
char const *text = TextFormat("%s: ", it->name);
|
||||
if (it->name_width == 0) {
|
||||
it->name_width = UIMeasureText(text, DBG_FONT_SIZE);
|
||||
}
|
||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
|
||||
xpos += it->name_width;
|
||||
|
||||
DrawRectangleLines(xpos, ypos, 100.0f, DBG_FONT_SIZE, RAYWHITE);
|
||||
|
||||
float stick_x = xpos + ((it->slider.val / it->slider.max) * 100.0f) - 5.0f;
|
||||
DrawRectangle(stick_x, ypos, 10.0f, DBG_FONT_SIZE, RED);
|
||||
|
||||
xpos += 100.0f + 5.0f;
|
||||
DrawFloat(xpos, ypos, it->slider.val);
|
||||
ypos += DBG_FONT_SPACING;
|
||||
}break;
|
||||
|
||||
default: {
|
||||
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
return (debug_draw_result){xpos, ypos};
|
||||
}
|
||||
|
||||
void debug_draw(void) {
|
||||
// NOTE(zaklaus): Flush old debug samples
|
||||
debug_draw_flush();
|
||||
|
||||
float xpos = debug_xpos;
|
||||
float ypos = debug_ypos;
|
||||
|
||||
// NOTE(zaklaus): move debug ui
|
||||
{
|
||||
debug_area_status area = check_mouse_area(xpos, ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM);
|
||||
Color color = BLUE;
|
||||
if (area == DAREA_HOVER) color = YELLOW;
|
||||
if (area == DAREA_HELD) {
|
||||
color = RED;
|
||||
is_handle_ctrl_held = 1;
|
||||
}
|
||||
|
||||
if (is_handle_ctrl_held) {
|
||||
debug_xpos = xpos = GetMouseX() - DBG_CTRL_HANDLE_DIM/2;
|
||||
debug_ypos = ypos = GetMouseY() - DBG_CTRL_HANDLE_DIM/2;
|
||||
|
||||
if (area == DAREA_PRESS) {
|
||||
is_handle_ctrl_held = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DrawRectangle(xpos, ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM, color);
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): toggle debug ui
|
||||
{
|
||||
Color color = BLUE;
|
||||
debug_area_status area = check_mouse_area(xpos, 15+ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM);
|
||||
if (area == DAREA_HOVER) color = YELLOW;
|
||||
if (area == DAREA_HELD) {
|
||||
color = RED;
|
||||
}
|
||||
if (area == DAREA_PRESS) {
|
||||
is_debug_open = !is_debug_open;
|
||||
}
|
||||
DrawPoly((Vector2){xpos+DBG_CTRL_HANDLE_DIM/2, ypos+15+DBG_CTRL_HANDLE_DIM/2}, 3, 6.0f,is_debug_open ? 0.0f : 180.0f, color);
|
||||
}
|
||||
|
||||
if (is_debug_open) {
|
||||
xpos += 15;
|
||||
debug_draw_list(items, xpos+DBG_SHADOW_OFFSET_XPOS, ypos+DBG_SHADOW_OFFSET_YPOS, 1); // NOTE(zaklaus): draw shadow
|
||||
debug_draw_list(items, xpos, ypos, 0);
|
||||
}
|
||||
}
|
||||
|
||||
debug_area_status check_mouse_area(float xpos, float ypos, float w, float h) {
|
||||
if (is_shadow_rendered) return DAREA_OUTSIDE;
|
||||
bool is_inside = CheckCollisionPointRec(GetMousePosition(), (Rectangle){xpos, ypos, w, h});
|
||||
|
||||
if (is_inside) {
|
||||
return IsMouseButtonReleased(MOUSE_LEFT_BUTTON) ? DAREA_PRESS : IsMouseButtonDown(MOUSE_LEFT_BUTTON) ? DAREA_HELD : DAREA_HOVER;
|
||||
}
|
||||
return DAREA_OUTSIDE;
|
||||
}
|
||||
|
||||
bool is_btn_pressed(float xpos, float ypos, float w, float h, Color *color) {
|
||||
ZPL_ASSERT(color);
|
||||
*color = RAYWHITE;
|
||||
debug_area_status area = check_mouse_area(xpos, ypos, w, h);
|
||||
if (area == DAREA_PRESS) {
|
||||
*color = RED;
|
||||
return true;
|
||||
} else if (area == DAREA_HOVER) {
|
||||
*color = YELLOW;
|
||||
} else if (area == DAREA_HELD) {
|
||||
*color = RED;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline
|
||||
void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color) {
|
||||
// Check if default font has been loaded
|
||||
if (GetFontDefault().texture.id != 0) {
|
||||
Vector2 position = { (float)posX , (float)posY };
|
||||
|
||||
int defaultFontSize = 10; // Default Font chars height in pixel
|
||||
int new_spacing = fontSize/defaultFontSize;
|
||||
|
||||
DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , is_shadow_rendered ? BLACK : color);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
int UIMeasureText(const char *text, int fontSize) {
|
||||
Vector2 vec = { 0.0f, 0.0f };
|
||||
|
||||
// Check if default font has been loaded
|
||||
if (GetFontDefault().texture.id != 0) {
|
||||
int defaultFontSize = 10; // Default Font chars height in pixel
|
||||
int new_spacing = fontSize/defaultFontSize;
|
||||
|
||||
vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing);
|
||||
}
|
||||
|
||||
return (int)vec.x;
|
||||
}
|
||||
#include "debug_ui.h"
|
||||
#include "debug_draw.h"
|
||||
#include "raylib.h"
|
||||
#include "vehicle.h"
|
||||
#include "camera.h"
|
||||
#include "world/world.h"
|
||||
#include "game.h"
|
||||
#include "sfd.h"
|
||||
|
||||
#include "modules/components.h"
|
||||
|
||||
typedef enum {
|
||||
DITEM_RAW,
|
||||
DITEM_GAP,
|
||||
DITEM_TEXT,
|
||||
DITEM_BUTTON,
|
||||
DITEM_SLIDER,
|
||||
DITEM_LIST,
|
||||
DITEM_COND,
|
||||
DITEM_END,
|
||||
|
||||
DITEM_FORCE_UINT8 = UINT8_MAX
|
||||
} debug_kind;
|
||||
|
||||
typedef struct {
|
||||
float x, y;
|
||||
} debug_draw_result;
|
||||
|
||||
#define DBG_FONT_SIZE 22
|
||||
#define DBG_FONT_SPACING DBG_FONT_SIZE * 1.2f
|
||||
#define DBG_START_XPOS 15
|
||||
#define DBG_START_YPOS 200
|
||||
#define DBG_LIST_XPOS_OFFSET 10
|
||||
#define DBG_SHADOW_OFFSET_XPOS 1
|
||||
#define DBG_SHADOW_OFFSET_YPOS 1
|
||||
#define DBG_CTRL_HANDLE_DIM 10
|
||||
#define DBG_GAP_HEIGHT DBG_FONT_SPACING * 0.5f
|
||||
|
||||
static uint8_t is_shadow_rendered;
|
||||
static uint8_t is_debug_open = 1;
|
||||
static uint8_t is_handle_ctrl_held;
|
||||
static float debug_xpos = DBG_START_XPOS;
|
||||
static float debug_ypos = DBG_START_YPOS;
|
||||
|
||||
typedef struct debug_item {
|
||||
debug_kind kind;
|
||||
char const *name;
|
||||
float name_width;
|
||||
uint8_t skip;
|
||||
|
||||
union {
|
||||
union {
|
||||
char const *text;
|
||||
uint64_t val;
|
||||
};
|
||||
|
||||
struct {
|
||||
struct debug_item *items;
|
||||
uint8_t is_collapsed;
|
||||
uint8_t is_sp_only;
|
||||
} list;
|
||||
|
||||
struct {
|
||||
float val, min, max;
|
||||
void (*on_change)(float);
|
||||
} slider;
|
||||
|
||||
void (*on_click)(void);
|
||||
|
||||
uint8_t (*on_success)(void);
|
||||
};
|
||||
|
||||
debug_draw_result (*proc)(struct debug_item*, float, float);
|
||||
} debug_item;
|
||||
|
||||
static void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color);
|
||||
static int UIMeasureText(const char *text, int fontSize);
|
||||
|
||||
#include "debug_replay.c"
|
||||
|
||||
#include "debug_ui_actions.c"
|
||||
#include "debug_ui_widgets.c"
|
||||
|
||||
static debug_item items[] = {
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "general",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime },
|
||||
{ .kind = DITEM_TEXT, .name = "pos", .proc = DrawCameraPos },
|
||||
{ .kind = DITEM_TEXT, .name = "zoom", .proc = DrawZoom },
|
||||
{ .kind = DITEM_END },
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "world simulation",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_COND, .on_success = CondIsWorldRunning },
|
||||
{ .kind = DITEM_BUTTON, .name = "pause", .on_click = ActWorldToggleSim },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondIsWorldPaused, .skip = 6 },
|
||||
{ .kind = DITEM_BUTTON, .name = "resume", .on_click = ActWorldToggleSim },
|
||||
|
||||
{ .kind = DITEM_GAP },
|
||||
|
||||
{ .kind = DITEM_TEXT, .name = "step size", .proc = DrawWorldStepSize },
|
||||
{ .kind = DITEM_BUTTON, .name = "single-step", .on_click = ActWorldStep },
|
||||
{ .kind = DITEM_BUTTON, .name = "increment step size", .on_click = ActWorldIncrementSimStepSize },
|
||||
{ .kind = DITEM_BUTTON, .name = "decrement step size", .on_click = ActWorldDecrementSimStepSize },
|
||||
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_sp_only = true,
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "debug actions",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_BUTTON, .name = "spawn car", .on_click = ActSpawnCar },
|
||||
{ .kind = DITEM_BUTTON, .name = "place ice rink", .on_click = ActPlaceIceRink },
|
||||
{ .kind = DITEM_BUTTON, .name = "erase world changes", .on_click = ActEraseWorldChanges },
|
||||
{ .kind = DITEM_BUTTON, .name = "spawn circling driver", .on_click = ActSpawnCirclingDriver },
|
||||
{ .kind = DITEM_BUTTON, .name = "spawn icemaker item", .on_click = ActSpawnIcemaker },
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "demo npcs",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_TEXT, .name = "npcs", .proc = DrawDemoNPCCount },
|
||||
{ .kind = DITEM_BUTTON, .name = "spawn 1000 npcs", .on_click = ActSpawnDemoNPCs },
|
||||
{ .kind = DITEM_BUTTON, .name = "remove all demo npcs", .on_click = ActDespawnDemoNPCs },
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_collapsed = true
|
||||
}
|
||||
},
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_sp_only = true,
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "replay system",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_TEXT, .name = "macro", .proc = DrawReplayFileName },
|
||||
{ .kind = DITEM_TEXT, .name = "samples", .proc = DrawReplaySamples },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayDataPresentAndNotPlaying },
|
||||
{ .kind = DITEM_BUTTON, .name = "replay", .on_click = ActReplayRun },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayStatusOff },
|
||||
{ .kind = DITEM_BUTTON, .name = "record", .on_click = ActReplayBegin },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayStatusOn },
|
||||
{ .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayIsPlaying },
|
||||
{ .kind = DITEM_BUTTON, .name = "stop", .on_click = ActReplayEnd },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayIsNotPlayingOrRecordsNotClear },
|
||||
{ .kind = DITEM_BUTTON, .name = "clear", .on_click = ActReplayClear },
|
||||
|
||||
{ .kind = DITEM_GAP },
|
||||
|
||||
{ .kind = DITEM_COND, .on_success = CondReplayIsNotPlaying, .skip = 4 },
|
||||
{ .kind = DITEM_BUTTON, .name = "new", .on_click = ActReplayNew },
|
||||
{ .kind = DITEM_BUTTON, .name = "load", .on_click = ActReplayLoad },
|
||||
{ .kind = DITEM_BUTTON, .name = "save", .on_click = ActReplaySave },
|
||||
{ .kind = DITEM_BUTTON, .name = "save as...", .on_click = ActReplaySaveAs },
|
||||
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_sp_only = true,
|
||||
.is_collapsed = true,
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_LIST,
|
||||
.name = "profilers",
|
||||
.list = {
|
||||
.items = (debug_item[]) {
|
||||
{ .kind = DITEM_RAW, .val = PROF_MAIN_LOOP, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_TEXT, .name = "unmeasured time", .proc = DrawUnmeasuredTime },
|
||||
{ .kind = DITEM_RAW, .val = PROF_WORLD_WRITE, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_RAW, .val = PROF_RENDER, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta },
|
||||
{ .kind = DITEM_END },
|
||||
},
|
||||
.is_collapsed = 1
|
||||
}
|
||||
},
|
||||
{
|
||||
.kind = DITEM_BUTTON,
|
||||
.name = "exit game",
|
||||
.on_click = ActExitGame,
|
||||
},
|
||||
{.kind = DITEM_END},
|
||||
};
|
||||
|
||||
debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool is_shadow) {
|
||||
is_shadow_rendered = is_shadow;
|
||||
for (debug_item *it = list; it->kind != DITEM_END; it += 1) {
|
||||
switch (it->kind) {
|
||||
case DITEM_GAP: {
|
||||
ypos += DBG_GAP_HEIGHT;
|
||||
}break;
|
||||
case DITEM_COND: {
|
||||
ZPL_ASSERT(it->on_success);
|
||||
|
||||
if (!it->on_success()) {
|
||||
it += it->skip ? it->skip : 1;
|
||||
}
|
||||
}break;
|
||||
case DITEM_LIST: {
|
||||
// NOTE(zaklaus): calculate and cache name width for future use
|
||||
if (it->name_width == 0) {
|
||||
it->name_width = UIMeasureText(it->name, DBG_FONT_SIZE);
|
||||
}
|
||||
Color color = RAYWHITE;
|
||||
if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color)) {
|
||||
it->list.is_collapsed = !it->list.is_collapsed;
|
||||
}
|
||||
|
||||
UIDrawText(it->name, xpos, ypos, DBG_FONT_SIZE, color);
|
||||
ypos += DBG_FONT_SPACING;
|
||||
if (it->list.is_collapsed) break;
|
||||
if (it->list.is_sp_only && game_get_kind() != GAMEKIND_SINGLE) break;
|
||||
debug_draw_result res = debug_draw_list(it->list.items, xpos+DBG_LIST_XPOS_OFFSET, ypos, is_shadow);
|
||||
ypos = res.y;
|
||||
}break;
|
||||
|
||||
case DITEM_TEXT: {
|
||||
char const *text = TextFormat("%s: ", it->name);
|
||||
if (it->name_width == 0) {
|
||||
it->name_width = UIMeasureText(text, DBG_FONT_SIZE);
|
||||
}
|
||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
|
||||
ZPL_ASSERT(it->proc);
|
||||
|
||||
debug_draw_result res = it->proc(it, xpos + it->name_width, ypos);
|
||||
ypos = res.y;
|
||||
}break;
|
||||
|
||||
case DITEM_RAW: {
|
||||
ZPL_ASSERT(it->proc);
|
||||
|
||||
debug_draw_result res = it->proc(it, xpos, ypos);
|
||||
ypos = res.y;
|
||||
}break;
|
||||
|
||||
case DITEM_BUTTON: {
|
||||
char const *text = TextFormat("> %s", it->name);
|
||||
if (it->name_width == 0) {
|
||||
it->name_width = UIMeasureText(text, DBG_FONT_SIZE);
|
||||
}
|
||||
Color color = RAYWHITE;
|
||||
if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color) && it->on_click) {
|
||||
it->on_click();
|
||||
}
|
||||
|
||||
if (!it->on_click) {
|
||||
color = GRAY;
|
||||
}
|
||||
|
||||
debug_draw_result res = DrawColoredText(xpos, ypos, text, color);
|
||||
ypos = res.y;
|
||||
}break;
|
||||
|
||||
case DITEM_SLIDER: {
|
||||
ZPL_ASSERT(it->slider.min != it->slider.max);
|
||||
char const *text = TextFormat("%s: ", it->name);
|
||||
if (it->name_width == 0) {
|
||||
it->name_width = UIMeasureText(text, DBG_FONT_SIZE);
|
||||
}
|
||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
|
||||
xpos += it->name_width;
|
||||
|
||||
DrawRectangleLines(xpos, ypos, 100.0f, DBG_FONT_SIZE, RAYWHITE);
|
||||
|
||||
float stick_x = xpos + ((it->slider.val / it->slider.max) * 100.0f) - 5.0f;
|
||||
DrawRectangle(stick_x, ypos, 10.0f, DBG_FONT_SIZE, RED);
|
||||
|
||||
xpos += 100.0f + 5.0f;
|
||||
DrawFloat(xpos, ypos, it->slider.val);
|
||||
ypos += DBG_FONT_SPACING;
|
||||
}break;
|
||||
|
||||
default: {
|
||||
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
return (debug_draw_result){xpos, ypos};
|
||||
}
|
||||
|
||||
void debug_draw(void) {
|
||||
// NOTE(zaklaus): Flush old debug samples
|
||||
debug_draw_flush();
|
||||
|
||||
float xpos = debug_xpos;
|
||||
float ypos = debug_ypos;
|
||||
|
||||
// NOTE(zaklaus): move debug ui
|
||||
{
|
||||
debug_area_status area = check_mouse_area(xpos, ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM);
|
||||
Color color = BLUE;
|
||||
if (area == DAREA_HOVER) color = YELLOW;
|
||||
if (area == DAREA_HELD) {
|
||||
color = RED;
|
||||
is_handle_ctrl_held = 1;
|
||||
}
|
||||
|
||||
if (is_handle_ctrl_held) {
|
||||
debug_xpos = xpos = GetMouseX() - DBG_CTRL_HANDLE_DIM/2;
|
||||
debug_ypos = ypos = GetMouseY() - DBG_CTRL_HANDLE_DIM/2;
|
||||
|
||||
if (area == DAREA_PRESS) {
|
||||
is_handle_ctrl_held = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DrawRectangle(xpos, ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM, color);
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): toggle debug ui
|
||||
{
|
||||
Color color = BLUE;
|
||||
debug_area_status area = check_mouse_area(xpos, 15+ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM);
|
||||
if (area == DAREA_HOVER) color = YELLOW;
|
||||
if (area == DAREA_HELD) {
|
||||
color = RED;
|
||||
}
|
||||
if (area == DAREA_PRESS) {
|
||||
is_debug_open = !is_debug_open;
|
||||
}
|
||||
DrawPoly((Vector2){xpos+DBG_CTRL_HANDLE_DIM/2, ypos+15+DBG_CTRL_HANDLE_DIM/2}, 3, 6.0f,is_debug_open ? 0.0f : 180.0f, color);
|
||||
}
|
||||
|
||||
if (is_debug_open) {
|
||||
xpos += 15;
|
||||
debug_draw_list(items, xpos+DBG_SHADOW_OFFSET_XPOS, ypos+DBG_SHADOW_OFFSET_YPOS, 1); // NOTE(zaklaus): draw shadow
|
||||
debug_draw_list(items, xpos, ypos, 0);
|
||||
}
|
||||
}
|
||||
|
||||
debug_area_status check_mouse_area(float xpos, float ypos, float w, float h) {
|
||||
if (is_shadow_rendered) return DAREA_OUTSIDE;
|
||||
bool is_inside = CheckCollisionPointRec(GetMousePosition(), (Rectangle){xpos, ypos, w, h});
|
||||
|
||||
if (is_inside) {
|
||||
return IsMouseButtonReleased(MOUSE_LEFT_BUTTON) ? DAREA_PRESS : IsMouseButtonDown(MOUSE_LEFT_BUTTON) ? DAREA_HELD : DAREA_HOVER;
|
||||
}
|
||||
return DAREA_OUTSIDE;
|
||||
}
|
||||
|
||||
bool is_btn_pressed(float xpos, float ypos, float w, float h, Color *color) {
|
||||
ZPL_ASSERT(color);
|
||||
*color = RAYWHITE;
|
||||
debug_area_status area = check_mouse_area(xpos, ypos, w, h);
|
||||
if (area == DAREA_PRESS) {
|
||||
*color = RED;
|
||||
return true;
|
||||
} else if (area == DAREA_HOVER) {
|
||||
*color = YELLOW;
|
||||
} else if (area == DAREA_HELD) {
|
||||
*color = RED;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline
|
||||
void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color) {
|
||||
// Check if default font has been loaded
|
||||
if (GetFontDefault().texture.id != 0) {
|
||||
Vector2 position = { (float)posX , (float)posY };
|
||||
|
||||
int defaultFontSize = 10; // Default Font chars height in pixel
|
||||
int new_spacing = fontSize/defaultFontSize;
|
||||
|
||||
DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , is_shadow_rendered ? BLACK : color);
|
||||
}
|
||||
}
|
||||
|
||||
static inline
|
||||
int UIMeasureText(const char *text, int fontSize) {
|
||||
Vector2 vec = { 0.0f, 0.0f };
|
||||
|
||||
// Check if default font has been loaded
|
||||
if (GetFontDefault().texture.id != 0) {
|
||||
int defaultFontSize = 10; // Default Font chars height in pixel
|
||||
int new_spacing = fontSize/defaultFontSize;
|
||||
|
||||
vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing);
|
||||
}
|
||||
|
||||
return (int)vec.x;
|
||||
}
|
||||
|
|
|
@ -1,272 +1,272 @@
|
|||
#include "debug_ui.h"
|
||||
#include "world/blocks.h"
|
||||
#include "items.h"
|
||||
|
||||
void
|
||||
ActExitGame(void) {
|
||||
game_request_close();
|
||||
}
|
||||
|
||||
void
|
||||
ActSpawnCar(void) {
|
||||
ecs_entity_t e = vehicle_spawn();
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
|
||||
Position const* origin = ecs_get(world_ecs(), plr, Position);
|
||||
Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL);
|
||||
*dest = *origin;
|
||||
|
||||
debug_replay_special_action(RPKIND_SPAWN_CAR);
|
||||
}
|
||||
|
||||
void
|
||||
ActSpawnIcemaker(void) {
|
||||
ecs_entity_t e = item_spawn(IKIND_DEMO_ICEMAKER, 32);
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
|
||||
Position const* origin = ecs_get(world_ecs(), plr, Position);
|
||||
Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL);
|
||||
*dest = *origin;
|
||||
|
||||
debug_replay_special_action(RPKIND_SPAWN_ICEMAKER_ITEM);
|
||||
}
|
||||
|
||||
void
|
||||
ActSpawnCirclingDriver(void) {
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
ecs_entity_t ve = vehicle_spawn();
|
||||
ecs_entity_t e = entity_spawn(EKIND_DEMO_NPC);
|
||||
|
||||
Position const *origin = ecs_get(world_ecs(), plr, Position);
|
||||
Position *veh_dest = ecs_get_mut(world_ecs(), ve, Position, NULL);
|
||||
Position *dest = ecs_get_mut(world_ecs(), e, Position, NULL);
|
||||
*veh_dest = *origin;
|
||||
*dest = *origin;
|
||||
|
||||
Input *input = ecs_get_mut(world_ecs(), e, Input, NULL);
|
||||
zpl_zero_item(input);
|
||||
input->x = input->y = 1.0f;
|
||||
|
||||
Vehicle *veh = ecs_get_mut(world_ecs(), ve, Vehicle, NULL);
|
||||
veh->seats[0] = e;
|
||||
|
||||
ecs_set(world_ecs(), e, IsInVehicle, { .veh = ve });
|
||||
|
||||
debug_replay_special_action(RPKIND_SPAWN_CIRCLING_DRIVER);
|
||||
}
|
||||
|
||||
void
|
||||
ActPlaceIceRink(void) {
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
uint8_t watr_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER);
|
||||
Position const *p = ecs_get(world_ecs(), plr, Position);
|
||||
float const bs = WORLD_BLOCK_SIZE;
|
||||
|
||||
for (int y = 0; y < 100; y++) {
|
||||
for (int x = 0; x < 100; x++) {
|
||||
world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f);
|
||||
world_chunk_replace_block(l.chunk_id, l.id, watr_id);
|
||||
}
|
||||
}
|
||||
|
||||
debug_replay_special_action(RPKIND_PLACE_ICE_RINK);
|
||||
}
|
||||
|
||||
void
|
||||
ActEraseWorldChanges(void) {
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
Position const *p = ecs_get(world_ecs(), plr, Position);
|
||||
float const bs = WORLD_BLOCK_SIZE;
|
||||
|
||||
for (int y = 0; y < 100; y++) {
|
||||
for (int x = 0; x < 100; x++) {
|
||||
world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f);
|
||||
world_chunk_place_block(l.chunk_id, l.id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
debug_replay_special_action(RPKIND_PLACE_ERASE_CHANGES);
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): Replay system
|
||||
|
||||
uint8_t
|
||||
CondReplayStatusOn(void) {
|
||||
return is_recording && !is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayStatusOff(void) {
|
||||
return !is_recording && !is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayDataPresentAndNotPlaying(void) {
|
||||
return records != NULL && !is_recording && !is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayIsPlaying(void) {
|
||||
return records != NULL && !is_recording && is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayIsNotPlaying(void) {
|
||||
return !is_recording && !is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayIsNotPlayingOrRecordsNotClear(void) {
|
||||
return records != NULL && !is_recording && !is_playing;
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayBegin(void) {
|
||||
debug_replay_start();
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayEnd(void) {
|
||||
debug_replay_stop();
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayRun(void) {
|
||||
debug_replay_run();
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayClear(void) {
|
||||
debug_replay_clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ActReplayNew(void) {
|
||||
debug_replay_clear();
|
||||
zpl_zero_size(replay_filename, sizeof(replay_filename));
|
||||
}
|
||||
|
||||
void
|
||||
ActReplaySaveAs(void) {
|
||||
if (!records) return;
|
||||
char const *workdir = GetWorkingDirectory();
|
||||
|
||||
sfd_Options sfd = {
|
||||
.title = "Save Macro",
|
||||
.path = "art",
|
||||
.filter_name = "eco2d Macro",
|
||||
.filter = "*.dem",
|
||||
};
|
||||
|
||||
char const *path = sfd_save_dialog(&sfd);
|
||||
ChangeDirectory(workdir);
|
||||
|
||||
if (path) {
|
||||
zpl_strcpy(replay_filename, zpl_bprintf("%s.dem", path));
|
||||
debug_replay_store();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ActReplaySave(void) {
|
||||
if (!replay_filename[0]) {
|
||||
ActReplaySaveAs();
|
||||
}
|
||||
else debug_replay_store();
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayLoad(void) {
|
||||
char const *workdir = GetWorkingDirectory();
|
||||
|
||||
sfd_Options sfd = {
|
||||
.title = "Load Macro",
|
||||
.path = "art",
|
||||
.filter_name = "eco2d Macro",
|
||||
.filter = "*.dem",
|
||||
};
|
||||
|
||||
char const *path = sfd_open_dialog(&sfd);
|
||||
ChangeDirectory(workdir);
|
||||
|
||||
if (path) {
|
||||
zpl_zero_size(replay_filename, sizeof(replay_filename));
|
||||
zpl_strcpy(replay_filename, path);
|
||||
debug_replay_clear();
|
||||
debug_replay_load();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): Demo NPCs
|
||||
static ecs_entity_t *demo_npcs = NULL;
|
||||
|
||||
void
|
||||
ActSpawnDemoNPCs(void) {
|
||||
if (!demo_npcs) zpl_array_init(demo_npcs, zpl_heap());
|
||||
if (zpl_array_count(demo_npcs) >= 100000) return;
|
||||
|
||||
for (uint32_t i = 0; i < 1000; i++) {
|
||||
uint64_t e = entity_spawn(EKIND_DEMO_NPC);
|
||||
ecs_add(world_ecs(), e, EcsDemoNPC);
|
||||
Position *pos = ecs_get_mut(world_ecs(), e, Position, NULL);
|
||||
pos->x=rand() % world_dim();
|
||||
pos->y=rand() % world_dim();
|
||||
|
||||
Velocity *v = ecs_get_mut(world_ecs(), e, Velocity, NULL);
|
||||
v->x = (rand()%3-1) * 10;
|
||||
v->y = (rand()%3-1) * 10;
|
||||
|
||||
zpl_array_append(demo_npcs, e);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ActDespawnDemoNPCs(void) {
|
||||
if (!demo_npcs) return;
|
||||
|
||||
entity_batch_despawn(demo_npcs, zpl_array_count(demo_npcs));
|
||||
|
||||
zpl_array_free(demo_npcs);
|
||||
demo_npcs = 0;
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): world simulation controls
|
||||
#define WORLDSIM_STEPPING 0.01f
|
||||
static float sim_step_size = 0.1f;
|
||||
|
||||
void
|
||||
ActWorldToggleSim(void) {
|
||||
if (world_is_paused()) {
|
||||
world_resume();
|
||||
} else {
|
||||
world_pause();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ActWorldIncrementSimStepSize(void) {
|
||||
sim_step_size += WORLDSIM_STEPPING;
|
||||
}
|
||||
|
||||
void
|
||||
ActWorldDecrementSimStepSize(void) {
|
||||
if (sim_step_size > WORLDSIM_STEPPING)
|
||||
sim_step_size -= WORLDSIM_STEPPING;
|
||||
}
|
||||
|
||||
void
|
||||
ActWorldStep(void) {
|
||||
world_step(sim_step_size);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondIsWorldPaused(void) {
|
||||
return world_is_paused();
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondIsWorldRunning(void) {
|
||||
return !world_is_paused();
|
||||
}
|
||||
#include "debug_ui.h"
|
||||
#include "world/blocks.h"
|
||||
#include "items.h"
|
||||
|
||||
void
|
||||
ActExitGame(void) {
|
||||
game_request_close();
|
||||
}
|
||||
|
||||
void
|
||||
ActSpawnCar(void) {
|
||||
ecs_entity_t e = vehicle_spawn();
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
|
||||
Position const* origin = ecs_get(world_ecs(), plr, Position);
|
||||
Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL);
|
||||
*dest = *origin;
|
||||
|
||||
debug_replay_special_action(RPKIND_SPAWN_CAR);
|
||||
}
|
||||
|
||||
void
|
||||
ActSpawnIcemaker(void) {
|
||||
ecs_entity_t e = item_spawn(IKIND_DEMO_ICEMAKER, 32);
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
|
||||
Position const* origin = ecs_get(world_ecs(), plr, Position);
|
||||
Position * dest = ecs_get_mut(world_ecs(), e, Position, NULL);
|
||||
*dest = *origin;
|
||||
|
||||
debug_replay_special_action(RPKIND_SPAWN_ICEMAKER_ITEM);
|
||||
}
|
||||
|
||||
void
|
||||
ActSpawnCirclingDriver(void) {
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
ecs_entity_t ve = vehicle_spawn();
|
||||
ecs_entity_t e = entity_spawn(EKIND_DEMO_NPC);
|
||||
|
||||
Position const *origin = ecs_get(world_ecs(), plr, Position);
|
||||
Position *veh_dest = ecs_get_mut(world_ecs(), ve, Position, NULL);
|
||||
Position *dest = ecs_get_mut(world_ecs(), e, Position, NULL);
|
||||
*veh_dest = *origin;
|
||||
*dest = *origin;
|
||||
|
||||
Input *input = ecs_get_mut(world_ecs(), e, Input, NULL);
|
||||
zpl_zero_item(input);
|
||||
input->x = input->y = 1.0f;
|
||||
|
||||
Vehicle *veh = ecs_get_mut(world_ecs(), ve, Vehicle, NULL);
|
||||
veh->seats[0] = e;
|
||||
|
||||
ecs_set(world_ecs(), e, IsInVehicle, { .veh = ve });
|
||||
|
||||
debug_replay_special_action(RPKIND_SPAWN_CIRCLING_DRIVER);
|
||||
}
|
||||
|
||||
void
|
||||
ActPlaceIceRink(void) {
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
uint8_t watr_id = blocks_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER);
|
||||
Position const *p = ecs_get(world_ecs(), plr, Position);
|
||||
float const bs = WORLD_BLOCK_SIZE;
|
||||
|
||||
for (int y = 0; y < 100; y++) {
|
||||
for (int x = 0; x < 100; x++) {
|
||||
world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f);
|
||||
world_chunk_replace_block(l.chunk_id, l.id, watr_id);
|
||||
}
|
||||
}
|
||||
|
||||
debug_replay_special_action(RPKIND_PLACE_ICE_RINK);
|
||||
}
|
||||
|
||||
void
|
||||
ActEraseWorldChanges(void) {
|
||||
ecs_entity_t plr = camera_get().ent_id;
|
||||
Position const *p = ecs_get(world_ecs(), plr, Position);
|
||||
float const bs = WORLD_BLOCK_SIZE;
|
||||
|
||||
for (int y = 0; y < 100; y++) {
|
||||
for (int x = 0; x < 100; x++) {
|
||||
world_block_lookup l = world_block_from_realpos((p->x - (x*bs)/2.0f), p->y - (y*bs)/2.0f);
|
||||
world_chunk_place_block(l.chunk_id, l.id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
debug_replay_special_action(RPKIND_PLACE_ERASE_CHANGES);
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): Replay system
|
||||
|
||||
uint8_t
|
||||
CondReplayStatusOn(void) {
|
||||
return is_recording && !is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayStatusOff(void) {
|
||||
return !is_recording && !is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayDataPresentAndNotPlaying(void) {
|
||||
return records != NULL && !is_recording && !is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayIsPlaying(void) {
|
||||
return records != NULL && !is_recording && is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayIsNotPlaying(void) {
|
||||
return !is_recording && !is_playing;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondReplayIsNotPlayingOrRecordsNotClear(void) {
|
||||
return records != NULL && !is_recording && !is_playing;
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayBegin(void) {
|
||||
debug_replay_start();
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayEnd(void) {
|
||||
debug_replay_stop();
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayRun(void) {
|
||||
debug_replay_run();
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayClear(void) {
|
||||
debug_replay_clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ActReplayNew(void) {
|
||||
debug_replay_clear();
|
||||
zpl_zero_size(replay_filename, sizeof(replay_filename));
|
||||
}
|
||||
|
||||
void
|
||||
ActReplaySaveAs(void) {
|
||||
if (!records) return;
|
||||
char const *workdir = GetWorkingDirectory();
|
||||
|
||||
sfd_Options sfd = {
|
||||
.title = "Save Macro",
|
||||
.path = "art",
|
||||
.filter_name = "eco2d Macro",
|
||||
.filter = "*.dem",
|
||||
};
|
||||
|
||||
char const *path = sfd_save_dialog(&sfd);
|
||||
ChangeDirectory(workdir);
|
||||
|
||||
if (path) {
|
||||
zpl_strcpy(replay_filename, zpl_bprintf("%s.dem", path));
|
||||
debug_replay_store();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ActReplaySave(void) {
|
||||
if (!replay_filename[0]) {
|
||||
ActReplaySaveAs();
|
||||
}
|
||||
else debug_replay_store();
|
||||
}
|
||||
|
||||
void
|
||||
ActReplayLoad(void) {
|
||||
char const *workdir = GetWorkingDirectory();
|
||||
|
||||
sfd_Options sfd = {
|
||||
.title = "Load Macro",
|
||||
.path = "art",
|
||||
.filter_name = "eco2d Macro",
|
||||
.filter = "*.dem",
|
||||
};
|
||||
|
||||
char const *path = sfd_open_dialog(&sfd);
|
||||
ChangeDirectory(workdir);
|
||||
|
||||
if (path) {
|
||||
zpl_zero_size(replay_filename, sizeof(replay_filename));
|
||||
zpl_strcpy(replay_filename, path);
|
||||
debug_replay_clear();
|
||||
debug_replay_load();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): Demo NPCs
|
||||
static ecs_entity_t *demo_npcs = NULL;
|
||||
|
||||
void
|
||||
ActSpawnDemoNPCs(void) {
|
||||
if (!demo_npcs) zpl_array_init(demo_npcs, zpl_heap());
|
||||
if (zpl_array_count(demo_npcs) >= 100000) return;
|
||||
|
||||
for (uint32_t i = 0; i < 1000; i++) {
|
||||
uint64_t e = entity_spawn(EKIND_DEMO_NPC);
|
||||
ecs_add(world_ecs(), e, EcsDemoNPC);
|
||||
Position *pos = ecs_get_mut(world_ecs(), e, Position, NULL);
|
||||
pos->x=rand() % world_dim();
|
||||
pos->y=rand() % world_dim();
|
||||
|
||||
Velocity *v = ecs_get_mut(world_ecs(), e, Velocity, NULL);
|
||||
v->x = (rand()%3-1) * 10;
|
||||
v->y = (rand()%3-1) * 10;
|
||||
|
||||
zpl_array_append(demo_npcs, e);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ActDespawnDemoNPCs(void) {
|
||||
if (!demo_npcs) return;
|
||||
|
||||
entity_batch_despawn(demo_npcs, zpl_array_count(demo_npcs));
|
||||
|
||||
zpl_array_free(demo_npcs);
|
||||
demo_npcs = 0;
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): world simulation controls
|
||||
#define WORLDSIM_STEPPING 0.01f
|
||||
static float sim_step_size = 0.1f;
|
||||
|
||||
void
|
||||
ActWorldToggleSim(void) {
|
||||
if (world_is_paused()) {
|
||||
world_resume();
|
||||
} else {
|
||||
world_pause();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ActWorldIncrementSimStepSize(void) {
|
||||
sim_step_size += WORLDSIM_STEPPING;
|
||||
}
|
||||
|
||||
void
|
||||
ActWorldDecrementSimStepSize(void) {
|
||||
if (sim_step_size > WORLDSIM_STEPPING)
|
||||
sim_step_size -= WORLDSIM_STEPPING;
|
||||
}
|
||||
|
||||
void
|
||||
ActWorldStep(void) {
|
||||
world_step(sim_step_size);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondIsWorldPaused(void) {
|
||||
return world_is_paused();
|
||||
}
|
||||
|
||||
uint8_t
|
||||
CondIsWorldRunning(void) {
|
||||
return !world_is_paused();
|
||||
}
|
||||
|
|
|
@ -1,101 +1,101 @@
|
|||
#include "debug_ui.h"
|
||||
#include "raylib.h"
|
||||
#include "platform.h"
|
||||
#include "profiler.h"
|
||||
|
||||
//~ NOTE(zaklaus): helpers
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawFloat(float xpos, float ypos, float val) {
|
||||
char const *text = TextFormat("%.02f\n", val);
|
||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
|
||||
return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING};
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawColoredText(float xpos, float ypos, char const *text, Color color) {
|
||||
ZPL_ASSERT(text);
|
||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, color);
|
||||
return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING};
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawFormattedText(float xpos, float ypos, char const *text) {
|
||||
return DrawColoredText(xpos, ypos, text, RAYWHITE);
|
||||
}
|
||||
|
||||
//~ NOTE(zaklaus): widgets
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawCameraPos(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
camera cam = camera_get();
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%d %d", (int)(cam.x/WORLD_BLOCK_SIZE), (int)(cam.y/WORLD_BLOCK_SIZE)));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawUnmeasuredTime(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
float total_time = profiler_delta(PROF_TOTAL_TIME);
|
||||
float acc_time = profiler_delta(PROF_MAIN_LOOP);
|
||||
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%.02f ms", (total_time-acc_time) * 1000.0f));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawDeltaTime(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
float dt = GetFrameTime();
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%.02f (%.02f fps)", dt * 1000.0f, 1.0f/dt));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawZoom(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
|
||||
return DrawFloat(xpos, ypos, platform_zoom_get());
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawLiteral(debug_item *it, float xpos, float ypos) {
|
||||
ZPL_ASSERT(it->text);
|
||||
return DrawFormattedText(xpos, ypos, it->text);
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawProfilerDelta(debug_item *it, float xpos, float ypos) {
|
||||
float dt = profiler_delta(it->val);
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%s: %.02f ms", profiler_name(it->val), dt * 1000.0f));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawReplaySamples(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
size_t cnt = 0;
|
||||
if (records) {
|
||||
cnt = zpl_array_count(records);
|
||||
}
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%d of %d", record_pos, cnt));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawReplayFileName(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%s", replay_filename[0] ? replay_filename : "<unnamed>"));
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): demo npcs
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawDemoNPCCount(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%d", demo_npcs ? zpl_array_count(demo_npcs) : 0));
|
||||
}
|
||||
|
||||
|
||||
// NOTE(zaklaus): world simulation
|
||||
static inline debug_draw_result
|
||||
DrawWorldStepSize(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%d ms", (int16_t)(sim_step_size*1000.f)));
|
||||
}
|
||||
#include "debug_ui.h"
|
||||
#include "raylib.h"
|
||||
#include "platform.h"
|
||||
#include "profiler.h"
|
||||
|
||||
//~ NOTE(zaklaus): helpers
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawFloat(float xpos, float ypos, float val) {
|
||||
char const *text = TextFormat("%.02f\n", val);
|
||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
|
||||
return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING};
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawColoredText(float xpos, float ypos, char const *text, Color color) {
|
||||
ZPL_ASSERT(text);
|
||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, color);
|
||||
return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING};
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawFormattedText(float xpos, float ypos, char const *text) {
|
||||
return DrawColoredText(xpos, ypos, text, RAYWHITE);
|
||||
}
|
||||
|
||||
//~ NOTE(zaklaus): widgets
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawCameraPos(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
camera cam = camera_get();
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%d %d", (int)(cam.x/WORLD_BLOCK_SIZE), (int)(cam.y/WORLD_BLOCK_SIZE)));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawUnmeasuredTime(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
float total_time = profiler_delta(PROF_TOTAL_TIME);
|
||||
float acc_time = profiler_delta(PROF_MAIN_LOOP);
|
||||
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%.02f ms", (total_time-acc_time) * 1000.0f));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawDeltaTime(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
float dt = GetFrameTime();
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%.02f (%.02f fps)", dt * 1000.0f, 1.0f/dt));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawZoom(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
|
||||
return DrawFloat(xpos, ypos, platform_zoom_get());
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawLiteral(debug_item *it, float xpos, float ypos) {
|
||||
ZPL_ASSERT(it->text);
|
||||
return DrawFormattedText(xpos, ypos, it->text);
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawProfilerDelta(debug_item *it, float xpos, float ypos) {
|
||||
float dt = profiler_delta(it->val);
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%s: %.02f ms", profiler_name(it->val), dt * 1000.0f));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawReplaySamples(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
size_t cnt = 0;
|
||||
if (records) {
|
||||
cnt = zpl_array_count(records);
|
||||
}
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%d of %d", record_pos, cnt));
|
||||
}
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawReplayFileName(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%s", replay_filename[0] ? replay_filename : "<unnamed>"));
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): demo npcs
|
||||
|
||||
static inline debug_draw_result
|
||||
DrawDemoNPCCount(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%d", demo_npcs ? zpl_array_count(demo_npcs) : 0));
|
||||
}
|
||||
|
||||
|
||||
// NOTE(zaklaus): world simulation
|
||||
static inline debug_draw_result
|
||||
DrawWorldStepSize(debug_item *it, float xpos, float ypos) {
|
||||
(void)it;
|
||||
return DrawFormattedText(xpos, ypos, TextFormat("%d ms", (int16_t)(sim_step_size*1000.f)));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
#include "system.h"
|
||||
|
||||
uint64_t entity_spawn(uint16_t class_id /* 0 = no streaming */);
|
||||
void entity_batch_despawn(uint64_t *ids, size_t num_ids);
|
||||
void entity_despawn(uint64_t ent_id);
|
||||
#pragma once
|
||||
#include "system.h"
|
||||
|
||||
uint64_t entity_spawn(uint16_t class_id /* 0 = no streaming */);
|
||||
void entity_batch_despawn(uint64_t *ids, size_t num_ids);
|
||||
void entity_despawn(uint64_t ent_id);
|
||||
|
|
|
@ -1,233 +1,233 @@
|
|||
#include "game.h"
|
||||
#include "zpl.h"
|
||||
#include "platform.h"
|
||||
#include "world/world.h"
|
||||
#include "packet.h"
|
||||
#include "signal_handling.h"
|
||||
#include "network.h"
|
||||
#include "entity.h"
|
||||
#include "world_view.h"
|
||||
#include "entity_view.h"
|
||||
#include "camera.h"
|
||||
#include "profiler.h"
|
||||
|
||||
#include "flecs/flecs.h"
|
||||
//#include "flecs/flecs_dash.h"
|
||||
//#include "flecs/flecs_systems_civetweb.h"
|
||||
#include "flecs/flecs_os_api_stdcpp.h"
|
||||
|
||||
#include "modules/components.h"
|
||||
#include "modules/systems.h"
|
||||
|
||||
#include "packets/pkt_00_init.h"
|
||||
#include "packets/pkt_01_welcome.h"
|
||||
|
||||
static uint8_t game_mode;
|
||||
|
||||
static world_view *world_viewers;
|
||||
static world_view *active_viewer;
|
||||
|
||||
static WORLD_PKT_READER(pkt_reader) {
|
||||
pkt_header header = {0};
|
||||
uint32_t ok = pkt_header_decode(&header, data, datalen);
|
||||
header.udata = udata;
|
||||
|
||||
if (ok && header.ok) {
|
||||
return pkt_handlers[header.id].handler(&header) >= 0;
|
||||
} else {
|
||||
zpl_printf("[warn] unknown packet id %d (header %d data %d)\n", header.id, ok, header.ok);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static WORLD_PKT_WRITER(sp_pkt_writer) {
|
||||
(void)udata;
|
||||
return world_read(pkt->data, pkt->datalen, (void*)game_world_view_get_active()->owner_id);
|
||||
}
|
||||
|
||||
static WORLD_PKT_WRITER(mp_pkt_writer) {
|
||||
if (pkt->is_reliable) {
|
||||
return network_msg_send(udata, pkt->data, pkt->datalen, pkt->channel_id);
|
||||
}
|
||||
else {
|
||||
return network_msg_send_unreliable(udata, pkt->data, pkt->datalen, pkt->channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
static WORLD_PKT_WRITER(mp_cli_pkt_writer) {
|
||||
(void)udata;
|
||||
if (pkt->is_reliable) {
|
||||
return network_msg_send(0, pkt->data, pkt->datalen, pkt->channel_id);
|
||||
}
|
||||
else {
|
||||
return network_msg_send_unreliable(0, pkt->data, pkt->datalen, pkt->channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
void world_viewers_init(uint32_t num_viewers) {
|
||||
zpl_buffer_init(world_viewers, zpl_heap(), num_viewers);
|
||||
|
||||
for (uint32_t i = 0; i < num_viewers; i++) {
|
||||
zpl_buffer_append(world_viewers, world_view_create(i));
|
||||
}
|
||||
}
|
||||
|
||||
void world_viewers_destroy() {
|
||||
for (uint32_t i = 0; i < zpl_buffer_count(world_viewers); i++) {
|
||||
world_view_destroy(&world_viewers[i]);
|
||||
}
|
||||
zpl_buffer_free(world_viewers);
|
||||
}
|
||||
|
||||
world_view *game_world_view_get(uint16_t idx) {
|
||||
return &world_viewers[idx];
|
||||
}
|
||||
|
||||
world_view *game_world_view_get_active(void) {
|
||||
return active_viewer;
|
||||
}
|
||||
|
||||
void game_world_view_cycle_active(int8_t dir) {
|
||||
uint16_t idx = (uint16_t)(active_viewer - world_viewers);
|
||||
game_world_view_set_active_by_idx(zpl_max(0, (idx+dir)%zpl_buffer_count(world_viewers)));
|
||||
}
|
||||
void game_world_view_set_active_by_idx(uint16_t idx) {
|
||||
ZPL_ASSERT(idx >= 0 && idx < zpl_buffer_count(world_viewers));
|
||||
game_world_view_set_active(&world_viewers[idx]);
|
||||
}
|
||||
|
||||
void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)) {
|
||||
entity_view_map(&active_viewer->entities, map_proc);
|
||||
}
|
||||
|
||||
entity_view *game_world_view_active_get_entity(uint64_t ent_id) {
|
||||
return entity_view_get(&active_viewer->entities, ent_id);
|
||||
}
|
||||
|
||||
void game_world_view_set_active(world_view *view) {
|
||||
active_viewer = view;
|
||||
camera_set_follow(view->owner_id);
|
||||
}
|
||||
|
||||
void flecs_dash_init() {
|
||||
#if 0
|
||||
ECS_IMPORT(world_ecs(), FlecsDash);
|
||||
ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb);
|
||||
|
||||
ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001});
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (game_mode != GAMEKIND_HEADLESS) {
|
||||
platform_init();
|
||||
|
||||
world_viewers_init(num_viewers);
|
||||
active_viewer = &world_viewers[0];
|
||||
camera_reset();
|
||||
}
|
||||
|
||||
if (game_mode != GAMEKIND_SINGLE) {
|
||||
network_init();
|
||||
}
|
||||
|
||||
if (game_mode == GAMEKIND_CLIENT) {
|
||||
world_setup_pkt_handlers(pkt_reader, mp_cli_pkt_writer);
|
||||
#ifndef _DEBUG
|
||||
network_client_connect("lab.zakto.pw", 27000);
|
||||
#else
|
||||
network_client_connect("127.0.0.1", 27000);
|
||||
#endif
|
||||
} else {
|
||||
stdcpp_set_os_api();
|
||||
world_setup_pkt_handlers(pkt_reader, game_mode == GAMEKIND_SINGLE ? sp_pkt_writer : mp_pkt_writer);
|
||||
world_init(seed, chunk_size, chunk_amount);
|
||||
if (is_dash_enabled) flecs_dash_init();
|
||||
|
||||
if (game_mode == GAMEKIND_HEADLESS) {
|
||||
network_server_start(0, 27000);
|
||||
ecs_set_target_fps(world_ecs(), 60);
|
||||
}
|
||||
}
|
||||
|
||||
if (game_mode == GAMEKIND_SINGLE) {
|
||||
for (uint32_t i = 0; i < num_viewers; i++) {
|
||||
pkt_00_init_send(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int8_t game_is_networked() {
|
||||
return game_mode != GAMEKIND_SINGLE;
|
||||
}
|
||||
|
||||
void game_shutdown() {
|
||||
|
||||
if (game_mode == GAMEKIND_CLIENT) {
|
||||
network_client_disconnect();
|
||||
} else {
|
||||
world_destroy();
|
||||
|
||||
if (game_mode == GAMEKIND_HEADLESS) {
|
||||
network_server_stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (game_mode != GAMEKIND_SINGLE) {
|
||||
network_destroy();
|
||||
}
|
||||
|
||||
if (game_mode != GAMEKIND_HEADLESS) {
|
||||
world_viewers_destroy();
|
||||
|
||||
// TODO(zaklaus): crashes on exit
|
||||
//platform_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t game_is_running() {
|
||||
return game_mode == GAMEKIND_HEADLESS || platform_is_running();
|
||||
}
|
||||
|
||||
game_kind game_get_kind(void) {
|
||||
return game_mode;
|
||||
}
|
||||
|
||||
void game_input() {
|
||||
if (game_mode != GAMEKIND_HEADLESS) {
|
||||
platform_input();
|
||||
}
|
||||
}
|
||||
|
||||
void game_update() {
|
||||
if (game_mode == GAMEKIND_CLIENT) {
|
||||
network_client_tick();
|
||||
}
|
||||
else {
|
||||
world_update();
|
||||
|
||||
if (game_mode == GAMEKIND_HEADLESS) {
|
||||
network_server_tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game_render() {
|
||||
if (game_mode != GAMEKIND_HEADLESS) {
|
||||
platform_render();
|
||||
}
|
||||
}
|
||||
|
||||
void game_action_send_keystate(game_keystate_data *data) {
|
||||
pkt_send_keystate_send(active_viewer->view_id, data);
|
||||
}
|
||||
|
||||
void game_request_close() {
|
||||
platform_request_close();
|
||||
#include "game.h"
|
||||
#include "zpl.h"
|
||||
#include "platform.h"
|
||||
#include "world/world.h"
|
||||
#include "packet.h"
|
||||
#include "signal_handling.h"
|
||||
#include "network.h"
|
||||
#include "entity.h"
|
||||
#include "world_view.h"
|
||||
#include "entity_view.h"
|
||||
#include "camera.h"
|
||||
#include "profiler.h"
|
||||
|
||||
#include "flecs/flecs.h"
|
||||
//#include "flecs/flecs_dash.h"
|
||||
//#include "flecs/flecs_systems_civetweb.h"
|
||||
#include "flecs/flecs_os_api_stdcpp.h"
|
||||
|
||||
#include "modules/components.h"
|
||||
#include "modules/systems.h"
|
||||
|
||||
#include "packets/pkt_00_init.h"
|
||||
#include "packets/pkt_01_welcome.h"
|
||||
|
||||
static uint8_t game_mode;
|
||||
|
||||
static world_view *world_viewers;
|
||||
static world_view *active_viewer;
|
||||
|
||||
static WORLD_PKT_READER(pkt_reader) {
|
||||
pkt_header header = {0};
|
||||
uint32_t ok = pkt_header_decode(&header, data, datalen);
|
||||
header.udata = udata;
|
||||
|
||||
if (ok && header.ok) {
|
||||
return pkt_handlers[header.id].handler(&header) >= 0;
|
||||
} else {
|
||||
zpl_printf("[warn] unknown packet id %d (header %d data %d)\n", header.id, ok, header.ok);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static WORLD_PKT_WRITER(sp_pkt_writer) {
|
||||
(void)udata;
|
||||
return world_read(pkt->data, pkt->datalen, (void*)game_world_view_get_active()->owner_id);
|
||||
}
|
||||
|
||||
static WORLD_PKT_WRITER(mp_pkt_writer) {
|
||||
if (pkt->is_reliable) {
|
||||
return network_msg_send(udata, pkt->data, pkt->datalen, pkt->channel_id);
|
||||
}
|
||||
else {
|
||||
return network_msg_send_unreliable(udata, pkt->data, pkt->datalen, pkt->channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
static WORLD_PKT_WRITER(mp_cli_pkt_writer) {
|
||||
(void)udata;
|
||||
if (pkt->is_reliable) {
|
||||
return network_msg_send(0, pkt->data, pkt->datalen, pkt->channel_id);
|
||||
}
|
||||
else {
|
||||
return network_msg_send_unreliable(0, pkt->data, pkt->datalen, pkt->channel_id);
|
||||
}
|
||||
}
|
||||
|
||||
void world_viewers_init(uint32_t num_viewers) {
|
||||
zpl_buffer_init(world_viewers, zpl_heap(), num_viewers);
|
||||
|
||||
for (uint32_t i = 0; i < num_viewers; i++) {
|
||||
zpl_buffer_append(world_viewers, world_view_create(i));
|
||||
}
|
||||
}
|
||||
|
||||
void world_viewers_destroy() {
|
||||
for (uint32_t i = 0; i < zpl_buffer_count(world_viewers); i++) {
|
||||
world_view_destroy(&world_viewers[i]);
|
||||
}
|
||||
zpl_buffer_free(world_viewers);
|
||||
}
|
||||
|
||||
world_view *game_world_view_get(uint16_t idx) {
|
||||
return &world_viewers[idx];
|
||||
}
|
||||
|
||||
world_view *game_world_view_get_active(void) {
|
||||
return active_viewer;
|
||||
}
|
||||
|
||||
void game_world_view_cycle_active(int8_t dir) {
|
||||
uint16_t idx = (uint16_t)(active_viewer - world_viewers);
|
||||
game_world_view_set_active_by_idx(zpl_max(0, (idx+dir)%zpl_buffer_count(world_viewers)));
|
||||
}
|
||||
void game_world_view_set_active_by_idx(uint16_t idx) {
|
||||
ZPL_ASSERT(idx >= 0 && idx < zpl_buffer_count(world_viewers));
|
||||
game_world_view_set_active(&world_viewers[idx]);
|
||||
}
|
||||
|
||||
void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value)) {
|
||||
entity_view_map(&active_viewer->entities, map_proc);
|
||||
}
|
||||
|
||||
entity_view *game_world_view_active_get_entity(uint64_t ent_id) {
|
||||
return entity_view_get(&active_viewer->entities, ent_id);
|
||||
}
|
||||
|
||||
void game_world_view_set_active(world_view *view) {
|
||||
active_viewer = view;
|
||||
camera_set_follow(view->owner_id);
|
||||
}
|
||||
|
||||
void flecs_dash_init() {
|
||||
#if 0
|
||||
ECS_IMPORT(world_ecs(), FlecsDash);
|
||||
ECS_IMPORT(world_ecs(), FlecsSystemsCivetweb);
|
||||
|
||||
ecs_set(world_ecs(), 0, EcsDashServer, {.port = 27001});
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (game_mode != GAMEKIND_HEADLESS) {
|
||||
platform_init();
|
||||
|
||||
world_viewers_init(num_viewers);
|
||||
active_viewer = &world_viewers[0];
|
||||
camera_reset();
|
||||
}
|
||||
|
||||
if (game_mode != GAMEKIND_SINGLE) {
|
||||
network_init();
|
||||
}
|
||||
|
||||
if (game_mode == GAMEKIND_CLIENT) {
|
||||
world_setup_pkt_handlers(pkt_reader, mp_cli_pkt_writer);
|
||||
#ifndef _DEBUG
|
||||
network_client_connect("lab.zakto.pw", 27000);
|
||||
#else
|
||||
network_client_connect("127.0.0.1", 27000);
|
||||
#endif
|
||||
} else {
|
||||
stdcpp_set_os_api();
|
||||
world_setup_pkt_handlers(pkt_reader, game_mode == GAMEKIND_SINGLE ? sp_pkt_writer : mp_pkt_writer);
|
||||
world_init(seed, chunk_size, chunk_amount);
|
||||
if (is_dash_enabled) flecs_dash_init();
|
||||
|
||||
if (game_mode == GAMEKIND_HEADLESS) {
|
||||
network_server_start(0, 27000);
|
||||
ecs_set_target_fps(world_ecs(), 60);
|
||||
}
|
||||
}
|
||||
|
||||
if (game_mode == GAMEKIND_SINGLE) {
|
||||
for (uint32_t i = 0; i < num_viewers; i++) {
|
||||
pkt_00_init_send(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int8_t game_is_networked() {
|
||||
return game_mode != GAMEKIND_SINGLE;
|
||||
}
|
||||
|
||||
void game_shutdown() {
|
||||
|
||||
if (game_mode == GAMEKIND_CLIENT) {
|
||||
network_client_disconnect();
|
||||
} else {
|
||||
world_destroy();
|
||||
|
||||
if (game_mode == GAMEKIND_HEADLESS) {
|
||||
network_server_stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (game_mode != GAMEKIND_SINGLE) {
|
||||
network_destroy();
|
||||
}
|
||||
|
||||
if (game_mode != GAMEKIND_HEADLESS) {
|
||||
world_viewers_destroy();
|
||||
|
||||
// TODO(zaklaus): crashes on exit
|
||||
//platform_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t game_is_running() {
|
||||
return game_mode == GAMEKIND_HEADLESS || platform_is_running();
|
||||
}
|
||||
|
||||
game_kind game_get_kind(void) {
|
||||
return game_mode;
|
||||
}
|
||||
|
||||
void game_input() {
|
||||
if (game_mode != GAMEKIND_HEADLESS) {
|
||||
platform_input();
|
||||
}
|
||||
}
|
||||
|
||||
void game_update() {
|
||||
if (game_mode == GAMEKIND_CLIENT) {
|
||||
network_client_tick();
|
||||
}
|
||||
else {
|
||||
world_update();
|
||||
|
||||
if (game_mode == GAMEKIND_HEADLESS) {
|
||||
network_server_tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game_render() {
|
||||
if (game_mode != GAMEKIND_HEADLESS) {
|
||||
platform_render();
|
||||
}
|
||||
}
|
||||
|
||||
void game_action_send_keystate(game_keystate_data *data) {
|
||||
pkt_send_keystate_send(active_viewer->view_id, data);
|
||||
}
|
||||
|
||||
void game_request_close() {
|
||||
platform_request_close();
|
||||
}
|
|
@ -1,36 +1,36 @@
|
|||
#pragma once
|
||||
#include "system.h"
|
||||
#include "world_view.h"
|
||||
#include "packets/pkt_send_keystate.h"
|
||||
|
||||
typedef enum {
|
||||
GAMEKIND_SINGLE,
|
||||
GAMEKIND_CLIENT,
|
||||
GAMEKIND_HEADLESS,
|
||||
FORCE_GAMEKIND_UINT8 = UINT8_MAX
|
||||
} game_kind;
|
||||
|
||||
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_shutdown();
|
||||
void game_request_close();
|
||||
uint8_t game_is_running();
|
||||
int8_t game_is_networked();
|
||||
float game_time();
|
||||
game_kind game_get_kind(void);
|
||||
|
||||
//~ NOTE(zaklaus): game events
|
||||
void game_input();
|
||||
void game_update();
|
||||
void game_render();
|
||||
|
||||
//~ NOTE(zaklaus): world view management
|
||||
world_view *game_world_view_get_active(void);
|
||||
world_view *game_world_view_get(uint16_t idx);
|
||||
void game_world_view_set_active_by_idx(uint16_t idx);
|
||||
void game_world_view_set_active(world_view *view);
|
||||
void game_world_view_cycle_active(int8_t dir);
|
||||
void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value));
|
||||
entity_view *game_world_view_active_get_entity(uint64_t ent_id);
|
||||
|
||||
//~ NOTE(zaklaus): viewer -> host actions
|
||||
#pragma once
|
||||
#include "system.h"
|
||||
#include "world_view.h"
|
||||
#include "packets/pkt_send_keystate.h"
|
||||
|
||||
typedef enum {
|
||||
GAMEKIND_SINGLE,
|
||||
GAMEKIND_CLIENT,
|
||||
GAMEKIND_HEADLESS,
|
||||
FORCE_GAMEKIND_UINT8 = UINT8_MAX
|
||||
} game_kind;
|
||||
|
||||
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_shutdown();
|
||||
void game_request_close();
|
||||
uint8_t game_is_running();
|
||||
int8_t game_is_networked();
|
||||
float game_time();
|
||||
game_kind game_get_kind(void);
|
||||
|
||||
//~ NOTE(zaklaus): game events
|
||||
void game_input();
|
||||
void game_update();
|
||||
void game_render();
|
||||
|
||||
//~ NOTE(zaklaus): world view management
|
||||
world_view *game_world_view_get_active(void);
|
||||
world_view *game_world_view_get(uint16_t idx);
|
||||
void game_world_view_set_active_by_idx(uint16_t idx);
|
||||
void game_world_view_set_active(world_view *view);
|
||||
void game_world_view_cycle_active(int8_t dir);
|
||||
void game_world_view_active_entity_map(void (*map_proc)(uint64_t key, entity_view * value));
|
||||
entity_view *game_world_view_active_get_entity(uint64_t ent_id);
|
||||
|
||||
//~ NOTE(zaklaus): viewer -> host actions
|
||||
void game_action_send_keystate(game_keystate_data *data);
|
|
@ -1,189 +1,189 @@
|
|||
#include "zpl.h"
|
||||
|
||||
#define ENET_IMPLEMENTATION
|
||||
#include "enet.h"
|
||||
|
||||
#define LIBRG_IMPL
|
||||
#define LIBRG_CUSTOM_ZPL
|
||||
#define LIBRG_ENTITY_MAXCHUNKS 1
|
||||
#include "librg.h"
|
||||
|
||||
#include "network.h"
|
||||
#include "packet.h"
|
||||
#include "packets/pkt_00_init.h"
|
||||
#include "world/world.h"
|
||||
#include "game.h"
|
||||
#include "player.h"
|
||||
|
||||
#define NETWORK_UPDATE_DELAY 0.100
|
||||
|
||||
static ENetHost *host = NULL;
|
||||
static ENetHost *server = NULL;
|
||||
static ENetPeer *peer = NULL;
|
||||
static librg_world *world = NULL;
|
||||
|
||||
int32_t network_init() {
|
||||
return enet_initialize() != 0;
|
||||
}
|
||||
|
||||
int32_t network_destroy() {
|
||||
enet_deinitialize();
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
//~ NOTE(zaklaus): client
|
||||
|
||||
int32_t network_client_connect(const char *hostname, uint16_t port) {
|
||||
ENetAddress address = {0}; address.port = port;
|
||||
enet_address_set_host(&address, hostname);
|
||||
|
||||
host = enet_host_create(NULL, 1, 2, 0, 0);
|
||||
peer = enet_host_connect(host, &address, 2, 0);
|
||||
|
||||
if (peer == NULL) {
|
||||
zpl_printf("[ERROR] Cannot connect to specicied server: %s:%d\n", hostname, port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
world = librg_world_create();
|
||||
librg_world_userdata_set(world, peer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t network_client_disconnect() {
|
||||
enet_peer_disconnect_now(peer, 0);
|
||||
enet_host_destroy(host);
|
||||
|
||||
librg_world_destroy(world);
|
||||
|
||||
peer = NULL;
|
||||
host = NULL;
|
||||
world = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t network_client_tick() {
|
||||
ENetEvent event = {0};
|
||||
|
||||
while (enet_host_service(host, &event, 1) > 0) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT: {
|
||||
zpl_printf("[INFO] We connected to the server.\n");
|
||||
pkt_00_init_send(0);
|
||||
} break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: {
|
||||
zpl_printf("[INFO] We disconnected from server.\n");
|
||||
} break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE: {
|
||||
if (!world_read(event.packet->data, event.packet->dataLength, event.peer)) {
|
||||
zpl_printf("[INFO] Server sent us an unsupported packet.\n");
|
||||
}
|
||||
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy(event.packet);
|
||||
} break;
|
||||
|
||||
case ENET_EVENT_TYPE_NONE: break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool network_client_is_connected() {
|
||||
return peer ? enet_peer_get_state(peer) == ENET_PEER_STATE_CONNECTED : false;
|
||||
}
|
||||
|
||||
//~ NOTE(zaklaus): server
|
||||
|
||||
int32_t network_server_start(const char *host, uint16_t port) {
|
||||
(void)host;
|
||||
|
||||
ENetAddress address = {0};
|
||||
|
||||
address.host = ENET_HOST_ANY;
|
||||
address.port = port;
|
||||
|
||||
server = enet_host_create(&address, 8, 2, 0, 0);
|
||||
|
||||
if (server == NULL) {
|
||||
zpl_printf("[ERROR] An error occured while trying to create a server host.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t network_server_stop(void) {
|
||||
enet_host_destroy(server);
|
||||
server = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t network_server_tick(void) {
|
||||
ENetEvent event = {0};
|
||||
while (enet_host_service(server, &event, 1) > 0) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT: {
|
||||
zpl_printf("[INFO] A new user %d connected.\n", event.peer->incomingPeerID);
|
||||
} break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: {
|
||||
zpl_printf("[INFO] A user %d disconnected.\n", event.peer->incomingPeerID);
|
||||
|
||||
if (event.peer->data) {
|
||||
player_despawn((ecs_entity_t)event.peer->data);
|
||||
event.peer->data = 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE: {
|
||||
if (!world_read(event.packet->data, event.packet->dataLength, event.peer)) {
|
||||
zpl_printf("[INFO] User %d sent us a malformed packet.\n", event.peer->incomingPeerID);
|
||||
}
|
||||
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy(event.packet);
|
||||
} break;
|
||||
|
||||
case ENET_EVENT_TYPE_NONE: break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void network_server_assign_entity(void *peer_id, uint64_t ent_id) {
|
||||
ENetPeer *peer = (ENetPeer *)peer_id;
|
||||
peer->data = (void*)ent_id;
|
||||
}
|
||||
|
||||
uint64_t network_server_get_entity(void *peer_id) {
|
||||
if (game_get_kind() == GAMEKIND_SINGLE) {
|
||||
return (uint64_t)peer_id;
|
||||
}
|
||||
ENetPeer *peer = (ENetPeer *)peer_id;
|
||||
ZPL_ASSERT(peer->data);
|
||||
return (uint64_t)peer->data;
|
||||
}
|
||||
|
||||
//~ NOTE(zaklaus): messaging
|
||||
|
||||
static int32_t network_msg_send_raw(ENetPeer *peer_id, void *data, size_t datalen, uint32_t flags, uint16_t channel_id) {
|
||||
if (peer_id == 0) peer_id = peer;
|
||||
ENetPacket *packet = enet_packet_create(data, datalen, flags);
|
||||
return enet_peer_send(peer_id, channel_id, packet);
|
||||
}
|
||||
|
||||
int32_t network_msg_send(void *peer_id, void *data, size_t datalen, uint16_t channel_id) {
|
||||
return network_msg_send_raw(peer_id, data, datalen, ENET_PACKET_FLAG_RELIABLE, channel_id);
|
||||
}
|
||||
|
||||
int32_t network_msg_send_unreliable(void *peer_id, void *data, size_t datalen, uint16_t channel_id) {
|
||||
return network_msg_send_raw(peer_id, data, datalen, 0, channel_id);
|
||||
}
|
||||
#include "zpl.h"
|
||||
|
||||
#define ENET_IMPLEMENTATION
|
||||
#include "enet.h"
|
||||
|
||||
#define LIBRG_IMPL
|
||||
#define LIBRG_CUSTOM_ZPL
|
||||
#define LIBRG_ENTITY_MAXCHUNKS 1
|
||||
#include "librg.h"
|
||||
|
||||
#include "network.h"
|
||||
#include "packet.h"
|
||||
#include "packets/pkt_00_init.h"
|
||||
#include "world/world.h"
|
||||
#include "game.h"
|
||||
#include "player.h"
|
||||
|
||||
#define NETWORK_UPDATE_DELAY 0.100
|
||||
|
||||
static ENetHost *host = NULL;
|
||||
static ENetHost *server = NULL;
|
||||
static ENetPeer *peer = NULL;
|
||||
static librg_world *world = NULL;
|
||||
|
||||
int32_t network_init() {
|
||||
return enet_initialize() != 0;
|
||||
}
|
||||
|
||||
int32_t network_destroy() {
|
||||
enet_deinitialize();
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
//~ NOTE(zaklaus): client
|
||||
|
||||
int32_t network_client_connect(const char *hostname, uint16_t port) {
|
||||
ENetAddress address = {0}; address.port = port;
|
||||
enet_address_set_host(&address, hostname);
|
||||
|
||||
host = enet_host_create(NULL, 1, 2, 0, 0);
|
||||
peer = enet_host_connect(host, &address, 2, 0);
|
||||
|
||||
if (peer == NULL) {
|
||||
zpl_printf("[ERROR] Cannot connect to specicied server: %s:%d\n", hostname, port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
world = librg_world_create();
|
||||
librg_world_userdata_set(world, peer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t network_client_disconnect() {
|
||||
enet_peer_disconnect_now(peer, 0);
|
||||
enet_host_destroy(host);
|
||||
|
||||
librg_world_destroy(world);
|
||||
|
||||
peer = NULL;
|
||||
host = NULL;
|
||||
world = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t network_client_tick() {
|
||||
ENetEvent event = {0};
|
||||
|
||||
while (enet_host_service(host, &event, 1) > 0) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT: {
|
||||
zpl_printf("[INFO] We connected to the server.\n");
|
||||
pkt_00_init_send(0);
|
||||
} break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: {
|
||||
zpl_printf("[INFO] We disconnected from server.\n");
|
||||
} break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE: {
|
||||
if (!world_read(event.packet->data, event.packet->dataLength, event.peer)) {
|
||||
zpl_printf("[INFO] Server sent us an unsupported packet.\n");
|
||||
}
|
||||
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy(event.packet);
|
||||
} break;
|
||||
|
||||
case ENET_EVENT_TYPE_NONE: break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool network_client_is_connected() {
|
||||
return peer ? enet_peer_get_state(peer) == ENET_PEER_STATE_CONNECTED : false;
|
||||
}
|
||||
|
||||
//~ NOTE(zaklaus): server
|
||||
|
||||
int32_t network_server_start(const char *host, uint16_t port) {
|
||||
(void)host;
|
||||
|
||||
ENetAddress address = {0};
|
||||
|
||||
address.host = ENET_HOST_ANY;
|
||||
address.port = port;
|
||||
|
||||
server = enet_host_create(&address, 8, 2, 0, 0);
|
||||
|
||||
if (server == NULL) {
|
||||
zpl_printf("[ERROR] An error occured while trying to create a server host.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t network_server_stop(void) {
|
||||
enet_host_destroy(server);
|
||||
server = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t network_server_tick(void) {
|
||||
ENetEvent event = {0};
|
||||
while (enet_host_service(server, &event, 1) > 0) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT: {
|
||||
zpl_printf("[INFO] A new user %d connected.\n", event.peer->incomingPeerID);
|
||||
} break;
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: {
|
||||
zpl_printf("[INFO] A user %d disconnected.\n", event.peer->incomingPeerID);
|
||||
|
||||
if (event.peer->data) {
|
||||
player_despawn((ecs_entity_t)event.peer->data);
|
||||
event.peer->data = 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE: {
|
||||
if (!world_read(event.packet->data, event.packet->dataLength, event.peer)) {
|
||||
zpl_printf("[INFO] User %d sent us a malformed packet.\n", event.peer->incomingPeerID);
|
||||
}
|
||||
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy(event.packet);
|
||||
} break;
|
||||
|
||||
case ENET_EVENT_TYPE_NONE: break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void network_server_assign_entity(void *peer_id, uint64_t ent_id) {
|
||||
ENetPeer *peer = (ENetPeer *)peer_id;
|
||||
peer->data = (void*)ent_id;
|
||||
}
|
||||
|
||||
uint64_t network_server_get_entity(void *peer_id) {
|
||||
if (game_get_kind() == GAMEKIND_SINGLE) {
|
||||
return (uint64_t)peer_id;
|
||||
}
|
||||
ENetPeer *peer = (ENetPeer *)peer_id;
|
||||
ZPL_ASSERT(peer->data);
|
||||
return (uint64_t)peer->data;
|
||||
}
|
||||
|
||||
//~ NOTE(zaklaus): messaging
|
||||
|
||||
static int32_t network_msg_send_raw(ENetPeer *peer_id, void *data, size_t datalen, uint32_t flags, uint16_t channel_id) {
|
||||
if (peer_id == 0) peer_id = peer;
|
||||
ENetPacket *packet = enet_packet_create(data, datalen, flags);
|
||||
return enet_peer_send(peer_id, channel_id, packet);
|
||||
}
|
||||
|
||||
int32_t network_msg_send(void *peer_id, void *data, size_t datalen, uint16_t channel_id) {
|
||||
return network_msg_send_raw(peer_id, data, datalen, ENET_PACKET_FLAG_RELIABLE, channel_id);
|
||||
}
|
||||
|
||||
int32_t network_msg_send_unreliable(void *peer_id, void *data, size_t datalen, uint16_t channel_id) {
|
||||
return network_msg_send_raw(peer_id, data, datalen, 0, channel_id);
|
||||
}
|
||||
|
|
|
@ -1,71 +1,71 @@
|
|||
#include "packet_utils.h"
|
||||
#include "network.h"
|
||||
#include "packets/pkt_send_keystate.h"
|
||||
#include "modules/components.h"
|
||||
#include "modules/systems.h"
|
||||
#include "world/world.h"
|
||||
|
||||
#include "debug_replay.h"
|
||||
|
||||
pkt_desc pkt_send_keystate_desc[] = {
|
||||
{ PKT_REAL(pkt_send_keystate, x) },
|
||||
{ PKT_REAL(pkt_send_keystate, y) },
|
||||
{ PKT_REAL(pkt_send_keystate, mx) },
|
||||
{ PKT_REAL(pkt_send_keystate, my) },
|
||||
{ PKT_UINT(pkt_send_keystate, use) },
|
||||
{ PKT_UINT(pkt_send_keystate, sprint) },
|
||||
{ PKT_UINT(pkt_send_keystate, ctrl) },
|
||||
{ PKT_UINT(pkt_send_keystate, selected_item) },
|
||||
{ PKT_UINT(pkt_send_keystate, drop) },
|
||||
{ PKT_UINT(pkt_send_keystate, swap) },
|
||||
{ PKT_UINT(pkt_send_keystate, swap_from) },
|
||||
{ PKT_UINT(pkt_send_keystate, swap_to) },
|
||||
{ PKT_UINT(pkt_send_keystate, placement_num) },
|
||||
{ PKT_ARRAY(pkt_send_keystate, placements) },
|
||||
{ PKT_END },
|
||||
};
|
||||
|
||||
size_t pkt_send_keystate_send(uint16_t view_id,
|
||||
game_keystate_data *data) {
|
||||
return pkt_world_write(MSG_ID_SEND_KEYSTATE, pkt_send_keystate_encode(data), 1, view_id, NULL, 1);
|
||||
}
|
||||
|
||||
size_t pkt_send_keystate_encode(pkt_send_keystate *table) {
|
||||
cw_pack_context pc = {0};
|
||||
pkt_pack_msg(&pc, pkt_pack_desc_args(pkt_send_keystate_desc));
|
||||
pkt_pack_struct(&pc, pkt_send_keystate_desc, PKT_STRUCT_PTR(table));
|
||||
return pkt_pack_msg_size(&pc);
|
||||
}
|
||||
|
||||
int32_t pkt_send_keystate_handler(pkt_header *header) {
|
||||
pkt_send_keystate table;
|
||||
PKT_IF(pkt_msg_decode(header, pkt_send_keystate_desc, pkt_pack_desc_args(pkt_send_keystate_desc), PKT_STRUCT_PTR(&table)));
|
||||
ecs_entity_t e = network_server_get_entity(header->udata);
|
||||
|
||||
if (!world_entity_valid(e))
|
||||
return 1;
|
||||
|
||||
Input *i = ecs_get_mut(world_ecs(), e, Input, NULL);
|
||||
if (i && !i->is_blocked) {
|
||||
i->x = zpl_clamp(table.x, -1.0f, 1.0f);
|
||||
i->y = zpl_clamp(table.y, -1.0f, 1.0f);
|
||||
i->mx = table.mx;
|
||||
i->my = table.my;
|
||||
i->use = table.use;
|
||||
i->sprint = table.sprint;
|
||||
i->ctrl = table.ctrl;
|
||||
i->selected_item = zpl_clamp(table.selected_item, 0, ITEMS_INVENTORY_SIZE-1);
|
||||
i->drop = table.drop;
|
||||
i->swap = table.swap;
|
||||
i->swap_from = zpl_clamp(table.swap_from, 0, ITEMS_INVENTORY_SIZE-1);
|
||||
i->swap_to = zpl_clamp(table.swap_to, 0, ITEMS_INVENTORY_SIZE-1);
|
||||
i->num_placements = zpl_clamp(table.placement_num, 0, BUILD_MAX_PLACEMENTS);
|
||||
for (uint8_t j = 0; j < i->num_placements; j++) {
|
||||
i->placements_x[j] = table.placements[j].x;
|
||||
i->placements_y[j] = table.placements[j].y;
|
||||
}
|
||||
debug_replay_record_keystate(table);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#include "packet_utils.h"
|
||||
#include "network.h"
|
||||
#include "packets/pkt_send_keystate.h"
|
||||
#include "modules/components.h"
|
||||
#include "modules/systems.h"
|
||||
#include "world/world.h"
|
||||
|
||||
#include "debug_replay.h"
|
||||
|
||||
pkt_desc pkt_send_keystate_desc[] = {
|
||||
{ PKT_REAL(pkt_send_keystate, x) },
|
||||
{ PKT_REAL(pkt_send_keystate, y) },
|
||||
{ PKT_REAL(pkt_send_keystate, mx) },
|
||||
{ PKT_REAL(pkt_send_keystate, my) },
|
||||
{ PKT_UINT(pkt_send_keystate, use) },
|
||||
{ PKT_UINT(pkt_send_keystate, sprint) },
|
||||
{ PKT_UINT(pkt_send_keystate, ctrl) },
|
||||
{ PKT_UINT(pkt_send_keystate, selected_item) },
|
||||
{ PKT_UINT(pkt_send_keystate, drop) },
|
||||
{ PKT_UINT(pkt_send_keystate, swap) },
|
||||
{ PKT_UINT(pkt_send_keystate, swap_from) },
|
||||
{ PKT_UINT(pkt_send_keystate, swap_to) },
|
||||
{ PKT_UINT(pkt_send_keystate, placement_num) },
|
||||
{ PKT_ARRAY(pkt_send_keystate, placements) },
|
||||
{ PKT_END },
|
||||
};
|
||||
|
||||
size_t pkt_send_keystate_send(uint16_t view_id,
|
||||
game_keystate_data *data) {
|
||||
return pkt_world_write(MSG_ID_SEND_KEYSTATE, pkt_send_keystate_encode(data), 1, view_id, NULL, 1);
|
||||
}
|
||||
|
||||
size_t pkt_send_keystate_encode(pkt_send_keystate *table) {
|
||||
cw_pack_context pc = {0};
|
||||
pkt_pack_msg(&pc, pkt_pack_desc_args(pkt_send_keystate_desc));
|
||||
pkt_pack_struct(&pc, pkt_send_keystate_desc, PKT_STRUCT_PTR(table));
|
||||
return pkt_pack_msg_size(&pc);
|
||||
}
|
||||
|
||||
int32_t pkt_send_keystate_handler(pkt_header *header) {
|
||||
pkt_send_keystate table;
|
||||
PKT_IF(pkt_msg_decode(header, pkt_send_keystate_desc, pkt_pack_desc_args(pkt_send_keystate_desc), PKT_STRUCT_PTR(&table)));
|
||||
ecs_entity_t e = network_server_get_entity(header->udata);
|
||||
|
||||
if (!world_entity_valid(e))
|
||||
return 1;
|
||||
|
||||
Input *i = ecs_get_mut(world_ecs(), e, Input, NULL);
|
||||
if (i && !i->is_blocked) {
|
||||
i->x = zpl_clamp(table.x, -1.0f, 1.0f);
|
||||
i->y = zpl_clamp(table.y, -1.0f, 1.0f);
|
||||
i->mx = table.mx;
|
||||
i->my = table.my;
|
||||
i->use = table.use;
|
||||
i->sprint = table.sprint;
|
||||
i->ctrl = table.ctrl;
|
||||
i->selected_item = zpl_clamp(table.selected_item, 0, ITEMS_INVENTORY_SIZE-1);
|
||||
i->drop = table.drop;
|
||||
i->swap = table.swap;
|
||||
i->swap_from = zpl_clamp(table.swap_from, 0, ITEMS_INVENTORY_SIZE-1);
|
||||
i->swap_to = zpl_clamp(table.swap_to, 0, ITEMS_INVENTORY_SIZE-1);
|
||||
i->num_placements = zpl_clamp(table.placement_num, 0, BUILD_MAX_PLACEMENTS);
|
||||
for (uint8_t j = 0; j < i->num_placements; j++) {
|
||||
i->placements_x[j] = table.placements[j].x;
|
||||
i->placements_y[j] = table.placements[j].y;
|
||||
}
|
||||
debug_replay_record_keystate(table);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
#pragma once
|
||||
#include "system.h"
|
||||
#include "packet_utils.h"
|
||||
#include "item_placement.h"
|
||||
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float mx;
|
||||
float my;
|
||||
uint8_t use;
|
||||
uint8_t sprint;
|
||||
uint8_t ctrl;
|
||||
uint8_t selected_item;
|
||||
uint8_t drop;
|
||||
uint8_t swap;
|
||||
uint8_t swap_from;
|
||||
uint8_t swap_to;
|
||||
|
||||
// TODO(zaklaus): build mode
|
||||
uint8_t placement_num;
|
||||
item_placement placements[BUILD_MAX_PLACEMENTS];
|
||||
} pkt_send_keystate;
|
||||
|
||||
typedef pkt_send_keystate game_keystate_data;
|
||||
|
||||
size_t pkt_send_keystate_send(uint16_t view_id,
|
||||
game_keystate_data *data);
|
||||
size_t pkt_send_keystate_encode(pkt_send_keystate *table);
|
||||
extern pkt_desc pkt_send_keystate_desc[];
|
||||
|
||||
PKT_HANDLER_PROC(pkt_send_keystate_handler);
|
||||
|
||||
#pragma once
|
||||
#include "system.h"
|
||||
#include "packet_utils.h"
|
||||
#include "item_placement.h"
|
||||
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float mx;
|
||||
float my;
|
||||
uint8_t use;
|
||||
uint8_t sprint;
|
||||
uint8_t ctrl;
|
||||
uint8_t selected_item;
|
||||
uint8_t drop;
|
||||
uint8_t swap;
|
||||
uint8_t swap_from;
|
||||
uint8_t swap_to;
|
||||
|
||||
// TODO(zaklaus): build mode
|
||||
uint8_t placement_num;
|
||||
item_placement placements[BUILD_MAX_PLACEMENTS];
|
||||
} pkt_send_keystate;
|
||||
|
||||
typedef pkt_send_keystate game_keystate_data;
|
||||
|
||||
size_t pkt_send_keystate_send(uint16_t view_id,
|
||||
game_keystate_data *data);
|
||||
size_t pkt_send_keystate_encode(pkt_send_keystate *table);
|
||||
extern pkt_desc pkt_send_keystate_desc[];
|
||||
|
||||
PKT_HANDLER_PROC(pkt_send_keystate_handler);
|
||||
|
||||
|
|
|
@ -58,6 +58,29 @@ uint8_t platform_is_running() {
|
|||
return !WindowShouldClose();
|
||||
}
|
||||
|
||||
static game_keystate_data last_input_data = {0};
|
||||
|
||||
void platform_input_update_input_frame(game_keystate_data data) {
|
||||
// NOTE(zaklaus): Test if there are any changes
|
||||
if (data.x != last_input_data.x) goto send_data;
|
||||
if (data.y != last_input_data.y) goto send_data;
|
||||
if (data.use != last_input_data.use) goto send_data;
|
||||
if (data.sprint != last_input_data.sprint) goto send_data;
|
||||
if (data.ctrl != last_input_data.ctrl) goto send_data;
|
||||
if (data.selected_item != last_input_data.selected_item) goto send_data;
|
||||
if (data.drop != last_input_data.drop) goto send_data;
|
||||
if (data.swap != last_input_data.swap) goto send_data;
|
||||
if (data.swap_from != last_input_data.swap_from) goto send_data;
|
||||
if (data.swap_to != last_input_data.swap_to) goto send_data;
|
||||
if (data.placement_num != last_input_data.placement_num) goto send_data;
|
||||
if (zpl_memcompare(data.placements, last_input_data.placements, zpl_size_of(data.placements))) goto send_data;
|
||||
return;
|
||||
|
||||
send_data:
|
||||
last_input_data = data;
|
||||
game_action_send_keystate(&data);
|
||||
}
|
||||
|
||||
void platform_input() {
|
||||
float mouse_z = (GetMouseWheelMove()*0.5f);
|
||||
|
||||
|
@ -92,7 +115,7 @@ void platform_input() {
|
|||
y = -mouse_pos.y;
|
||||
}
|
||||
|
||||
game_keystate_data data = {
|
||||
game_keystate_data in_data = {
|
||||
.x = x,
|
||||
.y = y,
|
||||
.mx = mouse_pos.x,
|
||||
|
@ -111,11 +134,11 @@ void platform_input() {
|
|||
if (build_submit_placements) {
|
||||
build_submit_placements = false;
|
||||
|
||||
data.placement_num = build_num_placements;
|
||||
zpl_memcopy(data.placements, build_placements, build_num_placements*zpl_size_of(item_placement));
|
||||
in_data.placement_num = build_num_placements;
|
||||
zpl_memcopy(in_data.placements, build_placements, build_num_placements*zpl_size_of(item_placement));
|
||||
}
|
||||
|
||||
game_action_send_keystate(&data);
|
||||
platform_input_update_input_frame(in_data);
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): cycle through viewers
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
#include "player.h"
|
||||
#include "entity.h"
|
||||
#include "entity_view.h"
|
||||
#include "flecs/flecs.h"
|
||||
#include "flecs/flecs_meta.h"
|
||||
#include "librg.h"
|
||||
#include "world/world.h"
|
||||
|
||||
#include "modules/components.h"
|
||||
#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(EKIND_PLAYER);
|
||||
|
||||
if (!name) {
|
||||
name = zpl_bprintf("player_%d", e);
|
||||
}
|
||||
|
||||
ecs_set_name(world_ecs(), e, name);
|
||||
ecs_set(world_ecs(), e, ClientInfo, {0});
|
||||
ecs_set(world_ecs(), e, Input, {0});
|
||||
ecs_set(world_ecs(), e, Inventory, {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);
|
||||
|
||||
return (uint64_t)e;
|
||||
}
|
||||
|
||||
void player_despawn(uint64_t ent_id) {
|
||||
entity_despawn(ent_id);
|
||||
}
|
||||
#include "player.h"
|
||||
#include "entity.h"
|
||||
#include "entity_view.h"
|
||||
#include "flecs/flecs.h"
|
||||
#include "flecs/flecs_meta.h"
|
||||
#include "librg.h"
|
||||
#include "world/world.h"
|
||||
|
||||
#include "modules/components.h"
|
||||
#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(EKIND_PLAYER);
|
||||
|
||||
if (!name) {
|
||||
name = zpl_bprintf("player_%d", e);
|
||||
}
|
||||
|
||||
ecs_set_name(world_ecs(), e, name);
|
||||
ecs_set(world_ecs(), e, ClientInfo, {0});
|
||||
ecs_set(world_ecs(), e, Input, {0});
|
||||
ecs_set(world_ecs(), e, Inventory, {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);
|
||||
|
||||
return (uint64_t)e;
|
||||
}
|
||||
|
||||
void player_despawn(uint64_t ent_id) {
|
||||
entity_despawn(ent_id);
|
||||
}
|
||||
|
|
|
@ -1,120 +1,120 @@
|
|||
#include "zpl.h"
|
||||
#include "prediction.h"
|
||||
#include "platform.h"
|
||||
#include "world/world.h"
|
||||
#include "game.h"
|
||||
|
||||
#define PREDICT_SMOOTH_FACTOR_LO 7.5
|
||||
#define PREDICT_SMOOTH_FACTOR_HI 12.5
|
||||
|
||||
static inline float map_factor(float x) {
|
||||
x = 1.0f - zpl_clamp01(x);
|
||||
return 1.0f - x*x*x*x*x*x*x*x;
|
||||
}
|
||||
|
||||
static inline float base_angle(float x) {
|
||||
while (x > ZPL_TAU) x -= ZPL_TAU;
|
||||
while (x < 0.0f) x += ZPL_TAU;
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline float spherical_lerp(float a, float b, float t) {
|
||||
a = base_angle(a);
|
||||
b = base_angle(b);
|
||||
float d = b - a;
|
||||
|
||||
if (d < -ZPL_PI) {
|
||||
b += ZPL_TAU;
|
||||
} else if (d > ZPL_PI) {
|
||||
b -= ZPL_TAU;
|
||||
}
|
||||
|
||||
return base_angle(zpl_lerp(a, b, t));
|
||||
}
|
||||
|
||||
float smooth_val(float cur, float tgt, uint64_t dt) {
|
||||
float factor = zpl_clamp01(map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_MP_FAST_MS, WORLD_TRACKER_UPDATE_MP_SLOW_MS)));
|
||||
|
||||
#if 0
|
||||
dt = 200;
|
||||
factor = map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_MP_FAST_MS, WORLD_TRACKER_UPDATE_MP_SLOW_MS));
|
||||
zpl_printf("lerp factor: %f\n", factor);
|
||||
zpl_exit(0);
|
||||
#endif
|
||||
|
||||
return zpl_lerp(cur, tgt, zpl_clamp01(zpl_lerp(PREDICT_SMOOTH_FACTOR_LO, PREDICT_SMOOTH_FACTOR_HI, factor)*platform_frametime()));
|
||||
}
|
||||
|
||||
float smooth_val_spherical(float cur, float tgt, uint64_t dt) {
|
||||
float factor = zpl_clamp01(map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_MP_FAST_MS, WORLD_TRACKER_UPDATE_MP_SLOW_MS)));
|
||||
|
||||
return spherical_lerp(cur, tgt, zpl_clamp01(zpl_lerp(PREDICT_SMOOTH_FACTOR_LO, PREDICT_SMOOTH_FACTOR_HI, factor)*platform_frametime()));
|
||||
}
|
||||
|
||||
void predict_receive_update(entity_view *d, entity_view *data) {
|
||||
if (d && data->flag & EFLAG_INTERP) {
|
||||
// NOTE(zaklaus): store target pos but keep x,y unchanged
|
||||
float tx = data->x;
|
||||
float ty = data->y;
|
||||
float theading = data->heading;
|
||||
data->x = d->x;
|
||||
data->y = d->y;
|
||||
data->heading = d->heading;
|
||||
data->tx = tx;
|
||||
data->ty = ty;
|
||||
data->theading = theading;
|
||||
}
|
||||
|
||||
data->tran_effect = d->tran_effect;
|
||||
data->tran_time = d->tran_time;
|
||||
}
|
||||
|
||||
#define ENTITY_DO_LERP_SP 0
|
||||
|
||||
void lerp_entity_positions(uint64_t key, entity_view *data) {
|
||||
(void)key;
|
||||
world_view *view = game_world_view_get_active();
|
||||
|
||||
if (data->flag == EFLAG_INTERP) {
|
||||
#if ENTITY_DO_LERP_SP==0
|
||||
if (game_get_kind() == GAMEKIND_CLIENT)
|
||||
#else
|
||||
if (1)
|
||||
#endif
|
||||
{
|
||||
data->x = smooth_val(data->x, data->tx, view->delta_time[data->layer_id]);
|
||||
data->y = smooth_val(data->y, data->ty, view->delta_time[data->layer_id]);
|
||||
data->heading = smooth_val_spherical(data->heading, data->theading, view->delta_time[data->layer_id]);
|
||||
} else {
|
||||
(void)view;
|
||||
data->x = data->tx;
|
||||
data->y = data->ty;
|
||||
data->heading = data->theading;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_entity_fadeinout(uint64_t key, entity_view * data) {
|
||||
(void)key;
|
||||
switch (data->tran_effect) {
|
||||
case ETRAN_FADEIN: {
|
||||
data->tran_time += platform_frametime();
|
||||
|
||||
if (data->tran_time > 1.0f) {
|
||||
data->tran_effect = ETRAN_NONE;
|
||||
data->tran_time = 1.0f;
|
||||
}
|
||||
}break;
|
||||
|
||||
case ETRAN_FADEOUT: {
|
||||
data->tran_time -= platform_frametime();
|
||||
|
||||
if (data->tran_time < 0.0f) {
|
||||
data->tran_effect = ETRAN_REMOVE;
|
||||
data->tran_time = 0.0f;
|
||||
}
|
||||
}break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
#include "zpl.h"
|
||||
#include "prediction.h"
|
||||
#include "platform.h"
|
||||
#include "world/world.h"
|
||||
#include "game.h"
|
||||
|
||||
#define PREDICT_SMOOTH_FACTOR_LO 7.5
|
||||
#define PREDICT_SMOOTH_FACTOR_HI 12.5
|
||||
|
||||
static inline float map_factor(float x) {
|
||||
x = 1.0f - zpl_clamp01(x);
|
||||
return 1.0f - x*x*x*x*x*x*x*x;
|
||||
}
|
||||
|
||||
static inline float base_angle(float x) {
|
||||
while (x > ZPL_TAU) x -= ZPL_TAU;
|
||||
while (x < 0.0f) x += ZPL_TAU;
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline float spherical_lerp(float a, float b, float t) {
|
||||
a = base_angle(a);
|
||||
b = base_angle(b);
|
||||
float d = b - a;
|
||||
|
||||
if (d < -ZPL_PI) {
|
||||
b += ZPL_TAU;
|
||||
} else if (d > ZPL_PI) {
|
||||
b -= ZPL_TAU;
|
||||
}
|
||||
|
||||
return base_angle(zpl_lerp(a, b, t));
|
||||
}
|
||||
|
||||
float smooth_val(float cur, float tgt, uint64_t dt) {
|
||||
float factor = zpl_clamp01(map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_MP_FAST_MS, WORLD_TRACKER_UPDATE_MP_SLOW_MS)));
|
||||
|
||||
#if 0
|
||||
dt = 200;
|
||||
factor = map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_MP_FAST_MS, WORLD_TRACKER_UPDATE_MP_SLOW_MS));
|
||||
zpl_printf("lerp factor: %f\n", factor);
|
||||
zpl_exit(0);
|
||||
#endif
|
||||
|
||||
return zpl_lerp(cur, tgt, zpl_clamp01(zpl_lerp(PREDICT_SMOOTH_FACTOR_LO, PREDICT_SMOOTH_FACTOR_HI, factor)*platform_frametime()));
|
||||
}
|
||||
|
||||
float smooth_val_spherical(float cur, float tgt, uint64_t dt) {
|
||||
float factor = zpl_clamp01(map_factor(zpl_unlerp(dt, WORLD_TRACKER_UPDATE_MP_FAST_MS, WORLD_TRACKER_UPDATE_MP_SLOW_MS)));
|
||||
|
||||
return spherical_lerp(cur, tgt, zpl_clamp01(zpl_lerp(PREDICT_SMOOTH_FACTOR_LO, PREDICT_SMOOTH_FACTOR_HI, factor)*platform_frametime()));
|
||||
}
|
||||
|
||||
void predict_receive_update(entity_view *d, entity_view *data) {
|
||||
if (d && data->flag & EFLAG_INTERP) {
|
||||
// NOTE(zaklaus): store target pos but keep x,y unchanged
|
||||
float tx = data->x;
|
||||
float ty = data->y;
|
||||
float theading = data->heading;
|
||||
data->x = d->x;
|
||||
data->y = d->y;
|
||||
data->heading = d->heading;
|
||||
data->tx = tx;
|
||||
data->ty = ty;
|
||||
data->theading = theading;
|
||||
}
|
||||
|
||||
data->tran_effect = d->tran_effect;
|
||||
data->tran_time = d->tran_time;
|
||||
}
|
||||
|
||||
#define ENTITY_DO_LERP_SP 0
|
||||
|
||||
void lerp_entity_positions(uint64_t key, entity_view *data) {
|
||||
(void)key;
|
||||
world_view *view = game_world_view_get_active();
|
||||
|
||||
if (data->flag == EFLAG_INTERP) {
|
||||
#if ENTITY_DO_LERP_SP==0
|
||||
if (game_get_kind() == GAMEKIND_CLIENT)
|
||||
#else
|
||||
if (1)
|
||||
#endif
|
||||
{
|
||||
data->x = smooth_val(data->x, data->tx, view->delta_time[data->layer_id]);
|
||||
data->y = smooth_val(data->y, data->ty, view->delta_time[data->layer_id]);
|
||||
data->heading = smooth_val_spherical(data->heading, data->theading, view->delta_time[data->layer_id]);
|
||||
} else {
|
||||
(void)view;
|
||||
data->x = data->tx;
|
||||
data->y = data->ty;
|
||||
data->heading = data->theading;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_entity_fadeinout(uint64_t key, entity_view * data) {
|
||||
(void)key;
|
||||
switch (data->tran_effect) {
|
||||
case ETRAN_FADEIN: {
|
||||
data->tran_time += platform_frametime();
|
||||
|
||||
if (data->tran_time > 1.0f) {
|
||||
data->tran_effect = ETRAN_NONE;
|
||||
data->tran_time = 1.0f;
|
||||
}
|
||||
}break;
|
||||
|
||||
case ETRAN_FADEOUT: {
|
||||
data->tran_time -= platform_frametime();
|
||||
|
||||
if (data->tran_time < 0.0f) {
|
||||
data->tran_effect = ETRAN_REMOVE;
|
||||
data->tran_time = 0.0f;
|
||||
}
|
||||
}break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,61 +1,61 @@
|
|||
#include "profiler.h"
|
||||
#include "raylib.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define PROF_COLLATE_WINDOW 0.5
|
||||
|
||||
// NOTE(zaklaus): KEEP ORDER IN SYNC WITH profiler_kind ENUM !!!
|
||||
static profiler profilers[] = {
|
||||
{ .id = PROF_TOTAL_TIME, .name = "measured time" },
|
||||
{ .id = PROF_MAIN_LOOP, .name = "main loop" },
|
||||
{ .id = PROF_WORLD_WRITE, .name = "world write" },
|
||||
{ .id = PROF_RENDER, .name = "render" },
|
||||
{ .id = PROF_UPDATE_SYSTEMS, .name = "update systems" },
|
||||
{ .id = PROF_ENTITY_LERP, .name = "entity lerp" },
|
||||
{ .id = PROF_INTEGRATE_POS, .name = "entity movement" },
|
||||
};
|
||||
|
||||
static_assert((sizeof(profilers)/sizeof(profilers[0])) == MAX_PROF, "mismatched profilers");
|
||||
|
||||
void profiler_reset(profiler_kind id) {
|
||||
profilers[id].num_invocations = 0;
|
||||
profilers[id].total_time = 0.0;
|
||||
}
|
||||
|
||||
void profiler_start(profiler_kind id) {
|
||||
profilers[id].start_time = GetTime();
|
||||
}
|
||||
|
||||
void profiler_stop(profiler_kind id) {
|
||||
profilers[id].num_invocations += 1;
|
||||
profilers[id].total_time += GetTime() - profilers[id].start_time;
|
||||
profilers[id].start_time = 0.0;
|
||||
}
|
||||
|
||||
void profiler_collate() {
|
||||
static double frame_counter = 0.0;
|
||||
static uint64_t frames = 0;
|
||||
|
||||
frame_counter += GetFrameTime();
|
||||
frames++;
|
||||
|
||||
if (frame_counter >= PROF_COLLATE_WINDOW) {
|
||||
profilers[PROF_TOTAL_TIME].delta_time = frame_counter / (double)frames;
|
||||
|
||||
for (uint32_t i = PROF_MAIN_LOOP; i < MAX_PROF; i += 1) {
|
||||
profiler *p = &profilers[i];
|
||||
p->delta_time = p->num_invocations == 0 ? 0.0 : p->total_time / (double)p->num_invocations;
|
||||
}
|
||||
|
||||
frame_counter = 0.0;
|
||||
frames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
double profiler_delta(profiler_kind id) {
|
||||
return profilers[id].delta_time;
|
||||
}
|
||||
|
||||
char const *profiler_name(profiler_kind id) {
|
||||
return profilers[id].name;
|
||||
}
|
||||
#include "profiler.h"
|
||||
#include "raylib.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define PROF_COLLATE_WINDOW 0.5
|
||||
|
||||
// NOTE(zaklaus): KEEP ORDER IN SYNC WITH profiler_kind ENUM !!!
|
||||
static profiler profilers[] = {
|
||||
{ .id = PROF_TOTAL_TIME, .name = "measured time" },
|
||||
{ .id = PROF_MAIN_LOOP, .name = "main loop" },
|
||||
{ .id = PROF_WORLD_WRITE, .name = "world write" },
|
||||
{ .id = PROF_RENDER, .name = "render" },
|
||||
{ .id = PROF_UPDATE_SYSTEMS, .name = "update systems" },
|
||||
{ .id = PROF_ENTITY_LERP, .name = "entity lerp" },
|
||||
{ .id = PROF_INTEGRATE_POS, .name = "entity movement" },
|
||||
};
|
||||
|
||||
static_assert((sizeof(profilers)/sizeof(profilers[0])) == MAX_PROF, "mismatched profilers");
|
||||
|
||||
void profiler_reset(profiler_kind id) {
|
||||
profilers[id].num_invocations = 0;
|
||||
profilers[id].total_time = 0.0;
|
||||
}
|
||||
|
||||
void profiler_start(profiler_kind id) {
|
||||
profilers[id].start_time = GetTime();
|
||||
}
|
||||
|
||||
void profiler_stop(profiler_kind id) {
|
||||
profilers[id].num_invocations += 1;
|
||||
profilers[id].total_time += GetTime() - profilers[id].start_time;
|
||||
profilers[id].start_time = 0.0;
|
||||
}
|
||||
|
||||
void profiler_collate() {
|
||||
static double frame_counter = 0.0;
|
||||
static uint64_t frames = 0;
|
||||
|
||||
frame_counter += GetFrameTime();
|
||||
frames++;
|
||||
|
||||
if (frame_counter >= PROF_COLLATE_WINDOW) {
|
||||
profilers[PROF_TOTAL_TIME].delta_time = frame_counter / (double)frames;
|
||||
|
||||
for (uint32_t i = PROF_MAIN_LOOP; i < MAX_PROF; i += 1) {
|
||||
profiler *p = &profilers[i];
|
||||
p->delta_time = p->num_invocations == 0 ? 0.0 : p->total_time / (double)p->num_invocations;
|
||||
}
|
||||
|
||||
frame_counter = 0.0;
|
||||
frames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
double profiler_delta(profiler_kind id) {
|
||||
return profilers[id].delta_time;
|
||||
}
|
||||
|
||||
char const *profiler_name(profiler_kind id) {
|
||||
return profilers[id].name;
|
||||
}
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
#pragma once
|
||||
#include "system.h"
|
||||
|
||||
typedef enum {
|
||||
PROF_TOTAL_TIME,
|
||||
PROF_MAIN_LOOP,
|
||||
|
||||
PROF_WORLD_WRITE,
|
||||
PROF_RENDER,
|
||||
PROF_UPDATE_SYSTEMS,
|
||||
PROF_ENTITY_LERP,
|
||||
PROF_INTEGRATE_POS,
|
||||
|
||||
MAX_PROF,
|
||||
PROF_FORCE_UINT8 = UINT8_MAX
|
||||
} profiler_kind;
|
||||
|
||||
typedef struct {
|
||||
profiler_kind id;
|
||||
char const *name;
|
||||
|
||||
uint32_t num_invocations;
|
||||
double start_time;
|
||||
double delta_time;
|
||||
double total_time;
|
||||
} profiler;
|
||||
|
||||
void profiler_reset(profiler_kind id);
|
||||
void profiler_start(profiler_kind id);
|
||||
void profiler_stop(profiler_kind id);
|
||||
void profiler_collate(void);
|
||||
|
||||
double profiler_delta(profiler_kind id);
|
||||
char const *profiler_name(profiler_kind id);
|
||||
|
||||
#define profile(id) defer(profiler_start(id), profiler_stop(id))
|
||||
#pragma once
|
||||
#include "system.h"
|
||||
|
||||
typedef enum {
|
||||
PROF_TOTAL_TIME,
|
||||
PROF_MAIN_LOOP,
|
||||
|
||||
PROF_WORLD_WRITE,
|
||||
PROF_RENDER,
|
||||
PROF_UPDATE_SYSTEMS,
|
||||
PROF_ENTITY_LERP,
|
||||
PROF_INTEGRATE_POS,
|
||||
|
||||
MAX_PROF,
|
||||
PROF_FORCE_UINT8 = UINT8_MAX
|
||||
} profiler_kind;
|
||||
|
||||
typedef struct {
|
||||
profiler_kind id;
|
||||
char const *name;
|
||||
|
||||
uint32_t num_invocations;
|
||||
double start_time;
|
||||
double delta_time;
|
||||
double total_time;
|
||||
} profiler;
|
||||
|
||||
void profiler_reset(profiler_kind id);
|
||||
void profiler_start(profiler_kind id);
|
||||
void profiler_stop(profiler_kind id);
|
||||
void profiler_collate(void);
|
||||
|
||||
double profiler_delta(profiler_kind id);
|
||||
char const *profiler_name(profiler_kind id);
|
||||
|
||||
#define profile(id) defer(profiler_start(id), profiler_stop(id))
|
||||
|
|
|
@ -1,209 +1,209 @@
|
|||
static Camera2D render_camera;
|
||||
static float zoom_overlay_tran = 0.0f;
|
||||
|
||||
#define CAM_OVERLAY_ZOOM_LEVEL 0.80f
|
||||
#define ALPHA(x) ColorAlpha(x, data->tran_time)
|
||||
|
||||
float zpl_lerp(float,float,float);
|
||||
float zpl_to_degrees(float);
|
||||
|
||||
void DEBUG_draw_ground(uint64_t key, entity_view * data) {
|
||||
(void)key;
|
||||
switch (data->kind) {
|
||||
case EKIND_CHUNK: {
|
||||
world_view *view = game_world_view_get_active();
|
||||
int32_t size = view->chunk_size * WORLD_BLOCK_SIZE;
|
||||
int16_t offset = 0;
|
||||
|
||||
float x = data->x * size + offset;
|
||||
float y = data->y * size + offset;
|
||||
|
||||
RenderTexture2D tex = GetChunkTexture(key);
|
||||
float scale = (size)/(float)(tex.texture.width);
|
||||
tex.texture.width *= scale;
|
||||
tex.texture.height *= scale;
|
||||
DrawTextureRec(tex.texture, (Rectangle){0, 0, size, -size}, (Vector2){x, y}, ColorAlpha(WHITE, data->tran_time));
|
||||
|
||||
if (zoom_overlay_tran > 0.02f) {
|
||||
DrawRectangleEco(x, y, size-offset, size-offset, ColorAlpha(ColorFromHSV(data->color, 0.13f, 0.89f), data->tran_time*zoom_overlay_tran*0.75f));
|
||||
|
||||
DrawTextEco(TextFormat("%d %d", (int)data->x, (int)data->y), (int16_t)x+15, (int16_t)y+15, 200 , ColorAlpha(BLACK, data->tran_time*zoom_overlay_tran), 0.0);
|
||||
|
||||
}
|
||||
}break;
|
||||
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
|
||||
extern bool inv_is_open;
|
||||
|
||||
void DEBUG_draw_entities(uint64_t key, entity_view * data) {
|
||||
uint16_t size = 16;
|
||||
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 = 8;
|
||||
|
||||
switch (data->kind) {
|
||||
case EKIND_DEMO_NPC: {
|
||||
float x = data->x;
|
||||
float y = data->y;
|
||||
DrawCircleEco(x, y, size, ColorAlpha(BLUE, data->tran_time));
|
||||
}break;
|
||||
case EKIND_PLAYER: {
|
||||
float x = data->x;
|
||||
float y = data->y;
|
||||
float health = (data->hp / data->max_hp);
|
||||
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);
|
||||
DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time));
|
||||
|
||||
if (data->has_items && !data->inside_vehicle) {
|
||||
float ix = data->x;
|
||||
float iy = data->y;
|
||||
if (data->items[data->selected_item].quantity > 0) {
|
||||
item_kind it_kind = data->items[data->selected_item].kind;
|
||||
uint32_t qty = data->items[data->selected_item].quantity;
|
||||
uint16_t it_id = item_find(it_kind);
|
||||
DrawTexturePro(GetSpriteTexture2D(assets_find(item_get_asset(it_id))), ASSET_SRC_RECT(), ((Rectangle){ix, iy, 32, 32}), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
|
||||
|
||||
if (!inv_is_open)
|
||||
DrawTextEco(zpl_bprintf("%d", qty), ix+24, iy+24, 8, RAYWHITE, 0.0f);
|
||||
}
|
||||
}
|
||||
}break;
|
||||
case EKIND_MACRO_BOT: {
|
||||
float x = data->x;
|
||||
float y = data->y;
|
||||
const char *title = TextFormat("Bot %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(GRAY, data->tran_time));
|
||||
DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(BLACK, data->tran_time), font_spacing);
|
||||
DrawCircleEco(x, y, size, ColorAlpha(PURPLE, data->tran_time));
|
||||
}break;
|
||||
case EKIND_ITEM: {
|
||||
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));
|
||||
DrawTextEco(zpl_bprintf("%d", data->quantity), x, y, 10, ALPHA(RAYWHITE), 0.0f);
|
||||
}break;
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
|
||||
void DEBUG_draw_entities_low(uint64_t key, entity_view * data) {
|
||||
(void)key;
|
||||
|
||||
switch (data->kind) {
|
||||
case EKIND_VEHICLE: {
|
||||
float x = data->x;
|
||||
float y = data->y;
|
||||
float const w = 80;
|
||||
float const h = 50;
|
||||
DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, zpl_to_degrees(data->heading), ColorAlpha(RED, data->tran_time));
|
||||
}break;
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
|
||||
void renderer_draw_v0(void) {
|
||||
render_camera.zoom = zpl_lerp(render_camera.zoom, target_zoom, GetFrameTime()*2.9978f);
|
||||
camera_update();
|
||||
|
||||
camera game_camera = camera_get();
|
||||
render_camera.target = (Vector2){game_camera.x, game_camera.y};
|
||||
zoom_overlay_tran = zpl_lerp(zoom_overlay_tran, (target_zoom <= CAM_OVERLAY_ZOOM_LEVEL) ? 1.0f : 0.0f, GetFrameTime()*2.0f);
|
||||
|
||||
|
||||
ClearBackground(GetColor(0x222034));
|
||||
BeginMode2D(render_camera);
|
||||
game_world_view_active_entity_map(DEBUG_draw_ground);
|
||||
game_world_view_active_entity_map(DEBUG_draw_entities_low);
|
||||
game_world_view_active_entity_map(DEBUG_draw_entities);
|
||||
EndMode2D();
|
||||
}
|
||||
|
||||
float renderer_zoom_get_v0(void) {
|
||||
return render_camera.zoom;
|
||||
}
|
||||
|
||||
void renderer_init_v0(void) {
|
||||
render_camera.target = (Vector2){0.0f,0.0f};
|
||||
render_camera.offset = (Vector2){screenWidth >> 1, screenHeight >> 1};
|
||||
render_camera.rotation = 0.0f;
|
||||
render_camera.zoom = 1.5f;
|
||||
|
||||
// NOTE(zaklaus): Paint the screen before we load the game
|
||||
// TODO(zaklaus): Render a cool loading screen background maybe? :wink: :wink:
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(GetColor(0x222034));
|
||||
|
||||
char const *loading_text = "zpl.eco2d is loading...";
|
||||
int text_w = MeasureText(loading_text, 120);
|
||||
DrawText(loading_text, GetScreenWidth()-text_w-15, GetScreenHeight()-135, 120, RAYWHITE);
|
||||
EndDrawing();
|
||||
|
||||
blocks_setup();
|
||||
assets_setup();
|
||||
}
|
||||
|
||||
void renderer_shutdown_v0(void) {
|
||||
blocks_destroy();
|
||||
assets_destroy();
|
||||
}
|
||||
|
||||
void renderer_debug_draw_v0(void) {
|
||||
BeginMode2D(render_camera);
|
||||
debug_draw_queue *que = debug_draw_samples();
|
||||
|
||||
for (size_t i = 0; i < que->num_entries; i += 1) {
|
||||
debug_draw_entry *e = &que->entries[i];
|
||||
Color color = GetColor(e->color);
|
||||
|
||||
switch (e->kind) {
|
||||
case DDRAW_LINE: {
|
||||
float x = e->a.x;
|
||||
float y = e->a.y;
|
||||
float x2 = e->b.x;
|
||||
float y2 = e->b.y;
|
||||
DrawLineV((Vector2){x, y}, (Vector2){x2, y2}, color);
|
||||
}break;
|
||||
|
||||
case DDRAW_CIRCLE:{
|
||||
float x = e->a.x;
|
||||
float y = e->a.y;
|
||||
DrawCircleLinesEco(x, y, e->radius, color);
|
||||
}break;
|
||||
|
||||
case DDRAW_RECT:{
|
||||
float x = e->bmin.x;
|
||||
float y = e->bmin.y;
|
||||
float w = e->bmax.x - e->bmin.x;
|
||||
float h = e->bmax.y - e->bmin.y;
|
||||
DrawRectangleLinesEco(x, y, w, h, color);
|
||||
}break;
|
||||
|
||||
default: {
|
||||
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
EndMode2D();
|
||||
}
|
||||
|
||||
void renderer_draw_single_v0(float x, float y, asset_id id, Color color) {
|
||||
BeginMode2D(render_camera);
|
||||
|
||||
x -= 32.0f;
|
||||
y -= 32.0f;
|
||||
|
||||
DrawTexturePro(GetSpriteTexture2D(assets_find(id)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, color);
|
||||
|
||||
EndMode2D();
|
||||
}
|
||||
static Camera2D render_camera;
|
||||
static float zoom_overlay_tran = 0.0f;
|
||||
|
||||
#define CAM_OVERLAY_ZOOM_LEVEL 0.80f
|
||||
#define ALPHA(x) ColorAlpha(x, data->tran_time)
|
||||
|
||||
float zpl_lerp(float,float,float);
|
||||
float zpl_to_degrees(float);
|
||||
|
||||
void DEBUG_draw_ground(uint64_t key, entity_view * data) {
|
||||
(void)key;
|
||||
switch (data->kind) {
|
||||
case EKIND_CHUNK: {
|
||||
world_view *view = game_world_view_get_active();
|
||||
int32_t size = view->chunk_size * WORLD_BLOCK_SIZE;
|
||||
int16_t offset = 0;
|
||||
|
||||
float x = data->x * size + offset;
|
||||
float y = data->y * size + offset;
|
||||
|
||||
RenderTexture2D tex = GetChunkTexture(key);
|
||||
float scale = (size)/(float)(tex.texture.width);
|
||||
tex.texture.width *= scale;
|
||||
tex.texture.height *= scale;
|
||||
DrawTextureRec(tex.texture, (Rectangle){0, 0, size, -size}, (Vector2){x, y}, ColorAlpha(WHITE, data->tran_time));
|
||||
|
||||
if (zoom_overlay_tran > 0.02f) {
|
||||
DrawRectangleEco(x, y, size-offset, size-offset, ColorAlpha(ColorFromHSV(data->color, 0.13f, 0.89f), data->tran_time*zoom_overlay_tran*0.75f));
|
||||
|
||||
DrawTextEco(TextFormat("%d %d", (int)data->x, (int)data->y), (int16_t)x+15, (int16_t)y+15, 200 , ColorAlpha(BLACK, data->tran_time*zoom_overlay_tran), 0.0);
|
||||
|
||||
}
|
||||
}break;
|
||||
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
|
||||
extern bool inv_is_open;
|
||||
|
||||
void DEBUG_draw_entities(uint64_t key, entity_view * data) {
|
||||
uint16_t size = 16;
|
||||
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 = 8;
|
||||
|
||||
switch (data->kind) {
|
||||
case EKIND_DEMO_NPC: {
|
||||
float x = data->x;
|
||||
float y = data->y;
|
||||
DrawCircleEco(x, y, size, ColorAlpha(BLUE, data->tran_time));
|
||||
}break;
|
||||
case EKIND_PLAYER: {
|
||||
float x = data->x;
|
||||
float y = data->y;
|
||||
float health = (data->hp / data->max_hp);
|
||||
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);
|
||||
DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time));
|
||||
|
||||
if (data->has_items && !data->inside_vehicle) {
|
||||
float ix = data->x;
|
||||
float iy = data->y;
|
||||
if (data->items[data->selected_item].quantity > 0) {
|
||||
item_kind it_kind = data->items[data->selected_item].kind;
|
||||
uint32_t qty = data->items[data->selected_item].quantity;
|
||||
uint16_t it_id = item_find(it_kind);
|
||||
DrawTexturePro(GetSpriteTexture2D(assets_find(item_get_asset(it_id))), ASSET_SRC_RECT(), ((Rectangle){ix, iy, 32, 32}), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
|
||||
|
||||
if (!inv_is_open)
|
||||
DrawTextEco(zpl_bprintf("%d", qty), ix+24, iy+24, 8, RAYWHITE, 0.0f);
|
||||
}
|
||||
}
|
||||
}break;
|
||||
case EKIND_MACRO_BOT: {
|
||||
float x = data->x;
|
||||
float y = data->y;
|
||||
const char *title = TextFormat("Bot %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(GRAY, data->tran_time));
|
||||
DrawTextEco(title, x-title_w/2, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(BLACK, data->tran_time), font_spacing);
|
||||
DrawCircleEco(x, y, size, ColorAlpha(PURPLE, data->tran_time));
|
||||
}break;
|
||||
case EKIND_ITEM: {
|
||||
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));
|
||||
DrawTextEco(zpl_bprintf("%d", data->quantity), x, y, 10, ALPHA(RAYWHITE), 0.0f);
|
||||
}break;
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
|
||||
void DEBUG_draw_entities_low(uint64_t key, entity_view * data) {
|
||||
(void)key;
|
||||
|
||||
switch (data->kind) {
|
||||
case EKIND_VEHICLE: {
|
||||
float x = data->x;
|
||||
float y = data->y;
|
||||
float const w = 80;
|
||||
float const h = 50;
|
||||
DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, zpl_to_degrees(data->heading), ColorAlpha(RED, data->tran_time));
|
||||
}break;
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
|
||||
void renderer_draw_v0(void) {
|
||||
render_camera.zoom = zpl_lerp(render_camera.zoom, target_zoom, GetFrameTime()*2.9978f);
|
||||
camera_update();
|
||||
|
||||
camera game_camera = camera_get();
|
||||
render_camera.target = (Vector2){game_camera.x, game_camera.y};
|
||||
zoom_overlay_tran = zpl_lerp(zoom_overlay_tran, (target_zoom <= CAM_OVERLAY_ZOOM_LEVEL) ? 1.0f : 0.0f, GetFrameTime()*2.0f);
|
||||
|
||||
|
||||
ClearBackground(GetColor(0x222034));
|
||||
BeginMode2D(render_camera);
|
||||
game_world_view_active_entity_map(DEBUG_draw_ground);
|
||||
game_world_view_active_entity_map(DEBUG_draw_entities_low);
|
||||
game_world_view_active_entity_map(DEBUG_draw_entities);
|
||||
EndMode2D();
|
||||
}
|
||||
|
||||
float renderer_zoom_get_v0(void) {
|
||||
return render_camera.zoom;
|
||||
}
|
||||
|
||||
void renderer_init_v0(void) {
|
||||
render_camera.target = (Vector2){0.0f,0.0f};
|
||||
render_camera.offset = (Vector2){screenWidth >> 1, screenHeight >> 1};
|
||||
render_camera.rotation = 0.0f;
|
||||
render_camera.zoom = 1.5f;
|
||||
|
||||
// NOTE(zaklaus): Paint the screen before we load the game
|
||||
// TODO(zaklaus): Render a cool loading screen background maybe? :wink: :wink:
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(GetColor(0x222034));
|
||||
|
||||
char const *loading_text = "zpl.eco2d is loading...";
|
||||
int text_w = MeasureText(loading_text, 120);
|
||||
DrawText(loading_text, GetScreenWidth()-text_w-15, GetScreenHeight()-135, 120, RAYWHITE);
|
||||
EndDrawing();
|
||||
|
||||
blocks_setup();
|
||||
assets_setup();
|
||||
}
|
||||
|
||||
void renderer_shutdown_v0(void) {
|
||||
blocks_destroy();
|
||||
assets_destroy();
|
||||
}
|
||||
|
||||
void renderer_debug_draw_v0(void) {
|
||||
BeginMode2D(render_camera);
|
||||
debug_draw_queue *que = debug_draw_samples();
|
||||
|
||||
for (size_t i = 0; i < que->num_entries; i += 1) {
|
||||
debug_draw_entry *e = &que->entries[i];
|
||||
Color color = GetColor(e->color);
|
||||
|
||||
switch (e->kind) {
|
||||
case DDRAW_LINE: {
|
||||
float x = e->a.x;
|
||||
float y = e->a.y;
|
||||
float x2 = e->b.x;
|
||||
float y2 = e->b.y;
|
||||
DrawLineV((Vector2){x, y}, (Vector2){x2, y2}, color);
|
||||
}break;
|
||||
|
||||
case DDRAW_CIRCLE:{
|
||||
float x = e->a.x;
|
||||
float y = e->a.y;
|
||||
DrawCircleLinesEco(x, y, e->radius, color);
|
||||
}break;
|
||||
|
||||
case DDRAW_RECT:{
|
||||
float x = e->bmin.x;
|
||||
float y = e->bmin.y;
|
||||
float w = e->bmax.x - e->bmin.x;
|
||||
float h = e->bmax.y - e->bmin.y;
|
||||
DrawRectangleLinesEco(x, y, w, h, color);
|
||||
}break;
|
||||
|
||||
default: {
|
||||
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
EndMode2D();
|
||||
}
|
||||
|
||||
void renderer_draw_single_v0(float x, float y, asset_id id, Color color) {
|
||||
BeginMode2D(render_camera);
|
||||
|
||||
x -= 32.0f;
|
||||
y -= 32.0f;
|
||||
|
||||
DrawTexturePro(GetSpriteTexture2D(assets_find(id)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, color);
|
||||
|
||||
EndMode2D();
|
||||
}
|
||||
|
|
|
@ -1,377 +1,377 @@
|
|||
#pragma once
|
||||
#include "system.h"
|
||||
#include "raylib.h"
|
||||
#include "world/blocks.h"
|
||||
#include "assets.h"
|
||||
|
||||
#define RAYLIB_NEW_RLGL
|
||||
#include "rlgl.h"
|
||||
|
||||
static inline float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; }
|
||||
|
||||
static inline
|
||||
void DrawTextEco(const char *text, float posX, float posY, int fontSize, Color color, float spacing) {
|
||||
#if 1
|
||||
// Check if default font has been loaded
|
||||
if (GetFontDefault().texture.id != 0) {
|
||||
Vector2 position = { (float)posX , (float)posY };
|
||||
|
||||
int defaultFontSize = 10; // Default Font chars height in pixel
|
||||
int new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing;
|
||||
|
||||
DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , color);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline
|
||||
int MeasureTextEco(const char *text, int fontSize, float spacing) {
|
||||
#if 1
|
||||
Vector2 vec = { 0.0f, 0.0f };
|
||||
|
||||
// Check if default font has been loaded
|
||||
if (GetFontDefault().texture.id != 0) {
|
||||
int defaultFontSize = 10; // Default Font chars height in pixel
|
||||
int new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing;
|
||||
|
||||
vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing);
|
||||
}
|
||||
|
||||
return (int)vec.x;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline
|
||||
void DrawCircleEco(float centerX, float centerY, float radius, Color color)
|
||||
{
|
||||
DrawCircleV((Vector2){ (float)centerX , (float)centerY }, radius , color);
|
||||
}
|
||||
|
||||
static inline
|
||||
void DrawRectangleEco(float posX, float posY, float width, float height, Color color)
|
||||
{
|
||||
DrawRectangleV((Vector2){ (float)posX , (float)posY }, (Vector2){ width , height }, color);
|
||||
}
|
||||
|
||||
static inline
|
||||
Texture2D GetBlockImage(uint8_t id) {
|
||||
return *(Texture2D*)blocks_get_img(id);
|
||||
}
|
||||
|
||||
static inline
|
||||
RenderTexture2D GetChunkTexture(uint64_t id) {
|
||||
RenderTexture2D *tex = (RenderTexture2D*)blocks_get_chunk_tex(id);
|
||||
if (!tex) return (RenderTexture2D){0};
|
||||
return *tex;
|
||||
}
|
||||
|
||||
static inline
|
||||
Texture2D GetSpriteTexture2D(uint16_t id) {
|
||||
return *(Texture2D*)assets_get_tex(id);
|
||||
}
|
||||
|
||||
static inline
|
||||
Sound GetSound(uint16_t id) {
|
||||
return *(Sound*)assets_get_snd(id);
|
||||
}
|
||||
|
||||
// Draw cube
|
||||
// NOTE: Cube position is the center position
|
||||
static inline
|
||||
void EcoDrawCube(Vector3 position, float width, float height, float length, float heading, Color color)
|
||||
{
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float z = 0.0f;
|
||||
|
||||
rlCheckRenderBatchLimit(36);
|
||||
|
||||
rlPushMatrix();
|
||||
// NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
|
||||
rlTranslatef(position.x, position.y, position.z);
|
||||
rlRotatef(heading, 0, 1, 0);
|
||||
//rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition
|
||||
|
||||
rlBegin(RL_TRIANGLES);
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
|
||||
// Front face
|
||||
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
|
||||
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
|
||||
|
||||
// Back face
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
|
||||
|
||||
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
|
||||
|
||||
// Top face
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
|
||||
|
||||
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
|
||||
|
||||
// Bottom face
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
|
||||
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
|
||||
|
||||
// Right face
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
|
||||
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
|
||||
|
||||
// Left face
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right
|
||||
|
||||
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlEnd();
|
||||
rlPopMatrix();
|
||||
}
|
||||
|
||||
// Draw codepoint at specified position in 3D space
|
||||
void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint)
|
||||
{
|
||||
#if 0
|
||||
// Character index position in sprite font
|
||||
// NOTE: In case a codepoint is not available in the font, index returned points to '?'
|
||||
int index = GetGlyphIndex(font, codepoint);
|
||||
float scale = fontSize/(float)font.baseSize;
|
||||
|
||||
// Character destination rectangle on screen
|
||||
// NOTE: We consider charsPadding on drawing
|
||||
position.x += (float)(font.chars[index].offsetX - font.charsPadding)/(float)font.baseSize*scale;
|
||||
position.z += (float)(font.chars[index].offsetY - font.charsPadding)/(float)font.baseSize*scale;
|
||||
|
||||
// Character source rectangle from font texture atlas
|
||||
// NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
|
||||
Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding,
|
||||
font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding };
|
||||
|
||||
float width = (float)(font.recs[index].width + 2.0f*font.charsPadding)/(float)font.baseSize*scale;
|
||||
float height = (float)(font.recs[index].height + 2.0f*font.charsPadding)/(float)font.baseSize*scale;
|
||||
|
||||
if (font.texture.id > 0)
|
||||
{
|
||||
const float x = 0.0f;
|
||||
const float y = 0.0f;
|
||||
const float z = 0.0f;
|
||||
|
||||
// normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f)
|
||||
const float tx = srcRec.x/font.texture.width;
|
||||
const float ty = srcRec.y/font.texture.height;
|
||||
const float tw = (srcRec.x+srcRec.width)/font.texture.width;
|
||||
const float th = (srcRec.y+srcRec.height)/font.texture.height;
|
||||
|
||||
{
|
||||
#if defined(RAYLIB_NEW_RLGL)
|
||||
}
|
||||
rlCheckRenderBatchLimit(4 + 4*backface);
|
||||
rlSetTexture(font.texture.id);
|
||||
#else
|
||||
if (rlCheckBufferLimit(4 + 4*backface)) rlglDraw();
|
||||
rlEnableTexture(font.texture.id);
|
||||
#endif
|
||||
rlPushMatrix();
|
||||
rlTranslatef(position.x, position.y, position.z);
|
||||
|
||||
rlBegin(RL_QUADS);
|
||||
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
|
||||
|
||||
// Front Face
|
||||
rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up
|
||||
rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Left Of The Texture and Quad
|
||||
rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad
|
||||
rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad
|
||||
rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Right Of The Texture and Quad
|
||||
|
||||
if (backface)
|
||||
{
|
||||
// Back Face
|
||||
rlNormal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down
|
||||
rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Right Of The Texture and Quad
|
||||
rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Left Of The Texture and Quad
|
||||
rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad
|
||||
rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Right Of The Texture and Quad
|
||||
}
|
||||
rlEnd();
|
||||
rlPopMatrix();
|
||||
|
||||
#if defined(RAYLIB_NEW_RLGL)
|
||||
rlSetTexture(0);
|
||||
#else
|
||||
rlDisableTexture();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint) {
|
||||
#if 0
|
||||
int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
|
||||
|
||||
float textOffsetY = 0.0f; // Offset between lines (on line break '\n')
|
||||
float textOffsetX = 0.0f; // Offset X to next character to draw
|
||||
|
||||
float scale = fontSize/(float)font.baseSize;
|
||||
|
||||
for (int i = 0; i < length;)
|
||||
{
|
||||
// Get next codepoint from byte string and glyph index in font
|
||||
int codepointByteCount = 0;
|
||||
int codepoint = GetCodepoint(&text[i], &codepointByteCount);
|
||||
int index = GetGlyphIndex(font, codepoint);
|
||||
|
||||
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
|
||||
if (codepoint == 0x3f) codepointByteCount = 1;
|
||||
|
||||
if (codepoint == '\n')
|
||||
{
|
||||
// NOTE: Fixed line spacing of 1.5 line-height
|
||||
// TODO: Support custom line spacing defined by user
|
||||
textOffsetY += scale + lineSpacing/(float)font.baseSize*scale;
|
||||
textOffsetX = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((codepoint != ' ') && (codepoint != '\t'))
|
||||
{
|
||||
DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint);
|
||||
}
|
||||
|
||||
if (font.chars[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale;
|
||||
else textOffsetX += (float)(font.chars[index].advanceX + fontSpacing)/(float)font.baseSize*scale;
|
||||
}
|
||||
|
||||
i += codepointByteCount; // Move text bytes counter to next codepoint
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector3 MeasureText3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing) {
|
||||
|
||||
#if 0
|
||||
int len = TextLength(text);
|
||||
int tempLen = 0; // Used to count longer text line num chars
|
||||
int lenCounter = 0;
|
||||
|
||||
float tempTextWidth = 0.0f; // Used to count longer text line width
|
||||
|
||||
float scale = fontSize/(float)font.baseSize;
|
||||
float textHeight = scale;
|
||||
float textWidth = 0.0f;
|
||||
|
||||
int letter = 0; // Current character
|
||||
int index = 0; // Index position in sprite font
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
lenCounter++;
|
||||
|
||||
int next = 0;
|
||||
letter = GetCodepoint(&text[i], &next);
|
||||
index = GetGlyphIndex(font, letter);
|
||||
|
||||
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
|
||||
if (letter == 0x3f) next = 1;
|
||||
i += next - 1;
|
||||
|
||||
if (letter != '\n')
|
||||
{
|
||||
if (font.chars[index].advanceX != 0) textWidth += (font.chars[index].advanceX+fontSpacing)/(float)font.baseSize*scale;
|
||||
else textWidth += (font.recs[index].width + font.chars[index].offsetX)/(float)font.baseSize*scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
|
||||
lenCounter = 0;
|
||||
textWidth = 0.0f;
|
||||
textHeight += scale + lineSpacing/(float)font.baseSize*scale;
|
||||
}
|
||||
|
||||
if (tempLen < lenCounter) tempLen = lenCounter;
|
||||
}
|
||||
|
||||
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
|
||||
|
||||
Vector3 vec = { 0 };
|
||||
vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing/(float)font.baseSize*scale); // Adds chars spacing to measure
|
||||
vec.y = 0.25f;
|
||||
vec.z = textHeight;
|
||||
|
||||
return vec;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
Color GenerateRandomColor(float s, float v) {
|
||||
const float Phi = 0.618033988749895f; // Golden ratio conjugate
|
||||
float h = GetRandomValue(0, 360);
|
||||
h = fmodf((h + h*Phi), 360.0f);
|
||||
return ColorFromHSV(h, s, v);
|
||||
}
|
||||
|
||||
|
||||
// Draw circle outline
|
||||
void DrawCircleLinesEco(float centerX, float centerY, float radius, Color color)
|
||||
{
|
||||
rlCheckRenderBatchLimit(2*36);
|
||||
|
||||
rlBegin(RL_LINES);
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
|
||||
// NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360)
|
||||
for (int i = 0; i < 360; i += 10)
|
||||
{
|
||||
rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius);
|
||||
rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius);
|
||||
}
|
||||
rlEnd();
|
||||
}
|
||||
|
||||
void DrawRectangleLinesEco(float posX, float posY, float width, float height, Color color)
|
||||
{
|
||||
rlBegin(RL_LINES);
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
rlVertex2f(posX + 1, posY + 1);
|
||||
rlVertex2f(posX + width, posY + 1);
|
||||
|
||||
rlVertex2f(posX + width, posY + 1);
|
||||
rlVertex2f(posX + width, posY + height);
|
||||
|
||||
rlVertex2f(posX + width, posY + height);
|
||||
rlVertex2f(posX + 1, posY + height);
|
||||
|
||||
rlVertex2f(posX + 1, posY + height);
|
||||
rlVertex2f(posX + 1, posY + 1);
|
||||
rlEnd();
|
||||
}
|
||||
#pragma once
|
||||
#include "system.h"
|
||||
#include "raylib.h"
|
||||
#include "world/blocks.h"
|
||||
#include "assets.h"
|
||||
|
||||
#define RAYLIB_NEW_RLGL
|
||||
#include "rlgl.h"
|
||||
|
||||
static inline float lerp(float a, float b, float t) { return a * (1.0f - t) + b * t; }
|
||||
|
||||
static inline
|
||||
void DrawTextEco(const char *text, float posX, float posY, int fontSize, Color color, float spacing) {
|
||||
#if 1
|
||||
// Check if default font has been loaded
|
||||
if (GetFontDefault().texture.id != 0) {
|
||||
Vector2 position = { (float)posX , (float)posY };
|
||||
|
||||
int defaultFontSize = 10; // Default Font chars height in pixel
|
||||
int new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing;
|
||||
|
||||
DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , color);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline
|
||||
int MeasureTextEco(const char *text, int fontSize, float spacing) {
|
||||
#if 1
|
||||
Vector2 vec = { 0.0f, 0.0f };
|
||||
|
||||
// Check if default font has been loaded
|
||||
if (GetFontDefault().texture.id != 0) {
|
||||
int defaultFontSize = 10; // Default Font chars height in pixel
|
||||
int new_spacing = spacing == 0.0f ? fontSize/defaultFontSize : spacing;
|
||||
|
||||
vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing);
|
||||
}
|
||||
|
||||
return (int)vec.x;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline
|
||||
void DrawCircleEco(float centerX, float centerY, float radius, Color color)
|
||||
{
|
||||
DrawCircleV((Vector2){ (float)centerX , (float)centerY }, radius , color);
|
||||
}
|
||||
|
||||
static inline
|
||||
void DrawRectangleEco(float posX, float posY, float width, float height, Color color)
|
||||
{
|
||||
DrawRectangleV((Vector2){ (float)posX , (float)posY }, (Vector2){ width , height }, color);
|
||||
}
|
||||
|
||||
static inline
|
||||
Texture2D GetBlockImage(uint8_t id) {
|
||||
return *(Texture2D*)blocks_get_img(id);
|
||||
}
|
||||
|
||||
static inline
|
||||
RenderTexture2D GetChunkTexture(uint64_t id) {
|
||||
RenderTexture2D *tex = (RenderTexture2D*)blocks_get_chunk_tex(id);
|
||||
if (!tex) return (RenderTexture2D){0};
|
||||
return *tex;
|
||||
}
|
||||
|
||||
static inline
|
||||
Texture2D GetSpriteTexture2D(uint16_t id) {
|
||||
return *(Texture2D*)assets_get_tex(id);
|
||||
}
|
||||
|
||||
static inline
|
||||
Sound GetSound(uint16_t id) {
|
||||
return *(Sound*)assets_get_snd(id);
|
||||
}
|
||||
|
||||
// Draw cube
|
||||
// NOTE: Cube position is the center position
|
||||
static inline
|
||||
void EcoDrawCube(Vector3 position, float width, float height, float length, float heading, Color color)
|
||||
{
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float z = 0.0f;
|
||||
|
||||
rlCheckRenderBatchLimit(36);
|
||||
|
||||
rlPushMatrix();
|
||||
// NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
|
||||
rlTranslatef(position.x, position.y, position.z);
|
||||
rlRotatef(heading, 0, 1, 0);
|
||||
//rlScalef(1.0f, 1.0f, 1.0f); // NOTE: Vertices are directly scaled on definition
|
||||
|
||||
rlBegin(RL_TRIANGLES);
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
|
||||
// Front face
|
||||
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
|
||||
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
|
||||
|
||||
// Back face
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
|
||||
|
||||
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
|
||||
|
||||
// Top face
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
|
||||
|
||||
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right
|
||||
|
||||
// Bottom face
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
|
||||
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Right
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Left
|
||||
|
||||
// Right face
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
|
||||
|
||||
rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left
|
||||
rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left
|
||||
|
||||
// Left face
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
|
||||
rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right
|
||||
|
||||
rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left
|
||||
rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left
|
||||
rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right
|
||||
rlEnd();
|
||||
rlPopMatrix();
|
||||
}
|
||||
|
||||
// Draw codepoint at specified position in 3D space
|
||||
void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint)
|
||||
{
|
||||
#if 0
|
||||
// Character index position in sprite font
|
||||
// NOTE: In case a codepoint is not available in the font, index returned points to '?'
|
||||
int index = GetGlyphIndex(font, codepoint);
|
||||
float scale = fontSize/(float)font.baseSize;
|
||||
|
||||
// Character destination rectangle on screen
|
||||
// NOTE: We consider charsPadding on drawing
|
||||
position.x += (float)(font.chars[index].offsetX - font.charsPadding)/(float)font.baseSize*scale;
|
||||
position.z += (float)(font.chars[index].offsetY - font.charsPadding)/(float)font.baseSize*scale;
|
||||
|
||||
// Character source rectangle from font texture atlas
|
||||
// NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
|
||||
Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding,
|
||||
font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding };
|
||||
|
||||
float width = (float)(font.recs[index].width + 2.0f*font.charsPadding)/(float)font.baseSize*scale;
|
||||
float height = (float)(font.recs[index].height + 2.0f*font.charsPadding)/(float)font.baseSize*scale;
|
||||
|
||||
if (font.texture.id > 0)
|
||||
{
|
||||
const float x = 0.0f;
|
||||
const float y = 0.0f;
|
||||
const float z = 0.0f;
|
||||
|
||||
// normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f)
|
||||
const float tx = srcRec.x/font.texture.width;
|
||||
const float ty = srcRec.y/font.texture.height;
|
||||
const float tw = (srcRec.x+srcRec.width)/font.texture.width;
|
||||
const float th = (srcRec.y+srcRec.height)/font.texture.height;
|
||||
|
||||
{
|
||||
#if defined(RAYLIB_NEW_RLGL)
|
||||
}
|
||||
rlCheckRenderBatchLimit(4 + 4*backface);
|
||||
rlSetTexture(font.texture.id);
|
||||
#else
|
||||
if (rlCheckBufferLimit(4 + 4*backface)) rlglDraw();
|
||||
rlEnableTexture(font.texture.id);
|
||||
#endif
|
||||
rlPushMatrix();
|
||||
rlTranslatef(position.x, position.y, position.z);
|
||||
|
||||
rlBegin(RL_QUADS);
|
||||
rlColor4ub(tint.r, tint.g, tint.b, tint.a);
|
||||
|
||||
// Front Face
|
||||
rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up
|
||||
rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Left Of The Texture and Quad
|
||||
rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad
|
||||
rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad
|
||||
rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Right Of The Texture and Quad
|
||||
|
||||
if (backface)
|
||||
{
|
||||
// Back Face
|
||||
rlNormal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down
|
||||
rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Right Of The Texture and Quad
|
||||
rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Left Of The Texture and Quad
|
||||
rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad
|
||||
rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Right Of The Texture and Quad
|
||||
}
|
||||
rlEnd();
|
||||
rlPopMatrix();
|
||||
|
||||
#if defined(RAYLIB_NEW_RLGL)
|
||||
rlSetTexture(0);
|
||||
#else
|
||||
rlDisableTexture();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint) {
|
||||
#if 0
|
||||
int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
|
||||
|
||||
float textOffsetY = 0.0f; // Offset between lines (on line break '\n')
|
||||
float textOffsetX = 0.0f; // Offset X to next character to draw
|
||||
|
||||
float scale = fontSize/(float)font.baseSize;
|
||||
|
||||
for (int i = 0; i < length;)
|
||||
{
|
||||
// Get next codepoint from byte string and glyph index in font
|
||||
int codepointByteCount = 0;
|
||||
int codepoint = GetCodepoint(&text[i], &codepointByteCount);
|
||||
int index = GetGlyphIndex(font, codepoint);
|
||||
|
||||
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||
// but we need to draw all of the bad bytes using the '?' symbol moving one byte
|
||||
if (codepoint == 0x3f) codepointByteCount = 1;
|
||||
|
||||
if (codepoint == '\n')
|
||||
{
|
||||
// NOTE: Fixed line spacing of 1.5 line-height
|
||||
// TODO: Support custom line spacing defined by user
|
||||
textOffsetY += scale + lineSpacing/(float)font.baseSize*scale;
|
||||
textOffsetX = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((codepoint != ' ') && (codepoint != '\t'))
|
||||
{
|
||||
DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint);
|
||||
}
|
||||
|
||||
if (font.chars[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale;
|
||||
else textOffsetX += (float)(font.chars[index].advanceX + fontSpacing)/(float)font.baseSize*scale;
|
||||
}
|
||||
|
||||
i += codepointByteCount; // Move text bytes counter to next codepoint
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Vector3 MeasureText3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing) {
|
||||
|
||||
#if 0
|
||||
int len = TextLength(text);
|
||||
int tempLen = 0; // Used to count longer text line num chars
|
||||
int lenCounter = 0;
|
||||
|
||||
float tempTextWidth = 0.0f; // Used to count longer text line width
|
||||
|
||||
float scale = fontSize/(float)font.baseSize;
|
||||
float textHeight = scale;
|
||||
float textWidth = 0.0f;
|
||||
|
||||
int letter = 0; // Current character
|
||||
int index = 0; // Index position in sprite font
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
lenCounter++;
|
||||
|
||||
int next = 0;
|
||||
letter = GetCodepoint(&text[i], &next);
|
||||
index = GetGlyphIndex(font, letter);
|
||||
|
||||
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
|
||||
// but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
|
||||
if (letter == 0x3f) next = 1;
|
||||
i += next - 1;
|
||||
|
||||
if (letter != '\n')
|
||||
{
|
||||
if (font.chars[index].advanceX != 0) textWidth += (font.chars[index].advanceX+fontSpacing)/(float)font.baseSize*scale;
|
||||
else textWidth += (font.recs[index].width + font.chars[index].offsetX)/(float)font.baseSize*scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
|
||||
lenCounter = 0;
|
||||
textWidth = 0.0f;
|
||||
textHeight += scale + lineSpacing/(float)font.baseSize*scale;
|
||||
}
|
||||
|
||||
if (tempLen < lenCounter) tempLen = lenCounter;
|
||||
}
|
||||
|
||||
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
|
||||
|
||||
Vector3 vec = { 0 };
|
||||
vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing/(float)font.baseSize*scale); // Adds chars spacing to measure
|
||||
vec.y = 0.25f;
|
||||
vec.z = textHeight;
|
||||
|
||||
return vec;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
Color GenerateRandomColor(float s, float v) {
|
||||
const float Phi = 0.618033988749895f; // Golden ratio conjugate
|
||||
float h = GetRandomValue(0, 360);
|
||||
h = fmodf((h + h*Phi), 360.0f);
|
||||
return ColorFromHSV(h, s, v);
|
||||
}
|
||||
|
||||
|
||||
// Draw circle outline
|
||||
void DrawCircleLinesEco(float centerX, float centerY, float radius, Color color)
|
||||
{
|
||||
rlCheckRenderBatchLimit(2*36);
|
||||
|
||||
rlBegin(RL_LINES);
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
|
||||
// NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360)
|
||||
for (int i = 0; i < 360; i += 10)
|
||||
{
|
||||
rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius);
|
||||
rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius);
|
||||
}
|
||||
rlEnd();
|
||||
}
|
||||
|
||||
void DrawRectangleLinesEco(float posX, float posY, float width, float height, Color color)
|
||||
{
|
||||
rlBegin(RL_LINES);
|
||||
rlColor4ub(color.r, color.g, color.b, color.a);
|
||||
rlVertex2f(posX + 1, posY + 1);
|
||||
rlVertex2f(posX + width, posY + 1);
|
||||
|
||||
rlVertex2f(posX + width, posY + 1);
|
||||
rlVertex2f(posX + width, posY + height);
|
||||
|
||||
rlVertex2f(posX + width, posY + height);
|
||||
rlVertex2f(posX + 1, posY + height);
|
||||
|
||||
rlVertex2f(posX + 1, posY + height);
|
||||
rlVertex2f(posX + 1, posY + 1);
|
||||
rlEnd();
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
#include "vehicle.h"
|
||||
#include "entity.h"
|
||||
#include "entity_view.h"
|
||||
#include "world/world.h"
|
||||
|
||||
#include "modules/components.h"
|
||||
|
||||
uint64_t vehicle_spawn(void) {
|
||||
ecs_entity_t e = entity_spawn(EKIND_VEHICLE);
|
||||
|
||||
Vehicle *veh = ecs_get_mut(world_ecs(), e, Vehicle, NULL);
|
||||
*veh = (Vehicle){
|
||||
.wheel_base = 50.0f,
|
||||
.speed = 50.0f,
|
||||
.reverse_speed = -20.0f,
|
||||
.force = 0.0f,
|
||||
};
|
||||
return (uint64_t)e;
|
||||
}
|
||||
|
||||
void vehicle_despawn(uint64_t ent_id) {
|
||||
entity_despawn(ent_id);
|
||||
}
|
||||
#include "vehicle.h"
|
||||
#include "entity.h"
|
||||
#include "entity_view.h"
|
||||
#include "world/world.h"
|
||||
|
||||
#include "modules/components.h"
|
||||
|
||||
uint64_t vehicle_spawn(void) {
|
||||
ecs_entity_t e = entity_spawn(EKIND_VEHICLE);
|
||||
|
||||
Vehicle *veh = ecs_get_mut(world_ecs(), e, Vehicle, NULL);
|
||||
*veh = (Vehicle){
|
||||
.wheel_base = 50.0f,
|
||||
.speed = 50.0f,
|
||||
.reverse_speed = -20.0f,
|
||||
.force = 0.0f,
|
||||
};
|
||||
return (uint64_t)e;
|
||||
}
|
||||
|
||||
void vehicle_despawn(uint64_t ent_id) {
|
||||
entity_despawn(ent_id);
|
||||
}
|
||||
|
|
|
@ -1,144 +1,144 @@
|
|||
#define ZPL_NANO
|
||||
#include "zpl.h"
|
||||
#include "world/world.h"
|
||||
#include "world/blocks.h"
|
||||
#include "raylib.h"
|
||||
#include "gen/texgen.h"
|
||||
#include "world_view.h"
|
||||
#include "perlin.h"
|
||||
|
||||
#define BLOCKS_COUNT (sizeof(blocks)/sizeof(block))
|
||||
#define WORLD_TEXTURE_BLOCK_SCALE 0.5f
|
||||
|
||||
ZPL_TABLE(static, blocks__chunk_tbl, blocks__chunk_tbl_, RenderTexture2D);
|
||||
|
||||
static blocks__chunk_tbl baked_chunks;
|
||||
|
||||
static void chunks_unload_textures(uint64_t key, RenderTexture2D *value) {
|
||||
(void)key;
|
||||
UnloadRenderTexture(*value);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
uint32_t kind;
|
||||
uint32_t biome;
|
||||
char symbol;
|
||||
float drag;
|
||||
float friction;
|
||||
float bounce;
|
||||
|
||||
// NOTE(zaklaus): viewer data
|
||||
Texture2D img;
|
||||
} block;
|
||||
|
||||
#include "blocks_list.c"
|
||||
|
||||
int32_t blocks_setup(void) {
|
||||
for (uint32_t i=0; i<BLOCKS_COUNT; i++) {
|
||||
block *b = &blocks[i];
|
||||
b->img = texgen_build_block(b->biome, b->kind);
|
||||
}
|
||||
|
||||
blocks__chunk_tbl_init(&baked_chunks, zpl_heap());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blocks_destroy(void) {
|
||||
for (uint32_t i=0; i<BLOCKS_COUNT; i++) {
|
||||
UnloadTexture(blocks[i].img);
|
||||
}
|
||||
|
||||
blocks__chunk_tbl_map_mut(&baked_chunks, chunks_unload_textures);
|
||||
blocks__chunk_tbl_destroy(&baked_chunks);
|
||||
}
|
||||
|
||||
uint8_t blocks_find(uint32_t biome, uint32_t kind) {
|
||||
for (uint32_t i=0; i<BLOCKS_COUNT; i++) {
|
||||
if (blocks[i].biome == biome && blocks[i].kind == kind)
|
||||
return i;
|
||||
}
|
||||
return BLOCK_INVALID;
|
||||
}
|
||||
|
||||
char *blocks_get_name(uint8_t id) {
|
||||
return blocks[id].name;
|
||||
}
|
||||
|
||||
char blocks_get_symbol(uint8_t id) {
|
||||
return blocks[id].symbol;
|
||||
}
|
||||
|
||||
uint32_t blocks_get_flags(uint8_t id) {
|
||||
return blocks[id].flags;
|
||||
}
|
||||
|
||||
uint32_t blocks_get_biome(uint8_t id) {
|
||||
return blocks[id].biome;
|
||||
}
|
||||
|
||||
uint32_t blocks_get_kind(uint8_t id) {
|
||||
return blocks[id].kind;
|
||||
}
|
||||
|
||||
float blocks_get_drag(uint8_t id) {
|
||||
return blocks[id].drag;
|
||||
}
|
||||
|
||||
float blocks_get_friction(uint8_t id) {
|
||||
return blocks[id].friction;
|
||||
}
|
||||
|
||||
float blocks_get_bounce(uint8_t id) {
|
||||
return blocks[id].bounce;
|
||||
}
|
||||
|
||||
void *blocks_get_img(uint8_t id) {
|
||||
return (void*)&blocks[id].img;
|
||||
}
|
||||
|
||||
void blocks_build_chunk_tex(uint64_t id, uint8_t *chunk_blocks, uint8_t *outer_chunk_blocks, void *raw_view) {
|
||||
world_view *view = (world_view*)raw_view;
|
||||
uint16_t blk_dims = WORLD_BLOCK_SIZE * WORLD_TEXTURE_BLOCK_SCALE;
|
||||
uint16_t dims = blk_dims * view->chunk_size;
|
||||
RenderTexture2D canvas = LoadRenderTexture(dims, dims);
|
||||
BeginTextureMode(canvas);
|
||||
ClearBackground(WHITE);
|
||||
for (int y = 0; y < view->chunk_size; y += 1) {
|
||||
for (int x = 0; x < view->chunk_size; x += 1) {
|
||||
#if 0
|
||||
Texture2D blk = blocks[chunk_blocks[(y*view->chunk_size)+x]].img;
|
||||
Rectangle src = {0, 0, WORLD_BLOCK_SIZE, WORLD_BLOCK_SIZE};
|
||||
Rectangle dst = {x*blk_dims, y*blk_dims, blk_dims, blk_dims};
|
||||
DrawTexturePro(blk, src, dst, (Vector2){0.0f,0.0f}, 0.0f, WHITE);
|
||||
#else
|
||||
static float rots[] = { 0.0f, 90.0f, 180.f, 270.0f };
|
||||
float rot = rots[(int32_t)(perlin_fbm(view->seed, x, y, 1.2f, 3) * 4.0f) % 4];
|
||||
float half_block = blk_dims / 2.0f;
|
||||
Texture2D blk = blocks[chunk_blocks[(y*view->chunk_size)+x]].img;
|
||||
Rectangle src = {0, 0, WORLD_BLOCK_SIZE, WORLD_BLOCK_SIZE};
|
||||
Rectangle dst = {x*blk_dims + half_block, y*blk_dims + half_block, blk_dims, blk_dims};
|
||||
DrawTexturePro(blk, src, dst, (Vector2){half_block, half_block}, rot, WHITE);
|
||||
|
||||
if (outer_chunk_blocks[(y*view->chunk_size)+x] != 0) {
|
||||
Texture2D blk2 = blocks[outer_chunk_blocks[(y*view->chunk_size)+x]].img;
|
||||
DrawTexturePro(blk2, src, dst, (Vector2){half_block, half_block}, rot, WHITE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
EndTextureMode();
|
||||
blocks__chunk_tbl_set(&baked_chunks, id, canvas);
|
||||
}
|
||||
|
||||
void *blocks_get_chunk_tex(uint64_t id) {
|
||||
return blocks__chunk_tbl_get(&baked_chunks, id);
|
||||
}
|
||||
|
||||
void blocks_remove_chunk_tex(uint64_t id) {
|
||||
RenderTexture2D *tex = blocks__chunk_tbl_get(&baked_chunks, id);
|
||||
if (!tex) return;
|
||||
UnloadRenderTexture(*tex);
|
||||
blocks__chunk_tbl_remove(&baked_chunks, id);
|
||||
#define ZPL_NANO
|
||||
#include "zpl.h"
|
||||
#include "world/world.h"
|
||||
#include "world/blocks.h"
|
||||
#include "raylib.h"
|
||||
#include "gen/texgen.h"
|
||||
#include "world_view.h"
|
||||
#include "perlin.h"
|
||||
|
||||
#define BLOCKS_COUNT (sizeof(blocks)/sizeof(block))
|
||||
#define WORLD_TEXTURE_BLOCK_SCALE 0.5f
|
||||
|
||||
ZPL_TABLE(static, blocks__chunk_tbl, blocks__chunk_tbl_, RenderTexture2D);
|
||||
|
||||
static blocks__chunk_tbl baked_chunks;
|
||||
|
||||
static void chunks_unload_textures(uint64_t key, RenderTexture2D *value) {
|
||||
(void)key;
|
||||
UnloadRenderTexture(*value);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
uint32_t flags;
|
||||
uint32_t kind;
|
||||
uint32_t biome;
|
||||
char symbol;
|
||||
float drag;
|
||||
float friction;
|
||||
float bounce;
|
||||
|
||||
// NOTE(zaklaus): viewer data
|
||||
Texture2D img;
|
||||
} block;
|
||||
|
||||
#include "blocks_list.c"
|
||||
|
||||
int32_t blocks_setup(void) {
|
||||
for (uint32_t i=0; i<BLOCKS_COUNT; i++) {
|
||||
block *b = &blocks[i];
|
||||
b->img = texgen_build_block(b->biome, b->kind);
|
||||
}
|
||||
|
||||
blocks__chunk_tbl_init(&baked_chunks, zpl_heap());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void blocks_destroy(void) {
|
||||
for (uint32_t i=0; i<BLOCKS_COUNT; i++) {
|
||||
UnloadTexture(blocks[i].img);
|
||||
}
|
||||
|
||||
blocks__chunk_tbl_map_mut(&baked_chunks, chunks_unload_textures);
|
||||
blocks__chunk_tbl_destroy(&baked_chunks);
|
||||
}
|
||||
|
||||
uint8_t blocks_find(uint32_t biome, uint32_t kind) {
|
||||
for (uint32_t i=0; i<BLOCKS_COUNT; i++) {
|
||||
if (blocks[i].biome == biome && blocks[i].kind == kind)
|
||||
return i;
|
||||
}
|
||||
return BLOCK_INVALID;
|
||||
}
|
||||
|
||||
char *blocks_get_name(uint8_t id) {
|
||||
return blocks[id].name;
|
||||
}
|
||||
|
||||
char blocks_get_symbol(uint8_t id) {
|
||||
return blocks[id].symbol;
|
||||
}
|
||||
|
||||
uint32_t blocks_get_flags(uint8_t id) {
|
||||
return blocks[id].flags;
|
||||
}
|
||||
|
||||
uint32_t blocks_get_biome(uint8_t id) {
|
||||
return blocks[id].biome;
|
||||
}
|
||||
|
||||
uint32_t blocks_get_kind(uint8_t id) {
|
||||
return blocks[id].kind;
|
||||
}
|
||||
|
||||
float blocks_get_drag(uint8_t id) {
|
||||
return blocks[id].drag;
|
||||
}
|
||||
|
||||
float blocks_get_friction(uint8_t id) {
|
||||
return blocks[id].friction;
|
||||
}
|
||||
|
||||
float blocks_get_bounce(uint8_t id) {
|
||||
return blocks[id].bounce;
|
||||
}
|
||||
|
||||
void *blocks_get_img(uint8_t id) {
|
||||
return (void*)&blocks[id].img;
|
||||
}
|
||||
|
||||
void blocks_build_chunk_tex(uint64_t id, uint8_t *chunk_blocks, uint8_t *outer_chunk_blocks, void *raw_view) {
|
||||
world_view *view = (world_view*)raw_view;
|
||||
uint16_t blk_dims = WORLD_BLOCK_SIZE * WORLD_TEXTURE_BLOCK_SCALE;
|
||||
uint16_t dims = blk_dims * view->chunk_size;
|
||||
RenderTexture2D canvas = LoadRenderTexture(dims, dims);
|
||||
BeginTextureMode(canvas);
|
||||
ClearBackground(WHITE);
|
||||
for (int y = 0; y < view->chunk_size; y += 1) {
|
||||
for (int x = 0; x < view->chunk_size; x += 1) {
|
||||
#if 0
|
||||
Texture2D blk = blocks[chunk_blocks[(y*view->chunk_size)+x]].img;
|
||||
Rectangle src = {0, 0, WORLD_BLOCK_SIZE, WORLD_BLOCK_SIZE};
|
||||
Rectangle dst = {x*blk_dims, y*blk_dims, blk_dims, blk_dims};
|
||||
DrawTexturePro(blk, src, dst, (Vector2){0.0f,0.0f}, 0.0f, WHITE);
|
||||
#else
|
||||
static float rots[] = { 0.0f, 90.0f, 180.f, 270.0f };
|
||||
float rot = rots[(int32_t)(perlin_fbm(view->seed, x, y, 1.2f, 3) * 4.0f) % 4];
|
||||
float half_block = blk_dims / 2.0f;
|
||||
Texture2D blk = blocks[chunk_blocks[(y*view->chunk_size)+x]].img;
|
||||
Rectangle src = {0, 0, WORLD_BLOCK_SIZE, WORLD_BLOCK_SIZE};
|
||||
Rectangle dst = {x*blk_dims + half_block, y*blk_dims + half_block, blk_dims, blk_dims};
|
||||
DrawTexturePro(blk, src, dst, (Vector2){half_block, half_block}, rot, WHITE);
|
||||
|
||||
if (outer_chunk_blocks[(y*view->chunk_size)+x] != 0) {
|
||||
Texture2D blk2 = blocks[outer_chunk_blocks[(y*view->chunk_size)+x]].img;
|
||||
DrawTexturePro(blk2, src, dst, (Vector2){half_block, half_block}, rot, WHITE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
EndTextureMode();
|
||||
blocks__chunk_tbl_set(&baked_chunks, id, canvas);
|
||||
}
|
||||
|
||||
void *blocks_get_chunk_tex(uint64_t id) {
|
||||
return blocks__chunk_tbl_get(&baked_chunks, id);
|
||||
}
|
||||
|
||||
void blocks_remove_chunk_tex(uint64_t id) {
|
||||
RenderTexture2D *tex = blocks__chunk_tbl_get(&baked_chunks, id);
|
||||
if (!tex) return;
|
||||
UnloadRenderTexture(*tex);
|
||||
blocks__chunk_tbl_remove(&baked_chunks, id);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,101 +1,101 @@
|
|||
#pragma once
|
||||
#include "system.h"
|
||||
#include "librg.h"
|
||||
#include "packet.h"
|
||||
#include "flecs/flecs.h"
|
||||
#include "flecs/flecs_meta.h"
|
||||
#include "modules/components.h"
|
||||
|
||||
#define WORLD_ERROR_NONE +0x0000
|
||||
#define WORLD_ERROR_OUTOFMEM -0x0001
|
||||
#define WORLD_ERROR_INVALID_BLOCKS -0x0002
|
||||
#define WORLD_ERROR_INVALID_DIMENSIONS -0x0003
|
||||
#define WORLD_ERROR_INVALID_BUFFER -0x0004
|
||||
#define WORLD_ERROR_TRACKER_FAILED -0x0005
|
||||
|
||||
#define WORLD_LAYERING 0
|
||||
#define WORLD_TRACKER_LAYERS 3
|
||||
#define WORLD_TRACKER_UPDATE_FAST_MS 0
|
||||
#define WORLD_TRACKER_UPDATE_NORMAL_MS 50
|
||||
#define WORLD_TRACKER_UPDATE_SLOW_MS 100
|
||||
#define WORLD_TRACKER_UPDATE_MP_FAST_MS 50
|
||||
#define WORLD_TRACKER_UPDATE_MP_NORMAL_MS 150
|
||||
#define WORLD_TRACKER_UPDATE_MP_SLOW_MS 300
|
||||
#define WORLD_BLOCK_SIZE 64
|
||||
|
||||
#define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata)
|
||||
typedef WORLD_PKT_READER(world_pkt_reader_proc);
|
||||
|
||||
#define WORLD_PKT_WRITER(name) int32_t name(pkt_header *pkt, void *udata)
|
||||
typedef WORLD_PKT_WRITER(world_pkt_writer_proc);
|
||||
|
||||
typedef struct {
|
||||
bool is_paused;
|
||||
uint8_t *data;
|
||||
uint32_t seed;
|
||||
uint32_t size;
|
||||
uint16_t chunk_size;
|
||||
uint16_t chunk_amount;
|
||||
uint8_t **block_mapping;
|
||||
uint8_t **outer_block_mapping;
|
||||
uint16_t dim;
|
||||
uint64_t tracker_update[3];
|
||||
uint8_t active_layer_id;
|
||||
ecs_world_t *ecs;
|
||||
ecs_world_t *ecs_stage;
|
||||
ecs_query_t *ecs_update;
|
||||
ecs_entity_t *chunk_mapping;
|
||||
librg_world *tracker;
|
||||
world_pkt_reader_proc *reader_proc;
|
||||
world_pkt_writer_proc *writer_proc;
|
||||
} world_data;
|
||||
|
||||
void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc);
|
||||
int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount);
|
||||
int32_t world_destroy(void);
|
||||
int32_t world_update(void);
|
||||
|
||||
int32_t world_read(void* data, uint32_t datalen, void *udata);
|
||||
int32_t world_write(pkt_header *pkt, void *udata);
|
||||
|
||||
uint32_t world_buf(uint8_t const **ptr, uint32_t *width);
|
||||
uint32_t world_seed(void);
|
||||
ecs_world_t *world_ecs(void);
|
||||
void world_set_stage(ecs_world_t *ecs);
|
||||
librg_world *world_tracker(void);
|
||||
|
||||
// NOTE(zaklaus): World simulation time control
|
||||
void world_pause(void);
|
||||
void world_resume(void);
|
||||
bool world_is_paused(void);
|
||||
void world_step(float step_size);
|
||||
|
||||
uint16_t world_chunk_size(void);
|
||||
uint16_t world_chunk_amount(void);
|
||||
uint16_t world_dim(void);
|
||||
ecs_entity_t world_chunk_mapping(librg_chunk id);
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint8_t block_id;
|
||||
ecs_entity_t chunk_e;
|
||||
int64_t chunk_id;
|
||||
float ox, oy;
|
||||
} world_block_lookup;
|
||||
|
||||
world_block_lookup world_block_from_realpos(float x, float y);
|
||||
world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx);
|
||||
int64_t world_chunk_from_realpos(float x, float y);
|
||||
int64_t world_chunk_from_entity(ecs_entity_t id);
|
||||
void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id);
|
||||
bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id);
|
||||
uint8_t *world_chunk_get_blocks(int64_t id);
|
||||
void world_chunk_mark_dirty(ecs_entity_t e);
|
||||
uint8_t world_chunk_is_dirty(ecs_entity_t e);
|
||||
|
||||
// NOTE(zaklaus): Uses locally persistent buffer !!
|
||||
int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len);
|
||||
int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len);
|
||||
int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius);
|
||||
|
||||
#pragma once
|
||||
#include "system.h"
|
||||
#include "librg.h"
|
||||
#include "packet.h"
|
||||
#include "flecs/flecs.h"
|
||||
#include "flecs/flecs_meta.h"
|
||||
#include "modules/components.h"
|
||||
|
||||
#define WORLD_ERROR_NONE +0x0000
|
||||
#define WORLD_ERROR_OUTOFMEM -0x0001
|
||||
#define WORLD_ERROR_INVALID_BLOCKS -0x0002
|
||||
#define WORLD_ERROR_INVALID_DIMENSIONS -0x0003
|
||||
#define WORLD_ERROR_INVALID_BUFFER -0x0004
|
||||
#define WORLD_ERROR_TRACKER_FAILED -0x0005
|
||||
|
||||
#define WORLD_LAYERING 0
|
||||
#define WORLD_TRACKER_LAYERS 3
|
||||
#define WORLD_TRACKER_UPDATE_FAST_MS 0
|
||||
#define WORLD_TRACKER_UPDATE_NORMAL_MS 50
|
||||
#define WORLD_TRACKER_UPDATE_SLOW_MS 100
|
||||
#define WORLD_TRACKER_UPDATE_MP_FAST_MS 50
|
||||
#define WORLD_TRACKER_UPDATE_MP_NORMAL_MS 150
|
||||
#define WORLD_TRACKER_UPDATE_MP_SLOW_MS 300
|
||||
#define WORLD_BLOCK_SIZE 64
|
||||
|
||||
#define WORLD_PKT_READER(name) int32_t name(void* data, uint32_t datalen, void *udata)
|
||||
typedef WORLD_PKT_READER(world_pkt_reader_proc);
|
||||
|
||||
#define WORLD_PKT_WRITER(name) int32_t name(pkt_header *pkt, void *udata)
|
||||
typedef WORLD_PKT_WRITER(world_pkt_writer_proc);
|
||||
|
||||
typedef struct {
|
||||
bool is_paused;
|
||||
uint8_t *data;
|
||||
uint32_t seed;
|
||||
uint32_t size;
|
||||
uint16_t chunk_size;
|
||||
uint16_t chunk_amount;
|
||||
uint8_t **block_mapping;
|
||||
uint8_t **outer_block_mapping;
|
||||
uint16_t dim;
|
||||
uint64_t tracker_update[3];
|
||||
uint8_t active_layer_id;
|
||||
ecs_world_t *ecs;
|
||||
ecs_world_t *ecs_stage;
|
||||
ecs_query_t *ecs_update;
|
||||
ecs_entity_t *chunk_mapping;
|
||||
librg_world *tracker;
|
||||
world_pkt_reader_proc *reader_proc;
|
||||
world_pkt_writer_proc *writer_proc;
|
||||
} world_data;
|
||||
|
||||
void world_setup_pkt_handlers(world_pkt_reader_proc *reader_proc, world_pkt_writer_proc *writer_proc);
|
||||
int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount);
|
||||
int32_t world_destroy(void);
|
||||
int32_t world_update(void);
|
||||
|
||||
int32_t world_read(void* data, uint32_t datalen, void *udata);
|
||||
int32_t world_write(pkt_header *pkt, void *udata);
|
||||
|
||||
uint32_t world_buf(uint8_t const **ptr, uint32_t *width);
|
||||
uint32_t world_seed(void);
|
||||
ecs_world_t *world_ecs(void);
|
||||
void world_set_stage(ecs_world_t *ecs);
|
||||
librg_world *world_tracker(void);
|
||||
|
||||
// NOTE(zaklaus): World simulation time control
|
||||
void world_pause(void);
|
||||
void world_resume(void);
|
||||
bool world_is_paused(void);
|
||||
void world_step(float step_size);
|
||||
|
||||
uint16_t world_chunk_size(void);
|
||||
uint16_t world_chunk_amount(void);
|
||||
uint16_t world_dim(void);
|
||||
ecs_entity_t world_chunk_mapping(librg_chunk id);
|
||||
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
uint8_t block_id;
|
||||
ecs_entity_t chunk_e;
|
||||
int64_t chunk_id;
|
||||
float ox, oy;
|
||||
} world_block_lookup;
|
||||
|
||||
world_block_lookup world_block_from_realpos(float x, float y);
|
||||
world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx);
|
||||
int64_t world_chunk_from_realpos(float x, float y);
|
||||
int64_t world_chunk_from_entity(ecs_entity_t id);
|
||||
void world_chunk_replace_block(int64_t id, uint16_t block_idx, uint8_t block_id);
|
||||
bool world_chunk_place_block(int64_t id, uint16_t block_idx, uint8_t block_id);
|
||||
uint8_t *world_chunk_get_blocks(int64_t id);
|
||||
void world_chunk_mark_dirty(ecs_entity_t e);
|
||||
uint8_t world_chunk_is_dirty(ecs_entity_t e);
|
||||
|
||||
// NOTE(zaklaus): Uses locally persistent buffer !!
|
||||
int64_t *world_chunk_fetch_entities(librg_chunk chunk_id, size_t *ents_len);
|
||||
int64_t *world_chunk_fetch_entities_realpos(float x, float y, size_t *ents_len);
|
||||
int64_t *world_chunk_query_entities(int64_t e, size_t *ents_len, int8_t radius);
|
||||
|
||||
uint8_t world_entity_valid(ecs_entity_t e);
|
|
@ -1,95 +1,95 @@
|
|||
#include "zpl.h"
|
||||
#include "world_view.h"
|
||||
#include "entity_view.h"
|
||||
#include "prediction.h"
|
||||
#include "librg.h"
|
||||
#include "world/world.h"
|
||||
#include "game.h"
|
||||
|
||||
int32_t tracker_read_remove(librg_world *w, librg_event *e) {
|
||||
int64_t entity_id = librg_event_entity_get(w, e);
|
||||
world_view *view = (world_view*)librg_world_userdata_get(w);
|
||||
entity_view_remove_chunk_texture(&view->entities, entity_id);
|
||||
entity_view_destroy(&view->entities, entity_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t tracker_read_update(librg_world *w, librg_event *e) {
|
||||
int64_t entity_id = librg_event_entity_get(w, e);
|
||||
size_t actual_length = librg_event_size_get(w, e);
|
||||
char *buffer = librg_event_buffer_get(w, e);
|
||||
world_view *view = (world_view*)librg_world_userdata_get(w);
|
||||
|
||||
entity_view data = entity_view_unpack_struct(buffer, actual_length);
|
||||
entity_view *d = entity_view_get(&view->entities, entity_id);
|
||||
#if 1
|
||||
if (d && d->layer_id < view->active_layer_id) {
|
||||
if (zpl_time_rel_ms() - d->last_update > WORLD_TRACKER_UPDATE_NORMAL_MS) {
|
||||
d->layer_id = zpl_min(WORLD_TRACKER_LAYERS-1, d->layer_id+1);
|
||||
}
|
||||
// NOTE(zaklaus): reject updates from slower layers
|
||||
else return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
data.last_update = zpl_time_rel_ms();
|
||||
data.layer_id = view->active_layer_id;
|
||||
predict_receive_update(d, &data);
|
||||
entity_view_update_or_create(&view->entities, entity_id, data);
|
||||
entity_view_remove_chunk_texture(&view->entities, entity_id);
|
||||
entity_view_update_chunk_texture(&view->entities, entity_id, view);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t tracker_read_create(librg_world *w, librg_event *e) {
|
||||
int64_t entity_id = librg_event_entity_get(w, e);
|
||||
size_t actual_length = librg_event_size_get(w, e);
|
||||
char *buffer = librg_event_buffer_get(w, e);
|
||||
world_view *view = (world_view*)librg_world_userdata_get(w);
|
||||
|
||||
entity_view data = entity_view_unpack_struct(buffer, actual_length);
|
||||
data.ent_id = entity_id;
|
||||
data.layer_id = view->active_layer_id;
|
||||
data.tran_time = 0.0f;
|
||||
data.color = rand(); // TODO(zaklaus): feed from server
|
||||
if (data.flag & EFLAG_INTERP) {
|
||||
data.tx = data.x;
|
||||
data.ty = data.y;
|
||||
data.theading = data.heading;
|
||||
}
|
||||
entity_view_update_or_create(&view->entities, entity_id, data);
|
||||
entity_view_mark_for_fadein(&view->entities, entity_id);
|
||||
entity_view_update_chunk_texture(&view->entities, entity_id, view);
|
||||
return 0;
|
||||
}
|
||||
|
||||
world_view world_view_create(uint16_t view_id) {
|
||||
world_view view = {0};
|
||||
view.view_id = view_id;
|
||||
view.tracker = librg_world_create();
|
||||
entity_view_init(&view.entities);
|
||||
return view;
|
||||
}
|
||||
|
||||
void world_view_init(world_view *view, uint32_t seed, uint64_t ent_id, uint16_t chunk_size, uint16_t chunk_amount) {
|
||||
view->seed = seed;
|
||||
view->owner_id = ent_id;
|
||||
view->chunk_size = chunk_size;
|
||||
view->chunk_amount = chunk_amount;
|
||||
view->dim = WORLD_BLOCK_SIZE * chunk_size * chunk_amount;
|
||||
view->size = view->dim * view->dim;
|
||||
|
||||
librg_config_chunksize_set(view->tracker, WORLD_BLOCK_SIZE * chunk_size, WORLD_BLOCK_SIZE * chunk_size, 1);
|
||||
librg_config_chunkamount_set(view->tracker, chunk_amount, chunk_amount, 1);
|
||||
librg_config_chunkoffset_set(view->tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, 0);
|
||||
|
||||
librg_event_set(view->tracker, LIBRG_READ_CREATE, tracker_read_create);
|
||||
librg_event_set(view->tracker, LIBRG_READ_REMOVE, tracker_read_remove);
|
||||
librg_event_set(view->tracker, LIBRG_READ_UPDATE, tracker_read_update);
|
||||
librg_world_userdata_set(view->tracker, view);
|
||||
}
|
||||
|
||||
void world_view_destroy(world_view *view) {
|
||||
librg_world_destroy(view->tracker);
|
||||
entity_view_free(&view->entities);
|
||||
}
|
||||
#include "zpl.h"
|
||||
#include "world_view.h"
|
||||
#include "entity_view.h"
|
||||
#include "prediction.h"
|
||||
#include "librg.h"
|
||||
#include "world/world.h"
|
||||
#include "game.h"
|
||||
|
||||
int32_t tracker_read_remove(librg_world *w, librg_event *e) {
|
||||
int64_t entity_id = librg_event_entity_get(w, e);
|
||||
world_view *view = (world_view*)librg_world_userdata_get(w);
|
||||
entity_view_remove_chunk_texture(&view->entities, entity_id);
|
||||
entity_view_destroy(&view->entities, entity_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t tracker_read_update(librg_world *w, librg_event *e) {
|
||||
int64_t entity_id = librg_event_entity_get(w, e);
|
||||
size_t actual_length = librg_event_size_get(w, e);
|
||||
char *buffer = librg_event_buffer_get(w, e);
|
||||
world_view *view = (world_view*)librg_world_userdata_get(w);
|
||||
|
||||
entity_view data = entity_view_unpack_struct(buffer, actual_length);
|
||||
entity_view *d = entity_view_get(&view->entities, entity_id);
|
||||
#if 1
|
||||
if (d && d->layer_id < view->active_layer_id) {
|
||||
if (zpl_time_rel_ms() - d->last_update > WORLD_TRACKER_UPDATE_NORMAL_MS) {
|
||||
d->layer_id = zpl_min(WORLD_TRACKER_LAYERS-1, d->layer_id+1);
|
||||
}
|
||||
// NOTE(zaklaus): reject updates from slower layers
|
||||
else return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
data.last_update = zpl_time_rel_ms();
|
||||
data.layer_id = view->active_layer_id;
|
||||
predict_receive_update(d, &data);
|
||||
entity_view_update_or_create(&view->entities, entity_id, data);
|
||||
entity_view_remove_chunk_texture(&view->entities, entity_id);
|
||||
entity_view_update_chunk_texture(&view->entities, entity_id, view);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t tracker_read_create(librg_world *w, librg_event *e) {
|
||||
int64_t entity_id = librg_event_entity_get(w, e);
|
||||
size_t actual_length = librg_event_size_get(w, e);
|
||||
char *buffer = librg_event_buffer_get(w, e);
|
||||
world_view *view = (world_view*)librg_world_userdata_get(w);
|
||||
|
||||
entity_view data = entity_view_unpack_struct(buffer, actual_length);
|
||||
data.ent_id = entity_id;
|
||||
data.layer_id = view->active_layer_id;
|
||||
data.tran_time = 0.0f;
|
||||
data.color = rand(); // TODO(zaklaus): feed from server
|
||||
if (data.flag & EFLAG_INTERP) {
|
||||
data.tx = data.x;
|
||||
data.ty = data.y;
|
||||
data.theading = data.heading;
|
||||
}
|
||||
entity_view_update_or_create(&view->entities, entity_id, data);
|
||||
entity_view_mark_for_fadein(&view->entities, entity_id);
|
||||
entity_view_update_chunk_texture(&view->entities, entity_id, view);
|
||||
return 0;
|
||||
}
|
||||
|
||||
world_view world_view_create(uint16_t view_id) {
|
||||
world_view view = {0};
|
||||
view.view_id = view_id;
|
||||
view.tracker = librg_world_create();
|
||||
entity_view_init(&view.entities);
|
||||
return view;
|
||||
}
|
||||
|
||||
void world_view_init(world_view *view, uint32_t seed, uint64_t ent_id, uint16_t chunk_size, uint16_t chunk_amount) {
|
||||
view->seed = seed;
|
||||
view->owner_id = ent_id;
|
||||
view->chunk_size = chunk_size;
|
||||
view->chunk_amount = chunk_amount;
|
||||
view->dim = WORLD_BLOCK_SIZE * chunk_size * chunk_amount;
|
||||
view->size = view->dim * view->dim;
|
||||
|
||||
librg_config_chunksize_set(view->tracker, WORLD_BLOCK_SIZE * chunk_size, WORLD_BLOCK_SIZE * chunk_size, 1);
|
||||
librg_config_chunkamount_set(view->tracker, chunk_amount, chunk_amount, 1);
|
||||
librg_config_chunkoffset_set(view->tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, 0);
|
||||
|
||||
librg_event_set(view->tracker, LIBRG_READ_CREATE, tracker_read_create);
|
||||
librg_event_set(view->tracker, LIBRG_READ_REMOVE, tracker_read_remove);
|
||||
librg_event_set(view->tracker, LIBRG_READ_UPDATE, tracker_read_update);
|
||||
librg_world_userdata_set(view->tracker, view);
|
||||
}
|
||||
|
||||
void world_view_destroy(world_view *view) {
|
||||
librg_world_destroy(view->tracker);
|
||||
entity_view_free(&view->entities);
|
||||
}
|
||||
|
|
|
@ -1,176 +1,176 @@
|
|||
#pragma once
|
||||
#include "flecs/flecs.h"
|
||||
#include "flecs/flecs_meta.h"
|
||||
|
||||
//NOTE(zaklaus): custom macro to define meta components outside the current scope
|
||||
|
||||
#ifndef ECS_META_DEFINE
|
||||
#define ECS_META_DEFINE(world, T)\
|
||||
ECS_COMPONENT_DEFINE(world, T);\
|
||||
ecs_new_meta(world, ecs_entity(T), &__##T##__);
|
||||
#endif
|
||||
|
||||
#ifndef ecs_get_mut_if
|
||||
#define ecs_get_mut_if(world, entity, component)\
|
||||
(ecs_get(world, entity, component) ? ecs_get_mut(world, entity, component, NULL) : NULL)
|
||||
#endif
|
||||
|
||||
#define ITEMS_INVENTORY_SIZE 9
|
||||
|
||||
ECS_STRUCT(Vector2D, {
|
||||
float x;
|
||||
float y;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Chunk, {
|
||||
uint32_t id;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
uint8_t is_dirty;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Drawable, {
|
||||
uint16_t id;
|
||||
});
|
||||
|
||||
ECS_ALIAS(Vector2D, Position);
|
||||
ECS_ALIAS(Vector2D, Velocity);
|
||||
|
||||
ECS_STRUCT(Input, {
|
||||
float x;
|
||||
float y;
|
||||
float mx;
|
||||
float my;
|
||||
uint8_t use;
|
||||
uint8_t sprint;
|
||||
uint8_t ctrl;
|
||||
uint8_t is_blocked;
|
||||
|
||||
// NOTE(zaklaus): inventory
|
||||
uint8_t selected_item;
|
||||
uint8_t drop;
|
||||
uint8_t swap;
|
||||
uint8_t swap_from;
|
||||
uint8_t swap_to;
|
||||
|
||||
// NOTE(zaklaus): build mode
|
||||
uint8_t num_placements;
|
||||
float placements_x[20];
|
||||
float placements_y[20];
|
||||
});
|
||||
|
||||
ECS_STRUCT(ClientInfo, {
|
||||
uintptr_t peer;
|
||||
uint16_t view_id;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Health, {
|
||||
float hp;
|
||||
float max_hp;
|
||||
|
||||
//NOTE(zaklaus): Intentionally global, to allow for creative use of damage combos
|
||||
float pain_time;
|
||||
float heal_time;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Classify, {
|
||||
uint16_t id;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Vehicle, {
|
||||
uint64_t seats[4];
|
||||
|
||||
float force;
|
||||
float heading;
|
||||
float steer;
|
||||
float wheel_base;
|
||||
|
||||
float speed;
|
||||
float reverse_speed;
|
||||
});
|
||||
|
||||
typedef struct {
|
||||
ecs_entity_t veh;
|
||||
} IsInVehicle;
|
||||
|
||||
typedef struct {
|
||||
uint16_t kind;
|
||||
uint32_t quantity;
|
||||
float merger_time;
|
||||
} ItemDrop;
|
||||
|
||||
typedef struct {
|
||||
ItemDrop items[ITEMS_INVENTORY_SIZE];
|
||||
float pickup_time;
|
||||
} Inventory;
|
||||
|
||||
ECS_COMPONENT_EXTERN(Chunk);
|
||||
ECS_COMPONENT_EXTERN(Position);
|
||||
ECS_COMPONENT_EXTERN(Vector2D);
|
||||
ECS_COMPONENT_EXTERN(Drawable);
|
||||
ECS_COMPONENT_EXTERN(Input);
|
||||
ECS_COMPONENT_EXTERN(Velocity);
|
||||
ECS_COMPONENT_EXTERN(ClientInfo);
|
||||
ECS_COMPONENT_EXTERN(Health);
|
||||
ECS_COMPONENT_EXTERN(Classify);
|
||||
ECS_COMPONENT_EXTERN(Vehicle);
|
||||
ECS_COMPONENT_EXTERN(IsInVehicle);
|
||||
ECS_COMPONENT_EXTERN(ItemDrop);
|
||||
ECS_COMPONENT_EXTERN(Inventory);
|
||||
ECS_TAG_EXTERN(EcsActor);
|
||||
ECS_TAG_EXTERN(EcsDemoNPC);
|
||||
ECS_TYPE_EXTERN(Player);
|
||||
ECS_TYPE_EXTERN(Movement);
|
||||
ECS_TYPE_EXTERN(Walking);
|
||||
ECS_TYPE_EXTERN(Flying);
|
||||
ECS_TYPE_EXTERN(EcsClient);
|
||||
// NOTE(zaklaus): @1 EXTERN
|
||||
|
||||
typedef struct {
|
||||
ECS_DECLARE_COMPONENT(Chunk);
|
||||
ECS_DECLARE_COMPONENT(Position);
|
||||
ECS_DECLARE_COMPONENT(Vector2D);
|
||||
ECS_DECLARE_COMPONENT(Drawable);
|
||||
ECS_DECLARE_COMPONENT(Input);
|
||||
ECS_DECLARE_COMPONENT(Velocity);
|
||||
ECS_DECLARE_COMPONENT(ClientInfo);
|
||||
ECS_DECLARE_COMPONENT(Health);
|
||||
ECS_DECLARE_COMPONENT(Classify);
|
||||
ECS_DECLARE_COMPONENT(Vehicle);
|
||||
ECS_DECLARE_COMPONENT(IsInVehicle);
|
||||
ECS_DECLARE_COMPONENT(ItemDrop);
|
||||
ECS_DECLARE_COMPONENT(Inventory);
|
||||
ECS_DECLARE_ENTITY(EcsActor);
|
||||
ECS_DECLARE_ENTITY(EcsDemoNPC);
|
||||
ECS_DECLARE_TYPE(Player);
|
||||
ECS_DECLARE_TYPE(Builder);
|
||||
ECS_DECLARE_TYPE(Movement);
|
||||
ECS_DECLARE_ENTITY(Walking);
|
||||
ECS_DECLARE_ENTITY(Flying);
|
||||
// NOTE(zaklaus): @2 DECLARE
|
||||
} Components;
|
||||
|
||||
#define ComponentsImportHandles(handles)\
|
||||
ECS_IMPORT_COMPONENT(handles, Chunk);\
|
||||
ECS_IMPORT_COMPONENT(handles, Vector2D);\
|
||||
ECS_IMPORT_COMPONENT(handles, Position);\
|
||||
ECS_IMPORT_COMPONENT(handles, Drawable);\
|
||||
ECS_IMPORT_COMPONENT(handles, Input);\
|
||||
ECS_IMPORT_COMPONENT(handles, Velocity);\
|
||||
ECS_IMPORT_COMPONENT(handles, ClientInfo);\
|
||||
ECS_IMPORT_COMPONENT(handles, Health);\
|
||||
ECS_IMPORT_COMPONENT(handles, Classify);\
|
||||
ECS_IMPORT_COMPONENT(handles, Vehicle);\
|
||||
ECS_IMPORT_COMPONENT(handles, IsInVehicle);\
|
||||
ECS_IMPORT_COMPONENT(handles, ItemDrop);\
|
||||
ECS_IMPORT_COMPONENT(handles, Inventory);\
|
||||
ECS_IMPORT_TYPE(handles, Player);\
|
||||
ECS_IMPORT_TYPE(handles, Builder);\
|
||||
ECS_IMPORT_TYPE(handles, Movement);\
|
||||
ECS_IMPORT_ENTITY(handles, EcsActor);\
|
||||
ECS_IMPORT_ENTITY(handles, EcsDemoNPC);\
|
||||
ECS_IMPORT_ENTITY(handles, Walking);\
|
||||
ECS_IMPORT_ENTITY(handles, Flying);\
|
||||
// NOTE(zaklaus): @3 IMPORT
|
||||
|
||||
void ComponentsImport(ecs_world_t *ecs);
|
||||
#pragma once
|
||||
#include "flecs/flecs.h"
|
||||
#include "flecs/flecs_meta.h"
|
||||
|
||||
//NOTE(zaklaus): custom macro to define meta components outside the current scope
|
||||
|
||||
#ifndef ECS_META_DEFINE
|
||||
#define ECS_META_DEFINE(world, T)\
|
||||
ECS_COMPONENT_DEFINE(world, T);\
|
||||
ecs_new_meta(world, ecs_entity(T), &__##T##__);
|
||||
#endif
|
||||
|
||||
#ifndef ecs_get_mut_if
|
||||
#define ecs_get_mut_if(world, entity, component)\
|
||||
(ecs_get(world, entity, component) ? ecs_get_mut(world, entity, component, NULL) : NULL)
|
||||
#endif
|
||||
|
||||
#define ITEMS_INVENTORY_SIZE 9
|
||||
|
||||
ECS_STRUCT(Vector2D, {
|
||||
float x;
|
||||
float y;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Chunk, {
|
||||
uint32_t id;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
uint8_t is_dirty;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Drawable, {
|
||||
uint16_t id;
|
||||
});
|
||||
|
||||
ECS_ALIAS(Vector2D, Position);
|
||||
ECS_ALIAS(Vector2D, Velocity);
|
||||
|
||||
ECS_STRUCT(Input, {
|
||||
float x;
|
||||
float y;
|
||||
float mx;
|
||||
float my;
|
||||
uint8_t use;
|
||||
uint8_t sprint;
|
||||
uint8_t ctrl;
|
||||
uint8_t is_blocked;
|
||||
|
||||
// NOTE(zaklaus): inventory
|
||||
uint8_t selected_item;
|
||||
uint8_t drop;
|
||||
uint8_t swap;
|
||||
uint8_t swap_from;
|
||||
uint8_t swap_to;
|
||||
|
||||
// NOTE(zaklaus): build mode
|
||||
uint8_t num_placements;
|
||||
float placements_x[20];
|
||||
float placements_y[20];
|
||||
});
|
||||
|
||||
ECS_STRUCT(ClientInfo, {
|
||||
uintptr_t peer;
|
||||
uint16_t view_id;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Health, {
|
||||
float hp;
|
||||
float max_hp;
|
||||
|
||||
//NOTE(zaklaus): Intentionally global, to allow for creative use of damage combos
|
||||
float pain_time;
|
||||
float heal_time;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Classify, {
|
||||
uint16_t id;
|
||||
});
|
||||
|
||||
ECS_STRUCT(Vehicle, {
|
||||
uint64_t seats[4];
|
||||
|
||||
float force;
|
||||
float heading;
|
||||
float steer;
|
||||
float wheel_base;
|
||||
|
||||
float speed;
|
||||
float reverse_speed;
|
||||
});
|
||||
|
||||
typedef struct {
|
||||
ecs_entity_t veh;
|
||||
} IsInVehicle;
|
||||
|
||||
typedef struct {
|
||||
uint16_t kind;
|
||||
uint32_t quantity;
|
||||
float merger_time;
|
||||
} ItemDrop;
|
||||
|
||||
typedef struct {
|
||||
ItemDrop items[ITEMS_INVENTORY_SIZE];
|
||||
float pickup_time;
|
||||
} Inventory;
|
||||
|
||||
ECS_COMPONENT_EXTERN(Chunk);
|
||||
ECS_COMPONENT_EXTERN(Position);
|
||||
ECS_COMPONENT_EXTERN(Vector2D);
|
||||
ECS_COMPONENT_EXTERN(Drawable);
|
||||
ECS_COMPONENT_EXTERN(Input);
|
||||
ECS_COMPONENT_EXTERN(Velocity);
|
||||
ECS_COMPONENT_EXTERN(ClientInfo);
|
||||
ECS_COMPONENT_EXTERN(Health);
|
||||
ECS_COMPONENT_EXTERN(Classify);
|
||||
ECS_COMPONENT_EXTERN(Vehicle);
|
||||
ECS_COMPONENT_EXTERN(IsInVehicle);
|
||||
ECS_COMPONENT_EXTERN(ItemDrop);
|
||||
ECS_COMPONENT_EXTERN(Inventory);
|
||||
ECS_TAG_EXTERN(EcsActor);
|
||||
ECS_TAG_EXTERN(EcsDemoNPC);
|
||||
ECS_TYPE_EXTERN(Player);
|
||||
ECS_TYPE_EXTERN(Movement);
|
||||
ECS_TYPE_EXTERN(Walking);
|
||||
ECS_TYPE_EXTERN(Flying);
|
||||
ECS_TYPE_EXTERN(EcsClient);
|
||||
// NOTE(zaklaus): @1 EXTERN
|
||||
|
||||
typedef struct {
|
||||
ECS_DECLARE_COMPONENT(Chunk);
|
||||
ECS_DECLARE_COMPONENT(Position);
|
||||
ECS_DECLARE_COMPONENT(Vector2D);
|
||||
ECS_DECLARE_COMPONENT(Drawable);
|
||||
ECS_DECLARE_COMPONENT(Input);
|
||||
ECS_DECLARE_COMPONENT(Velocity);
|
||||
ECS_DECLARE_COMPONENT(ClientInfo);
|
||||
ECS_DECLARE_COMPONENT(Health);
|
||||
ECS_DECLARE_COMPONENT(Classify);
|
||||
ECS_DECLARE_COMPONENT(Vehicle);
|
||||
ECS_DECLARE_COMPONENT(IsInVehicle);
|
||||
ECS_DECLARE_COMPONENT(ItemDrop);
|
||||
ECS_DECLARE_COMPONENT(Inventory);
|
||||
ECS_DECLARE_ENTITY(EcsActor);
|
||||
ECS_DECLARE_ENTITY(EcsDemoNPC);
|
||||
ECS_DECLARE_TYPE(Player);
|
||||
ECS_DECLARE_TYPE(Builder);
|
||||
ECS_DECLARE_TYPE(Movement);
|
||||
ECS_DECLARE_ENTITY(Walking);
|
||||
ECS_DECLARE_ENTITY(Flying);
|
||||
// NOTE(zaklaus): @2 DECLARE
|
||||
} Components;
|
||||
|
||||
#define ComponentsImportHandles(handles)\
|
||||
ECS_IMPORT_COMPONENT(handles, Chunk);\
|
||||
ECS_IMPORT_COMPONENT(handles, Vector2D);\
|
||||
ECS_IMPORT_COMPONENT(handles, Position);\
|
||||
ECS_IMPORT_COMPONENT(handles, Drawable);\
|
||||
ECS_IMPORT_COMPONENT(handles, Input);\
|
||||
ECS_IMPORT_COMPONENT(handles, Velocity);\
|
||||
ECS_IMPORT_COMPONENT(handles, ClientInfo);\
|
||||
ECS_IMPORT_COMPONENT(handles, Health);\
|
||||
ECS_IMPORT_COMPONENT(handles, Classify);\
|
||||
ECS_IMPORT_COMPONENT(handles, Vehicle);\
|
||||
ECS_IMPORT_COMPONENT(handles, IsInVehicle);\
|
||||
ECS_IMPORT_COMPONENT(handles, ItemDrop);\
|
||||
ECS_IMPORT_COMPONENT(handles, Inventory);\
|
||||
ECS_IMPORT_TYPE(handles, Player);\
|
||||
ECS_IMPORT_TYPE(handles, Builder);\
|
||||
ECS_IMPORT_TYPE(handles, Movement);\
|
||||
ECS_IMPORT_ENTITY(handles, EcsActor);\
|
||||
ECS_IMPORT_ENTITY(handles, EcsDemoNPC);\
|
||||
ECS_IMPORT_ENTITY(handles, Walking);\
|
||||
ECS_IMPORT_ENTITY(handles, Flying);\
|
||||
// NOTE(zaklaus): @3 IMPORT
|
||||
|
||||
void ComponentsImport(ecs_world_t *ecs);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
#define DEMO_NPC_MOVE_SPEED 500
|
||||
#define DEMO_NPC_STEER_SPEED 300
|
||||
|
||||
void DemoNPCMoveAround(ecs_iter_t *it) {
|
||||
Velocity *v = ecs_column(it, Velocity, 1);
|
||||
for (int i = 0; i < it->count; i++) {
|
||||
float d = zpl_quake_rsqrt(v[i].x*v[i].x + v[i].y*v[i].y);
|
||||
v[i].x += (v[i].x*d*DEMO_NPC_MOVE_SPEED*safe_dt(it) + zpl_cos(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED*safe_dt(it));
|
||||
v[i].y += (v[i].y*d*DEMO_NPC_MOVE_SPEED*safe_dt(it) + zpl_sin(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED*safe_dt(it));
|
||||
}
|
||||
}
|
||||
|
||||
#define DEMO_NPC_MOVE_SPEED 500
|
||||
#define DEMO_NPC_STEER_SPEED 300
|
||||
|
||||
void DemoNPCMoveAround(ecs_iter_t *it) {
|
||||
Velocity *v = ecs_column(it, Velocity, 1);
|
||||
for (int i = 0; i < it->count; i++) {
|
||||
float d = zpl_quake_rsqrt(v[i].x*v[i].x + v[i].y*v[i].y);
|
||||
v[i].x += (v[i].x*d*DEMO_NPC_MOVE_SPEED*safe_dt(it) + zpl_cos(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED*safe_dt(it));
|
||||
v[i].y += (v[i].y*d*DEMO_NPC_MOVE_SPEED*safe_dt(it) + zpl_sin(zpl_to_radians(rand()%360))*DEMO_NPC_STEER_SPEED*safe_dt(it));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
|
||||
cd "$script_path/" || exit 1
|
||||
|
||||
find code/modules/ -type f -print0 | xargs -0 dos2unix -ic0 | xargs -0 dos2unix -b
|
||||
find code/game/ -type f -print0 | xargs -0 dos2unix -ic0 | xargs -0 dos2unix -b
|
Loading…
Reference in New Issue