Compare commits

..

No commits in common. "master" and "isolation_bkp/dynres" have entirely different histories.

289 changed files with 27174 additions and 347501 deletions

View File

@ -1,22 +0,0 @@
name: Build web and deploy
on:
workflow_dispatch:
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
BUTLER_API_KEY: ${{ secrets.BUTLER_KEY }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up emsdk
run: web/setup.sh
- name: Build and deploy eco2d
run: web/deploy.sh

8
.gitignore vendored
View File

@ -1,6 +1,6 @@
build
build.*
build_*
build_rel
build_web
emsdk
deploy_web
run_web
@ -20,7 +20,3 @@ GTAGS
pkg
pkg.zip
eco2d.zip
eco2d.db
eco2d.sublime-workspace
.cache

View File

@ -1,11 +0,0 @@
{
"version": 2,
"buildPresets": [
{
"name": "vs2022-debug",
"displayName": "Visual Studio Community 2022 Release - x86_amd64 - Debug",
"configurePreset": "vs2022",
"configuration": "Debug"
}
]
}

View File

@ -1,6 +1,6 @@
<h1>
eco2d
</h1>
<div align="center">
<a href="https://zaklaus.itch.io/eco2d"><img src="https://user-images.githubusercontent.com/2182108/111983468-d5593e80-8b12-11eb-9c59-8c78ecc0504e.png" alt="eco2d" /></a>
</div>
<br />
@ -23,7 +23,7 @@
</div>
# Introduction
eco2d is a experimental set of games made out of curiosity. It attempts to bridge several libraries to create a playable sandbox with ease of extensibility and with performance in mind. The goal is not to make a generic 2D game engine but to build a game prototype that anyone can use to build various experiments.
zpl.eco2d is a small framework/game engine made out of curiosity. It attempts to bridge several libraries to create a playable sandbox with ease of extensibility and with performance in mind. The goal is not to make a generic 2D game engine but to build a game prototype that anyone can use to build various experiments.
The game runs on top of [raylib](https://raylib.com/) technologies and makes use of the [zpl](https://zpl.pw/) ecosystem alongside the **cwpack** library for data serialization. The game logic and lifecycle are maintained using [flecs](https://github.com/SanderMertens/flecs/) library and its suite of tools that help us improve the development efficiency.
@ -31,7 +31,7 @@ It was built with networked game sessions in mind from the ground up and therefo
* Networked game (UDP) - networked sessions benefit from the use of [enet](https://github.com/zpl-c/enet/) library.
* Local Only sessions - Data is streamed via local buffers.
In both cases, the game engine does not differentiate between these two options and makes the concept of Server<>client infrastructure entirely abstract. As a result, gameplay logic is only tied to living entities, where the entity might represent a networked client / local controller.
In both cases, the game engine does not differentiate between these two options and makes the concept of Server <>client infrastructure entirely abstract. As a result, gameplay logic is only tied to living entities, where the entity might represent a networked client / local controller.
All data is transferred via packets fully automated by our serialization rule system, which uses the **cwpack** library in the background.
@ -90,7 +90,7 @@ build\eco2d.exe -?
```
# License
eco2d code is licensed under the BSD 3-Clause license, as seen [here](LICENSE).
zpl.eco2d code is licensed under the BSD 3-Clause license, as seen [here](LICENSE).
Assets under the **art** folder are released into [Public Domain](https://creativecommons.org/share-your-work/public-domain/cc0/) unless otherwise stated.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

After

Width:  |  Height:  |  Size: 254 B

View File

@ -1,28 +0,0 @@
-- typedef enum {
-- BLOCK_FLAG_COLLISION = (1 << 1),
-- BLOCK_FLAG_HAZARD = (1 << 2),
-- BLOCK_FLAG_ESSENTIAL = (1 << 3),
-- BLOCK_FLAG_DESTROY_ON_COLLISION = (1 << 4),
-- BLOCK_FLAG_ENTITY = (1 << 5),
-- } block_flags;
INSERT INTO blocks (kind, flags, drag, friction, bounce, velx, vely) VALUES
(asset('EMPTY'), NULL, NULL, NULL, NULL, NULL, NULL),
(asset('GROUND'), NULL, 1.0, 1.0, NULL, NULL, NULL),
(asset('DIRT'), NULL, 2.1, 1.0, NULL, NULL, NULL),
(asset('WALL'), (1<<1), 1.0, 1.0, 1.0, NULL, NULL),
(asset('HILL'), (1<<1), 1.0, 1.0, NULL, NULL, NULL),
(asset('HILL_SNOW'), (1<<1), 1.0, 1.0, NULL, NULL, NULL),
(asset('WATER'), 0, 0.11, 1.0, NULL, NULL, NULL),
(asset('LAVA'), (1<<2), 6.2, 4.0, NULL, NULL, NULL),
(asset('FENCE'), (1<<1), 1.0, 1.0, 1.0, NULL, NULL),
(asset('WOOD'), (1<<1), 1.0, 1.0, 0.0, NULL, NULL),
(asset('TREE'), (1<<1)|(1<<4), 1.0, 1.0, 0.0, NULL, NULL),
(asset('CHEST'), (1<<5), NULL, NULL, NULL, NULL, NULL),
(asset('FURNACE'), (1<<5), NULL, NULL, NULL, NULL, NULL),
(asset('TEST_TALL'), (1<<1), NULL, NULL, NULL, NULL, NULL),
(asset('BELT_LEFT'), NULL, 1.0, 1.0, NULL, -150.0, NULL),
(asset('BELT_RIGHT'), NULL, 1.0, 1.0, NULL, 150.0, NULL),
(asset('BELT_UP'), NULL, 1.0, 1.0, NULL, NULL, -150.0),
(asset('BELT_DOWN'), NULL, 1.0, 1.0, NULL, NULL, 150.0);

View File

@ -1,46 +0,0 @@
-- typedef enum {
-- // NOTE(zaklaus): hardcoded fields for placement ops
-- UKIND_DELETE,
-- UKIND_PLACE,
-- UKIND_PLACE_ITEM,
-- UKIND_PLACE_ITEM_DATA,
-- UKIND_END_PLACE,
--
-- // NOTE(zaklaus): the rest of possible actions
-- UKIND_HOLD,
-- UKIND_PROXY,
-- } item_usage;
--
-- typedef enum {
-- UDATA_NONE,
-- UDATA_ENERGY_SOURCE,
-- } item_attachment;
INSERT INTO items (kind, usage, attachment, max_quantity, place_kind, directional,
proxy_id, place_item_id, producer, energy_level, blueprint_w, blueprint_h, blueprint_plan)
VALUES
(0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('FENCE'), 5, 0, 64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('COAL'), 5, 1, 64, NULL, NULL, NULL, NULL, asset('FURNACE'), 15.0, NULL, NULL, NULL),
(asset('WOOD'), 1, 0, 64, asset('WOOD'), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('TREE'), 1, 0, 64, asset('TREE'), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('TEST_TALL'), 1, 0, 64, asset('TEST_TALL'), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('BELT'), 1, 0, 999, asset('BELT'), 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('BELT_LEFT'), 6, 0, NULL, NULL, NULL, asset('BELT'), NULL, NULL, NULL, NULL, NULL, NULL),
(asset('BELT_RIGHT'), 6, 0, NULL, NULL, NULL, asset('BELT'), NULL, NULL, NULL, NULL, NULL, NULL),
(asset('BELT_UP'), 6, 0, NULL, NULL, NULL, asset('BELT'), NULL, NULL, NULL, NULL, NULL, NULL),
(asset('BELT_DOWN'), 6, 0, NULL, NULL, NULL, asset('BELT'), NULL, NULL, NULL, NULL, NULL, NULL),
(asset('CHEST'), 2, 0, 32, NULL, NULL, NULL, asset('CHEST'), NULL, NULL, NULL, NULL, NULL),
(asset('CRAFTBENCH'), 2, 0, 32, NULL, NULL, NULL, asset('CRAFTBENCH'), NULL, NULL, NULL, NULL, NULL),
(asset('FURNACE'), 2, 0, 32, NULL, NULL, NULL, asset('FURNACE'), NULL, NULL, NULL, NULL, NULL),
(asset('SPLITTER'), 2, 0, 32, NULL, NULL, NULL, asset('SPLITTER'), NULL, NULL, NULL, NULL, NULL),
(asset('ASSEMBLER'), 2, 0, 32, NULL, NULL, NULL, asset('ASSEMBLER'), NULL, NULL, NULL, NULL, NULL),
(asset('CREATURE'), 2, 0, 32, NULL, NULL, NULL, asset('CREATURE'), NULL, NULL, NULL, NULL, NULL),
(asset('IRON_ORE'), 5, 0, 64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('IRON_INGOT'), 5, 0, 64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('IRON_PLATES'), 5, 0, 64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('SCREWS'), 5, 0, 64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('LOG'), 5, 0, 64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(asset('PLANK'), 5, 0, 64, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
-- (asset('BLUEPRINT_DEMO_HOUSE'), 3, 0, 1, NULL, NULL, NULL, asset('BLUEPRINT'), NULL, NULL, 4, 4, ''),
(asset('CREATURE_FOOD'), 5, 0, 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

View File

@ -1 +0,0 @@
Only modify these for eco2d game specifically!

View File

@ -1,18 +0,0 @@
INSERT INTO recipes (product, product_qty, process_ticks, producer) VALUES
(asset('IRON_PLATES'), 4, 20, asset('FURNACE')),
(asset('SCREWS'), 8, 40, asset('CRAFTBENCH')),
(asset('BELT'), 1, 120, asset('ASSEMBLER'));
INSERT INTO reagents (asset_id, qty) VALUES
(asset('IRON_ORE'), 1),
(asset('IRON_PLATES'), 1),
(asset('FENCE'), 1),
(asset('SCREWS'), 4),
(asset('IRON_PLATES'), 2);
INSERT INTO recipe_reagents (recipe_id, reagent_id) VALUES
(1, 1),
(2, 2),
(3, 3),
(3, 4),
(3, 5);

View File

@ -1,42 +0,0 @@
INSERT INTO resources (asset, kind) VALUES
(asset('EMPTY'), 0),
(asset('BLANK'), 0),
(asset('BLOCK_FRAME'), 0),
(asset('BUILDMODE_HIGHLIGHT'), 0),
(asset('COAL'), 0),
(asset('IRON_ORE'), 0),
(asset('IRON_INGOT'), 0),
(asset('IRON_PLATES'), 0),
(asset('SCREWS'), 0),
(asset('LOG'), 0),
(asset('PLANK'), 0),
(asset('CHEST'), 0),
(asset('CREATURE'), 0),
(asset('CREATURE_FOOD'), 0),
(asset('FURNACE'), 0),
(asset('SPLITTER'), 0),
(asset('ASSEMBLER'), 0),
(asset('CRAFTBENCH'), 0),
(asset('BLUEPRINT'), 0),
(asset('BLUEPRINT_DEMO_HOUSE'), 0),
(asset('MOB'), 0),
(asset('PLAYER'), 0),
(asset('FENCE'), 0),
(asset('DEV'), 0),
(asset('GROUND'), 0),
(asset('DIRT'), 0),
(asset('WATER'), 2),
(asset('LAVA'), 0),
(asset('WALL'), 0),
(asset('HILL'), 0),
(asset('HILL_SNOW'), 0),
(asset('HOLE'), 0),
(asset('WOOD'), 0),
(asset('TEST_TALL'), 0),
(asset('TREE'), 0),
(asset('BELT'), 0),
(asset('BELT_LEFT'), 0),
(asset('BELT_RIGHT'), 0),
(asset('BELT_UP'), 0),
(asset('BELT_DOWN'), 0);

View File

@ -1,72 +0,0 @@
CREATE TABLE assets (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL
);
CREATE TABLE resources (
id INTEGER PRIMARY KEY AUTOINCREMENT,
asset INTEGER NOT NULL,
kind INTEGER NOT NULL
);
CREATE TABLE blocks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kind INTEGER NOT NULL,
flags INTEGER DEFAULT 0,
drag REAL,
friction REAL,
bounce REAL,
velx REAL,
vely REAL,
FOREIGN KEY (kind) REFERENCES assets(id)
);
CREATE TABLE items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kind INTEGER NOT NULL,
usage INTEGER,
attachment INTEGER,
max_quantity INTEGER,
has_storage BOOLEAN,
place_kind INTEGER,
directional BOOLEAN,
proxy_id INTEGER,
place_item_id INTEGER,
producer INTEGER,
energy_level REAL,
blueprint_w INTEGER,
blueprint_h INTEGER,
blueprint_plan TEXT,
FOREIGN KEY (kind) REFERENCES assets(id)
);
CREATE TABLE reagents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
asset_id INTEGER NOT NULL,
qty INTEGER NOT NULL,
FOREIGN KEY (asset_id) REFERENCES assets(id)
);
CREATE TABLE recipes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product INTEGER NOT NULL,
product_qty INTEGER NOT NULL,
process_ticks INTEGER NOT NULL,
producer INTEGER NOT NULL,
FOREIGN KEY (product) REFERENCES assets(id),
FOREIGN KEY (producer) REFERENCES assets(id)
);
CREATE TABLE recipe_reagents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
recipe_id INTEGER NOT NULL,
reagent_id INTEGER NOT NULL,
FOREIGN KEY (recipe_id) REFERENCES recipes(id),
FOREIGN KEY (reagent_id) REFERENCES reagents(id)
);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1,3 +0,0 @@
@echo off
call win\setup_cl_generic.bat amd64
cmake --build build && build\eco2d

View File

@ -1,20 +1,20 @@
find_package(raylib 3.5 QUIET)
if (NOT raylib_FOUND)
include(FetchContent)
FetchContent_Declare(
raylib
URL https://github.com/zpl-c/raylib/archive/master.tar.gz
)
FetchContent_GetProperties(raylib)
if (NOT raylib_POPULATED)
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR})
endif()
endif()
find_package(raylib 3.5 QUIET)
if (NOT raylib_FOUND)
include(FetchContent)
FetchContent_Declare(
raylib
URL https://github.com/zpl-c/raylib/archive/master.tar.gz
)
FetchContent_GetProperties(raylib)
if (NOT raylib_POPULATED)
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR})
endif()
endif()

View File

@ -8,12 +8,6 @@ function(link_system_libs target_name)
elseif (UNIX)
target_link_libraries(${target_name} pthread m dl atomic)
endif()
if (WIN32)
target_compile_options(${target_name} PRIVATE -WX -W3 -wd5105)
else()
target_compile_options(${target_name} PRIVATE -Werror -Wall -Wextra -Wno-unused-function -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-parameter)
endif ()
endfunction()
macro(use_cxx11)

View File

@ -3,29 +3,21 @@ file(GLOB PKT_SRCS src/packets/*.h src/packets/*.c)
add_library(eco2d-foundation STATIC
src/core/game.c
src/core/camera.c
src/core/rules_default.c
src/platform/signal_handling.c
src/platform/profiler.c
src/platform/input.c
src/models/database.c
src/models/assets.c
src/models/components.c
src/models/items.c
src/models/entity.c
src/models/device.c
src/models/crafting.c
src/models/prefabs/player.c
src/models/prefabs/vehicle.c
src/ents/items.c
src/ents/entity.c
src/ents/player.c
src/ents/vehicle.c
src/ents/storage.c
src/ents/device.c
src/pkt/packet.c
src/gen/texgen_fallback.c
src/dev/debug_ui.c
src/dev/debug_draw.c
src/debug/debug_ui.c
src/debug/debug_draw.c
src/utils/options.c
src/utils/compress.c
@ -38,14 +30,20 @@ add_library(eco2d-foundation STATIC
src/world/world_view.c
src/world/entity_view.c
src/world/prediction.c
src/world/worldgen/worldgen_test.c
src/systems/systems.c
src/gen/assets.c
src/gen/texgen.c
src/ecs/systems.c
src/ecs/components.c
${PKT_SRCS}
)
target_compile_definitions(eco2d-foundation PRIVATE CLIENT)
include_directories(src ../modules ../../art/gen)
target_link_libraries(eco2d-foundation raylib raylib_nuklear cwpack flecs-bundle vendors-bundle)
target_link_libraries(eco2d-foundation raylib cwpack flecs-bundle vendors-bundle)
target_compile_options(eco2d-foundation PRIVATE -Werror -Wall -Wextra -Wno-unused-function -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-parameter)
link_system_libs(eco2d-foundation)

View File

@ -1,58 +0,0 @@
#pragma once
#define ASSET_INVALID 0xFF
#define _ASSETS\
X(ASSET_EMPTY)\
X(ASSET_BLANK)\
X(ASSET_BLOCK_FRAME)\
X(ASSET_BUILDMODE_HIGHLIGHT)\
X(ASSET_PLAYER)\
X(ASSET_THING)\
X(ASSET_CREATURE)\
X(ASSET_CREATURE_FOOD)\
X(ASSET_CHEST)\
X(ASSET_SPLITTER)\
X(ASSET_ASSEMBLER)\
X(ASSET_FURNACE)\
X(ASSET_CRAFTBENCH)\
X(ASSET_BLUEPRINT_BEGIN)\
X(ASSET_BLUEPRINT)\
X(ASSET_BLUEPRINT_DEMO_HOUSE)\
X(ASSET_BLUEPRINT_END)\
X(ASSET_FENCE)\
X(ASSET_DEV)\
X(ASSET_GROUND)\
X(ASSET_DIRT)\
X(ASSET_WATER)\
X(ASSET_LAVA)\
X(ASSET_WALL)\
X(ASSET_HILL)\
X(ASSET_HILL_SNOW)\
X(ASSET_HOLE)\
X(ASSET_WOOD)\
X(ASSET_TREE)\
X(ASSET_COAL)\
X(ASSET_IRON_ORE)\
X(ASSET_IRON_INGOT)\
X(ASSET_IRON_PLATES)\
X(ASSET_SCREWS)\
X(ASSET_LOG)\
X(ASSET_PLANK)\
X(ASSET_TEST_TALL)\
X(ASSET_BELT)\
X(ASSET_BELT_LEFT)\
X(ASSET_BELT_RIGHT)\
X(ASSET_BELT_UP)\
X(ASSET_BELT_DOWN)
typedef enum {
#define X(idx) idx,
_ASSETS
#undef X
MAX_INTERNAL_ASSETS,
NEXT_FREE_ASSET = MAX_INTERNAL_ASSETS,
MAX_ASSETS = 255
} asset_id;
extern const char *asset_names[];

View File

@ -3,44 +3,36 @@
#include "platform/platform.h"
#include "world/world.h"
#include "pkt/packet.h"
#include "models/database.h"
#include "platform/signal_handling.h"
#include "net/network.h"
#include "models/entity.h"
#include "ents/entity.h"
#include "ents/items.h"
#include "world/world_view.h"
#include "world/entity_view.h"
#include "core/camera.h"
#include "platform/profiler.h"
#include "platform/renderer.h"
#include "flecs/flecs_os_api_stdcpp.h"
#include "flecs.h"
#include "flecs/flecs.h"
#include "models/components.h"
#include "systems/systems.h"
#include "ecs/components.h"
#include "ecs/systems.h"
#include "packets/pkt_00_init.h"
#include "packets/pkt_01_welcome.h"
#include "packets/pkt_switch_viewer.h"
#define RAYLIB_NUKLEAR_IMPLEMENTATION
ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(0)
#include "raylib-nuklear.h"
ZPL_DIAGNOSTIC_POP
static uint8_t game_mode;
static uint8_t game_should_close;
static world_view *world_viewers;
static world_view *active_viewer;
struct nk_context *game_ui = 0;
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 {
@ -75,7 +67,7 @@ static WORLD_PKT_WRITER(mp_cli_pkt_writer) {
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));
}
@ -101,7 +93,7 @@ void game_world_view_cycle_active(int8_t dir) {
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 < zpl_buffer_count(world_viewers));
ZPL_ASSERT(idx >= 0 && idx < zpl_buffer_count(world_viewers));
game_world_view_set_active(&world_viewers[idx]);
}
@ -134,40 +126,42 @@ float game_time() {
return (float)get_cached_time();
}
void game_setup(const char *ip, uint16_t port, game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) {
void game_init(const char *ip, uint16_t port, game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled) {
game_mode = play_mode;
game_should_close = false;
entity_default_spawnlist();
game_init(db_init());
#ifndef _DEBUG
const char *host_ip = "lab.zakto.pw";
#else
const char *host_ip = "127.0.0.1";
#endif
uint16_t host_port = (port > 0) ? port : 27000;
if (ip != NULL) {
host_ip = ip;
}
// NOTE: initialise subsystems
{
assets_setup();
blocks_setup();
item_setup();
entity_spawndef_setup();
}
if (game_mode != GAMEKIND_HEADLESS) {
platform_init();
world_viewers_init(num_viewers);
active_viewer = &world_viewers[0];
camera_reset();
Font font = LoadFontEx("art/anonymous_pro_bold.ttf", 14, 0, 0);
game_ui = InitNuklearEx(font, 14);
}
if (game_mode != GAMEKIND_SINGLE) {
network_init();
}
if (game_mode == GAMEKIND_CLIENT) {
world_setup_pkt_handlers(pkt_reader, mp_cli_pkt_writer);
network_client_connect(host_ip, host_port);
@ -176,15 +170,13 @@ void game_setup(const char *ip, uint16_t port, game_kind play_mode, uint32_t num
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);
}
}
game_init_ecs();
if (game_mode == GAMEKIND_SINGLE) {
for (uint32_t i = 0; i < num_viewers; i++) {
pkt_00_init_send(i);
@ -197,29 +189,35 @@ int8_t game_is_networked() {
}
void game_shutdown() {
db_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();
UnloadNuklear(game_ui);
}
}
// NOTE: shutdown subsystems
{
item_cleanup();
entity_spawndef_cleanup();
blocks_cleanup();
assets_cleanup();
}
}
uint8_t game_is_running() {
@ -234,24 +232,23 @@ game_kind game_get_kind(void) {
return game_mode;
}
void game_core_input() {
void game_input() {
if (game_mode != GAMEKIND_HEADLESS) {
platform_input();
UpdateNuklear(game_ui);
}
}
void game_core_update() {
void game_update() {
static double last_update = 0.0f;
if (game_mode == GAMEKIND_CLIENT) {
network_client_tick();
}
else {
world_update();
if (game_mode == GAMEKIND_HEADLESS) {
network_server_tick();
static float ms_report = 2.5f;
if (ms_report < get_cached_time()) {
ms_report = get_cached_time() + 5.f;
@ -259,20 +256,16 @@ void game_core_update() {
}
}
}
last_update = get_cached_time();
}
void game_core_render() {
void game_render() {
if (game_mode != GAMEKIND_HEADLESS) {
platform_render();
}
}
void game_draw_ui() {
DrawNuklear(game_ui);
}
void game_action_send_keystate(game_keystate_data *data) {
pkt_send_keystate_send(active_viewer->view_id, data);
}
@ -291,71 +284,3 @@ void game_request_close() {
platform_request_close();
}
}
static game_world_render_entry* render_queue = NULL;
static void game__world_view_render_push_entry(uint64_t key, entity_view * data) {
if (!data) return;
if (data->kind == EKIND_CHUNK) {
world_view *view = game_world_view_get_active();
float size = (float)(view->chunk_size * WORLD_BLOCK_SIZE);
float offset = 0.0;
for (size_t ty = 0; ty < view->chunk_size; ty++) {
for (size_t tx = 0; tx < view->chunk_size; tx++) {
block_id blk_id = data->outer_blocks[(ty*view->chunk_size)+tx];
if (blk_id != 0) {
game_world_render_entry entry = {
.key = key,
.data = data,
.blk_id = blk_id,
.x = (data->x*size + offset) + (float)tx*WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2,
.y = (data->y*size + offset) + (float)ty*WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2,
.cy = (data->y*size + offset) + (float)ty*WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2,
};
if (!(blocks_get_flags(blk_id) & BLOCK_FLAG_COLLISION)) {
entry.cy = ZPL_F32_MIN;
}
zpl_array_append(render_queue, entry);
}
}
}
return;
}
game_world_render_entry entry = {
.key = key,
.data = data,
.x = data->x,
.y = data->y,
.cy = data->y,
.blk_id = 0,
};
zpl_array_append(render_queue, entry);
}
static void game__world_view_render_ground(uint64_t key, entity_view * data) {
if (data->kind != EKIND_CHUNK) return;
renderer_draw_entry(key, data, 0);
}
void game_world_view_render_world(void) {
if (!render_queue) {
zpl_array_init(render_queue, zpl_heap());
}
zpl_array_clear(render_queue);
profile(PROF_RENDER_PUSH_AND_SORT_ENTRIES) {
game_world_view_active_entity_map(game__world_view_render_push_entry);
zpl_sort_array(render_queue, zpl_array_count(render_queue), zpl_f32_cmp(zpl_offset_of(game_world_render_entry, cy)));
}
game_world_view_active_entity_map(game__world_view_render_ground);
for (zpl_isize i = 0; i < zpl_array_count(render_queue); i++) {
renderer_draw_entry(render_queue[i].key, render_queue[i].data, &render_queue[i]);
}
}

View File

@ -2,7 +2,6 @@
#include "platform/system.h"
#include "world/world_view.h"
#include "packets/pkt_send_keystate.h"
#include "packets/pkt_send_code.h"
typedef enum {
GAMEKIND_SINGLE,
@ -11,7 +10,7 @@ typedef enum {
FORCE_GAMEKIND_UINT8 = UINT8_MAX
} game_kind;
void game_setup(const char *ip, uint16_t port, game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t chunk_size, uint16_t chunk_amount, int8_t is_dash_enabled);
void game_init(const char *ip, uint16_t port, 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();
@ -20,24 +19,9 @@ float game_time();
game_kind game_get_kind(void);
//~ NOTE(zaklaus): game events
// Implemented by games
void game_init(bool new_db);
void game_init_ecs(); // called once the world is initialised
void game_input();
void game_update();
void game_render();
void game_player_joined(uint64_t ent);
void game_player_departed(uint64_t ent);
void game_player_died(uint64_t ent);
void game_client_receive_code(pkt_send_code data);
// base methods called from games
void game_core_input();
void game_core_update();
void game_core_render();
//~ Called from platform.c
void game_draw_ui();
//~ NOTE(zaklaus): world view management
world_view *game_world_view_get_active(void);
@ -48,7 +32,6 @@ 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);
void game_world_view_render_world(void);
//~ NOTE(zaklaus): viewer -> host actions
void game_action_send_keystate(game_keystate_data *data);

View File

@ -1,33 +0,0 @@
#pragma once
#include "platform/system.h"
typedef struct {
float phy_walk_drag;
uint64_t demo_npc_move_speed;
uint64_t demo_npc_steer_speed;
float item_pick_radius;
float item_merger_radius;
float item_attract_radius;
float item_attract_force;
float item_container_reach_radius;
float item_drop_pickup_time;
float item_drop_merger_time;
float plr_move_speed;
float plr_move_speed_mult;
float vehicle_force;
float vehicle_accel;
float vehicle_decel;
float vehicle_steer;
float vehicle_steer_compensation;
float vehicle_steer_revert;
float vehicle_power;
float vehicle_brake_force;
float veh_enter_radius;
float blueprint_build_time;
// survival rules
} game_rulesdef;
extern game_rulesdef game_rules;
void rules_setup();

View File

@ -1,26 +0,0 @@
#include "core/rules.h"
game_rulesdef game_rules = {
.phy_walk_drag = 4.23f,
.demo_npc_move_speed = 500,
.demo_npc_steer_speed = 300,
.item_pick_radius = 25.0f,
.item_merger_radius = 75.0f,
.item_attract_radius = 75.0f,
.item_attract_force = 1.98f,
.item_container_reach_radius = 105.0f,
.item_drop_pickup_time = 2.5f,
.item_drop_merger_time = 6.5f,
.plr_move_speed = 800.0f,
.plr_move_speed_mult = 1.5f,
.vehicle_force = 240.8f,
.vehicle_accel = 0.032f,
.vehicle_decel = 0.28f,
.vehicle_steer = 35.89f,
.vehicle_steer_compensation = 4.0f,
.vehicle_steer_revert = 6.0941816f,
.vehicle_power = 97.89f,
.vehicle_brake_force = 0.84f,
.veh_enter_radius = 45.0f,
.blueprint_build_time = 1.5f,
};

View File

@ -1,9 +1,9 @@
#include "dev/debug_draw.h"
#include "debug/debug_draw.h"
#include "core/game.h"
static debug_draw_queue draw_queue = {0};
#if !defined(_DEBUG) || 0
#ifndef _DEBUG
static bool draw_is_enabled = false;
#else
static bool draw_is_enabled = true;

View File

@ -1,17 +1,20 @@
#include "dev/debug_replay.h"
#include "debug/debug_replay.h"
#include "core/camera.h"
#include "models/entity.h"
#include "ents/entity.h"
#include "cwpack/cwpack.h"
typedef enum {
RPKIND_KEY,
// NOTE(zaklaus): Special actions
RPKIND_SPAWN_CAR,
RPKIND_PLACE_ICE_RINK,
RPKIND_PLACE_ERASE_CHANGES,
RPKIND_SPAWN_CIRCLING_DRIVER,
RPKIND_SPAWN_ICEMAKER_ITEM,
RPKIND_SPAWN_CHEST,
RPKIND_SPAWN_BELT,
} replay_kind;
typedef struct {
@ -42,17 +45,17 @@ static char replaybuf[sizeof(replay_record)*UINT16_MAX + 32];
void debug_replay_store(void) {
ZPL_ASSERT(replay_filename[0]);
if (!records) return;
cw_pack_context pc = {0};
cw_pack_context_init(&pc, replaybuf, sizeof(replaybuf), 0);
cw_pack_unsigned(&pc, REPLAY_MAGIC);
cw_pack_unsigned(&pc, REPLAY_VERSION);
cw_pack_array_size(&pc, (uint32_t)zpl_array_count(records));
for (int i = 0; i < zpl_array_count(records); i++) {
cw_pack_bin(&pc, &records[i], sizeof(replay_record));
}
zpl_file f = {0};
zpl_file_create(&f, replay_filename);
zpl_file_write(&f, replaybuf, pc.current - pc.start);
@ -61,44 +64,44 @@ void debug_replay_store(void) {
void debug_replay_load(void) {
ZPL_ASSERT(replay_filename[0]);
zpl_file f = {0};
zpl_file_error err = zpl_file_open(&f, replay_filename);
ZPL_ASSERT(err == ZPL_FILE_ERROR_NONE);
size_t file_size = zpl_file_size(&f);
zpl_file_read(&f, replaybuf, file_size);
zpl_file_close(&f);
cw_unpack_context uc = {0};
cw_unpack_context_init(&uc, replaybuf, (uint32_t)file_size, 0);
cw_unpack_next(&uc);
ZPL_ASSERT(uc.item.type == CWP_ITEM_POSITIVE_INTEGER && uc.item.as.u64 == REPLAY_MAGIC);
cw_unpack_next(&uc);
ZPL_ASSERT(uc.item.type == CWP_ITEM_POSITIVE_INTEGER);
uint64_t version = uc.item.as.u64;
ZPL_ASSERT(version >= 2);
cw_unpack_next(&uc);
ZPL_ASSERT(uc.item.type == CWP_ITEM_ARRAY);
size_t items = uc.item.as.array.size;
zpl_array_init_reserve(records, zpl_heap(), sizeof(replay_record)*items);
for (size_t i = 0; i < items; i++) {
cw_unpack_next(&uc);
ZPL_ASSERT(uc.item.type == CWP_ITEM_BIN);
replay_record rec = {0};
switch (version) {
case 2:{
debug_replay_load_record_v2(&rec, uc.item.as.bin.start);
}break;
default:{
zpl_memcopy(&rec, uc.item.as.bin.start, sizeof(replay_record));
}break;
@ -109,10 +112,10 @@ void debug_replay_load(void) {
void debug_replay_start(void) {
is_recording = true;
if (records) zpl_array_free(records);
zpl_array_init_reserve(records, zpl_heap(), UINT16_MAX);
last_record_time = get_cached_time();
SetTargetFPS(60);
}
@ -127,17 +130,17 @@ void debug_replay_clear(void) {
void debug_replay_cleanup_ents(void) {
SetTargetFPS(0);
if (!mime) return;
entity_despawn(mime);
mime = 0;
is_playing = false;
camera_set_follow(plr);
for (int i = 0; i < zpl_array_count(temp_actors); i++) {
entity_despawn(temp_actors[i]);
}
zpl_array_free(temp_actors);
}
@ -154,17 +157,17 @@ void debug_replay_run(void) {
record_pos = 0;
playback_time = get_cached_time();
zpl_array_init(temp_actors, zpl_heap());
plr = camera_get().ent_id;
Position const *p1 = ecs_get(world_ecs(), plr, Position);
mime = entity_spawn(EKIND_MACRO_BOT);
Position *pos = ecs_get_mut(world_ecs(), mime, Position);
*pos = *p1;
ecs_set(world_ecs(), mime, Input, {0});
ecs_set(world_ecs(), mime, Inventory, {0});
camera_set_follow(mime);
SetTargetFPS(60);
}
@ -172,17 +175,17 @@ void debug_replay_run(void) {
void ActPlaceIceRink(void);
void ActSpawnCirclingDriver(void);
void ActEraseWorldChanges(void);
void ActSpawnCoal(void);
void ActSpawnIcemaker(void);
void ActSpawnChest(void);
void ActSpawnBelt(void);
void debug_replay_update(void) {
if (!is_playing) return;
if (playback_time >= get_cached_time()) return;
replay_record *r = &records[record_pos];
playback_time = get_cached_time() + r->delay;
switch (r->kind) {
case RPKIND_KEY: {
Input *i = ecs_get_mut(world_ecs(), mime, Input);
@ -205,12 +208,12 @@ void debug_replay_update(void) {
}
}break;
case RPKIND_SPAWN_CAR: {
ecs_entity_t e = vehicle_spawn(EVEH_CAR);
ecs_entity_t e = vehicle_spawn();
Position const *origin = ecs_get(world_ecs(), mime, Position);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
zpl_array_append(temp_actors, e);
}break;
case RPKIND_PLACE_ICE_RINK: {
@ -222,13 +225,22 @@ void debug_replay_update(void) {
case RPKIND_PLACE_ERASE_CHANGES:{
ActEraseWorldChanges();
}break;
case RPKIND_SPAWN_ICEMAKER_ITEM:{
ActSpawnIcemaker();
}break;
case RPKIND_SPAWN_CHEST:{
ActSpawnChest();
}break;
case RPKIND_SPAWN_BELT:{
ActSpawnBelt();
}break;
default: {
ZPL_PANIC("unreachable");
}break;
}
record_pos += 1;
// NOTE(zaklaus): remove our dummy art exhibist
if (mime && record_pos == zpl_array_count(records)) {
debug_replay_cleanup_ents();
@ -238,13 +250,13 @@ void debug_replay_update(void) {
void debug_replay_record_keystate(pkt_send_keystate state) {
if (!is_recording) return;
double record_time = get_cached_time();
replay_record rec = {
.kind = RPKIND_KEY,
.pkt = state,
.delay = (record_time - last_record_time),
};
zpl_array_append(records, rec);
last_record_time = get_cached_time();
}
@ -253,12 +265,12 @@ void debug_replay_special_action(replay_kind kind) {
ZPL_ASSERT(kind != RPKIND_KEY);
if (!is_recording || is_playing) return;
double record_time = get_cached_time();
replay_record rec = {
.kind = kind,
.delay = (record_time - last_record_time),
};
zpl_array_append(records, rec);
last_record_time = get_cached_time();
}

View File

@ -1,17 +1,13 @@
#include "dev/debug_ui.h"
#include "dev/debug_draw.h"
#include "debug/debug_ui.h"
#include "debug/debug_draw.h"
#include "raylib.h"
#include "models/prefabs/vehicle.h"
#include "ents/vehicle.h"
#include "core/camera.h"
#include "world/world.h"
#include "core/game.h"
#include "sfd.h"
#include "models/components.h"
ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(0)
#include "raylib-nuklear.h"
ZPL_DIAGNOSTIC_POP
#include "ecs/components.h"
typedef enum {
DITEM_RAW,
@ -20,11 +16,9 @@ typedef enum {
DITEM_BUTTON,
DITEM_SLIDER,
DITEM_LIST,
DITEM_TOOL,
DITEM_COND,
DITEM_END,
DITEM_FORCE_UINT8 = UINT8_MAX
} debug_kind;
@ -47,8 +41,6 @@ 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;
static zpl_u16 sel_item_id = 0;
static struct nk_context *dev_ui = 0;
typedef enum {
L_NONE = 0,
@ -62,58 +54,48 @@ typedef struct debug_item {
float name_width;
uint8_t skip;
limit_kind limit_to;
union {
union {
char const *text;
uint64_t val;
};
struct {
struct debug_item *items;
uint8_t is_collapsed;
} list;
struct {
float val, min, max;
void (*on_change)(float);
} slider;
struct {
uint8_t is_open;
void (*on_draw)(void);
} tool;
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 "dev/debug_replay.c"
#include "debug/debug_replay.c"
#include "gui/ui_skin.c"
#include "dev/debug_ui_actions.c"
#include "dev/debug_ui_widgets.c"
#include "dev/debug_ui_tools.c"
#include "debug/debug_ui_actions.c"
#include "debug/debug_ui_widgets.c"
static debug_item items[] = {
{
.kind = DITEM_LIST,
.name = "general",
.kind = DITEM_LIST,
.name = "general",
.list = {
.is_collapsed = true,
.items = (debug_item[]) {
{ .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime },
{ .kind = DITEM_TEXT, .name = "camera pos", .proc = DrawCameraPos },
{ .kind = DITEM_TEXT, .name = "mouse block", .proc = DrawBlockPos },
{ .kind = DITEM_TEXT, .name = "mouse chunk", .proc = DrawChunkPos },
{ .kind = DITEM_TEXT, .name = "pos", .proc = DrawCameraPos },
{ .kind = DITEM_TEXT, .name = "zoom", .proc = DrawZoom },
{ .kind = DITEM_END },
}
@ -127,17 +109,17 @@ static debug_item items[] = {
.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 },
},
},
@ -149,25 +131,14 @@ static debug_item items[] = {
.list = {
.is_collapsed = true,
.items = (debug_item[]) {
{
.kind = DITEM_LIST,
.name = "spawn item",
.list = {
.items = (debug_item[]) {
{ .kind = DITEM_TEXT, .name = "selected", .proc = DrawSelectedSpawnItem },
{ .kind = DITEM_BUTTON, .name = "Previous", .on_click = ActSpawnItemPrev },
{ .kind = DITEM_BUTTON, .name = "Next", .on_click = ActSpawnItemNext },
{ .kind = DITEM_BUTTON, .name = "Spawn <", .on_click = ActSpawnSelItem },
{ .kind = DITEM_END },
},
.is_collapsed = false
}
},
{ .kind = DITEM_BUTTON, .name = "spawn car", .on_click = ActSpawnCar },
{ .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_BUTTON, .name = "spawn chest", .on_click = ActSpawnChest },
{ .kind = DITEM_BUTTON, .name = "spawn belt", .on_click = ActSpawnBelt },
{
.kind = DITEM_LIST,
.name = "demo npcs",
.list = {
@ -193,13 +164,13 @@ static debug_item items[] = {
.items = (debug_item[]) {
{ .kind = DITEM_COND, .on_success = CondClientDisconnected },
{ .kind = DITEM_TEXT, .name = "status", .proc = DrawLiteral, .text = "disconnected" },
{ .kind = DITEM_COND, .on_success = CondClientConnected },
{ .kind = DITEM_TEXT, .name = "status", .proc = DrawLiteral, .text = "connected" },
{ .kind = DITEM_COND, .on_success = CondClientConnected },
{ .kind = DITEM_TEXT, .proc = DrawNetworkStats },
{ .kind = DITEM_END },
},
},
@ -213,30 +184,30 @@ static debug_item items[] = {
.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_collapsed = true,
@ -255,27 +226,12 @@ static debug_item items[] = {
{ .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_RAW, .val = PROF_PHYS_BLOCK_COLS, .proc = DrawProfilerDelta },
{ .kind = DITEM_RAW, .val = PROF_PHYS_BODY_COLS, .proc = DrawProfilerDelta },
{ .kind = DITEM_RAW, .val = PROF_RENDER_PUSH_AND_SORT_ENTRIES, .proc = DrawProfilerDelta },
{ .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta },
{ .kind = DITEM_END },
},
.is_collapsed = 1
}
},
{
.kind = DITEM_LIST,
.name = "tools",
.list = {
.items = (debug_item[]) {
{ .kind = DITEM_TOOL, .name = "asset inspector", .tool = { .is_open = 0, .on_draw = ToolAssetInspector } },
{ .kind = DITEM_TOOL, .name = "entity inspector", .tool = { .is_open = 0, .on_draw = ToolEntityInspector } },
{ .kind = DITEM_END },
},
.is_collapsed = 0
}
},
},
#if !defined(PLATFORM_WEB)
{
.kind = DITEM_BUTTON,
@ -297,7 +253,7 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
}break;
case DITEM_COND: {
ZPL_ASSERT(it->on_success);
if (!it->on_success()) {
it += it->skip ? it->skip : 1;
}
@ -311,15 +267,15 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
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;
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: {
if (it->name) {
char const *text = TextFormat("%s: ", it->name);
@ -329,18 +285,18 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
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) {
@ -350,15 +306,15 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
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);
@ -367,65 +323,33 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
}
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
xpos += it->name_width;
DrawRectangleLines((int)xpos, (int)ypos, 100, DBG_FONT_SIZE, RAYWHITE);
float stick_x = xpos + ((it->slider.val / it->slider.max) * 100.0f) - 5.0f;
DrawRectangle((int)stick_x, (int)ypos, 10, DBG_FONT_SIZE, RED);
xpos += 100.0f + 5.0f;
DrawFloat(xpos, ypos, it->slider.val);
ypos += DBG_FONT_SPACING;
}break;
case DITEM_TOOL: {
char const *text = TextFormat("> %s", it->name);
if (it->name_width == 0) {
it->name_width = (float)UIMeasureText(text, DBG_FONT_SIZE);
}
Color color = RAYWHITE;
if (is_btn_pressed(xpos, ypos, it->name_width, DBG_FONT_SIZE, &color)) {
it->tool.is_open ^= 1;
}
debug_draw_result res = DrawColoredText(xpos, ypos, text, color);
ypos = res.y;
if (it->tool.is_open) {
if (is_shadow_rendered) break;
it->tool.on_draw();
}
} break;
default: {
}break;
}
}
return (debug_draw_result){xpos, ypos};
}
void debug_draw(void) {
// NOTE(zaklaus): Flush old debug samples
debug_draw_flush();
set_style(dev_ui, THEME_RED);
static zpl_u8 first_run=0;
if (!first_run) {
first_run = 1;
ActSpawnItemNext();
// Initialize Nuklear ctx
dev_ui = InitNuklear(10);
}
UpdateNuklear(dev_ui);
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);
@ -435,19 +359,19 @@ void debug_draw(void) {
color = RED;
is_handle_ctrl_held = 1;
}
if (is_handle_ctrl_held) {
debug_xpos = xpos = (float)(GetMouseX() - DBG_CTRL_HANDLE_DIM/2);
debug_ypos = ypos = (float)(GetMouseY() - DBG_CTRL_HANDLE_DIM/2);
if (area == DAREA_PRESS) {
is_handle_ctrl_held = 0;
}
}
DrawRectangle((int)xpos, (int)ypos, DBG_CTRL_HANDLE_DIM, DBG_CTRL_HANDLE_DIM, color);
}
// NOTE(zaklaus): toggle debug ui
{
Color color = BLUE;
@ -461,20 +385,18 @@ void debug_draw(void) {
}
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);
}
DrawNuklear(dev_ui);
}
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;
}
@ -493,34 +415,34 @@ bool is_btn_pressed(float xpos, float ypos, float w, float h, Color *color) {
} else if (area == DAREA_HELD) {
*color = RED;
}
return false;
}
static inline
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
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;
}

View File

@ -1,10 +1,8 @@
#include "dev/debug_ui.h"
#include "debug/debug_ui.h"
#include "world/blocks.h"
#include "models/items.h"
#include "ents/items.h"
#include "net/network.h"
#include "models/entity.h"
void
ActExitGame(void) {
game_request_close();
@ -12,54 +10,62 @@ ActExitGame(void) {
void
ActSpawnCar(void) {
ecs_entity_t e = vehicle_spawn(EVEH_CAR);
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);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
debug_replay_special_action(RPKIND_SPAWN_CAR);
}
void
ActSpawnItemPrev(void) {
while (true) {
--sel_item_id;
item_id id = item_find_no_proxy(sel_item_id);
if (sel_item_id > 0 && id != ASSET_INVALID && (item_get_usage(id) != UKIND_PROXY)) {
break;
}
}
}
void
ActSpawnItemNext(void) {
while (true) {
++sel_item_id;
item_id id = item_find_no_proxy(sel_item_id);
if (sel_item_id > 0 && id != ASSET_INVALID && (item_get_usage(id) != UKIND_PROXY)) {
break;
}
}
}
void
ActSpawnSelItem(void) {
ecs_entity_t e = item_spawn(sel_item_id, 32);
ActSpawnIcemaker(void) {
ecs_entity_t e = item_spawn(ASSET_DEMO_ICEMAKER, 32);
ecs_entity_t plr = camera_get().ent_id;
Position const* origin = ecs_get(world_ecs(), plr, Position);
entity_set_position(e, origin->x, origin->y);
Position * dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
debug_replay_special_action(RPKIND_SPAWN_ICEMAKER_ITEM);
}
void
ActSpawnChest(void) {
ecs_entity_t e = item_spawn(ASSET_CHEST, 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);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
debug_replay_special_action(RPKIND_SPAWN_CHEST);
}
void
ActSpawnBelt(void) {
ecs_entity_t e = item_spawn(ASSET_BELT, 999);
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);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
debug_replay_special_action(RPKIND_SPAWN_BELT);
}
void
ActSpawnCirclingDriver(void) {
ecs_entity_t plr = camera_get().ent_id;
ecs_entity_t ve = vehicle_spawn(EVEH_CAR);
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);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
@ -67,16 +73,16 @@ ActSpawnCirclingDriver(void) {
*dest = *origin;
entity_set_position(ve, veh_dest->x, veh_dest->y);
entity_set_position(e, dest->x, dest->y);
Input *input = ecs_get_mut(world_ecs(), e, Input);
zpl_zero_item(input);
input->x = input->y = 1.0f;
Vehicle *veh = ecs_get_mut(world_ecs(), ve, Vehicle);
veh->seats[0] = e;
ecs_set(world_ecs(), e, IsInVehicle, { .veh = ve });
debug_replay_special_action(RPKIND_SPAWN_CIRCLING_DRIVER);
}
@ -86,14 +92,14 @@ ActPlaceIceRink(void) {
block_id watr_id = blocks_find(ASSET_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_place_block(l.chunk_id, l.id, watr_id);
}
}
debug_replay_special_action(RPKIND_PLACE_ICE_RINK);
}
@ -102,13 +108,13 @@ 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_chunk_destroy_block((p->x - (x*bs)/2.0f), (p->y - (y*bs)/2.0f), true);
}
}
debug_replay_special_action(RPKIND_PLACE_ERASE_CHANGES);
}
@ -175,22 +181,22 @@ 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
@ -204,17 +210,17 @@ ActReplaySave(void) {
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);
@ -230,17 +236,18 @@ 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_id(ASSET_CREATURE);
uint64_t e = entity_spawn(EKIND_DEMO_NPC);
ecs_add(world_ecs(), e, DemoNPC);
Position *pos = ecs_get_mut(world_ecs(), e, Position);
pos->x=(float)(rand() % world_dim());
pos->y=(float)(rand() % world_dim());
pos->y=(float)(rand() % world_dim());
Velocity *v = ecs_get_mut(world_ecs(), e, Velocity);
v->x = (float)((rand()%3-1) * 10);
v->y = (float)((rand()%3-1) * 10);
zpl_array_append(demo_npcs, e);
}
}
@ -248,9 +255,9 @@ ActSpawnDemoNPCs(void) {
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;
}

View File

@ -1,4 +1,4 @@
#include "dev/debug_ui.h"
#include "debug/debug_ui.h"
#include "raylib.h"
#include "platform/platform.h"
#include "net/network.h"
@ -6,96 +6,79 @@
//~ NOTE(zaklaus): helpers
static inline debug_draw_result
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
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);
char const *p = text;
uint8_t newlines = 1;
do {
if (*p == '\n')
++newlines;
} while (*p++ != 0);
return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING*newlines};
}
static inline debug_draw_result
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
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
DrawChunkPos(debug_item *it, float xpos, float ypos) {
float mx, my;
platform_get_block_realpos(&mx, &my);
int csize = world_chunk_size();
return DrawFormattedText(xpos, ypos, TextFormat("%d %d", (int)(mx/WORLD_BLOCK_SIZE/csize), (int)(my/WORLD_BLOCK_SIZE/csize)));
}
static inline debug_draw_result
DrawBlockPos(debug_item *it, float xpos, float ypos) {
float mx, my;
platform_get_block_realpos(&mx, &my);
return DrawFormattedText(xpos, ypos, TextFormat("%d %d", (int)(mx/WORLD_BLOCK_SIZE), (int)(my/WORLD_BLOCK_SIZE)));
}
static inline debug_draw_result
static inline debug_draw_result
DrawUnmeasuredTime(debug_item *it, float xpos, float ypos) {
(void)it;
float total_time = (float)profiler_delta(PROF_TOTAL_TIME);
float acc_time = (float)profiler_delta(PROF_MAIN_LOOP);
return DrawFormattedText(xpos, ypos, TextFormat("%.02f ms", (total_time-acc_time) * 1000.0f));
}
static inline debug_draw_result
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
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
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
static inline debug_draw_result
DrawProfilerDelta(debug_item *it, float xpos, float ypos) {
float dt = (float)profiler_delta(it->val);
return DrawFormattedText(xpos, ypos, TextFormat("%s: %.02f ms", profiler_name(it->val), dt * 1000.0f));
}
static inline debug_draw_result
static inline debug_draw_result
DrawReplaySamples(debug_item *it, float xpos, float ypos) {
(void)it;
size_t cnt = 0;
@ -105,7 +88,7 @@ DrawReplaySamples(debug_item *it, float xpos, float ypos) {
return DrawFormattedText(xpos, ypos, TextFormat("%d of %d", record_pos, cnt));
}
static inline debug_draw_result
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>"));
@ -113,46 +96,41 @@ DrawReplayFileName(debug_item *it, float xpos, float ypos) {
// NOTE(zaklaus): demo npcs
static inline debug_draw_result
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));
}
static inline debug_draw_result
DrawSelectedSpawnItem(debug_item *it, float xpos, float ypos) {
(void)it;
return DrawFormattedText(xpos, ypos, TextFormat("%s", asset_names[sel_item_id]));
}
// NOTE(zaklaus): world simulation
static inline debug_draw_result
// 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)));
}
// NOTE(zaklaus): network stats
static inline debug_draw_result
static inline debug_draw_result
DrawNetworkStats(debug_item *it, float xpos, float ypos) {
(void)it;
network_client_stats s = network_client_fetch_stats();
#define _kb(x) ((x) / 1024)
debug_draw_result r;
r = DrawFormattedText(xpos, ypos, TextFormat("recv total: %lld kb", _kb(s.total_received)));
r = DrawFormattedText(xpos, r.y, TextFormat("sent total: %lld kb", _kb(s.total_sent)));
r = DrawFormattedText(xpos, r.y, TextFormat("dn rate: %.02f kb/sec (%.02f kbit/sec)", _kb(s.incoming_bandwidth), _kb(s.incoming_bandwidth * 8.0f)));
r = DrawFormattedText(xpos, r.y, TextFormat("up rate: %.02f kb/sec (%.02f kbit/sec)", _kb(s.outgoing_bandwidth), _kb(s.outgoing_bandwidth * 8.0f)));
r = DrawFormattedText(xpos, r.y, TextFormat("packets sent: %lld", s.packets_sent));
r = DrawFormattedText(xpos, r.y, TextFormat("packets lost: %d (%.02f%%)", s.packets_lost, s.packet_loss));
r = DrawFormattedText(xpos, r.y, TextFormat("ping: %d ms", s.ping));
#undef _kb
return r;
}

View File

@ -1,143 +0,0 @@
// debug tools written with Nuklear UI
#include "models/assets.h"
#include "world/blocks.h"
#include "models/items.h"
extern void tooltip_show_cursor(const char* name);
extern const char *tooltip_find_desc_contents(const char *name);
void ToolAssetInspector(void) {
if (nk_begin(dev_ui, "Asset Inspector", nk_rect(400, 100, 240, 800),
NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| NK_WINDOW_TITLE))
{
for (int i = 0; i < MAX_ASSETS; i++) {
uint16_t idx = assets_find(i);
if (idx != ASSET_INVALID && nk_tree_push_id(dev_ui, NK_TREE_TAB, asset_names[i], NK_MINIMIZED, i)) {
{
// draw kind
const char *asset_kind_name = assets_get_kind_name(idx);
nk_labelf(dev_ui, NK_TEXT_LEFT, "kind: %s", asset_kind_name);
nk_labelf(dev_ui, NK_TEXT_LEFT, "spawnable entity: %s", entity_spawn_provided(i) ? "true" : "false");
if (nk_button_label(dev_ui, "show tooltip")) {
tooltip_show_cursor(asset_names[i]);
}
if (nk_button_label(dev_ui, "spawn at me")) {
uint64_t e = entity_spawn_id(i);
ecs_entity_t plr = camera_get().ent_id;
Position const* origin = ecs_get(world_ecs(), plr, Position);
entity_set_position(e, origin->x, origin->y);
}
// draw help text
if (nk_tree_push_id(dev_ui, NK_TREE_NODE, "description", NK_MINIMIZED, i)) {
{
const char *desc = tooltip_find_desc_contents(asset_names[i]);
if (desc) {
nk_layout_row_dynamic(dev_ui, 0, 1);
nk_label_wrap(dev_ui, desc);
}
}
nk_tree_pop(dev_ui);
}
// draw block
block_id blk_id = blocks_find(i);
if (blk_id != 0xF) {
if (nk_tree_push_id(dev_ui, NK_TREE_NODE, "block", NK_MINIMIZED, i)) {
{
nk_labelf(dev_ui, NK_TEXT_LEFT, "symbol: %s", zpl_bprintf("%c", blocks_get_symbol(blk_id)));
nk_labelf(dev_ui, NK_TEXT_LEFT, "flags: %u", blocks_get_flags(blk_id));
nk_labelf(dev_ui, NK_TEXT_LEFT, "drag: %f", blocks_get_drag(blk_id));
nk_labelf(dev_ui, NK_TEXT_LEFT, "friction: %f", blocks_get_friction(blk_id));
nk_labelf(dev_ui, NK_TEXT_LEFT, "bounce: %f", blocks_get_bounce(blk_id));
nk_labelf(dev_ui, NK_TEXT_LEFT, "velx: %f", blocks_get_velx(blk_id));
nk_labelf(dev_ui, NK_TEXT_LEFT, "vely: %f", blocks_get_vely(blk_id));
}
nk_tree_pop(dev_ui);
}
}
// draw item
item_id it_id = item_find_no_proxy(i);
if (it_id != ASSET_INVALID) {
if (nk_tree_push_id(dev_ui, NK_TREE_NODE, "item", NK_MINIMIZED, i)) {
{
item_desc it = item_get_desc(it_id);
if (nk_button_label(dev_ui, "spawn")) {
ecs_entity_t e = item_spawn(i, it.max_quantity);
ecs_entity_t plr = camera_get().ent_id;
Position const* origin = ecs_get(world_ecs(), plr, Position);
entity_set_position(e, origin->x, origin->y);
}
nk_labelf(dev_ui, NK_TEXT_LEFT, "usage: %d", it.usage);
nk_labelf(dev_ui, NK_TEXT_LEFT, "attachment: %d", it.attachment);
nk_labelf(dev_ui, NK_TEXT_LEFT, "max quantity: %d", it.max_quantity);
nk_labelf(dev_ui, NK_TEXT_LEFT, "has storage: %s", it.has_storage ? "true" : "false");
// todo: draw item-specific data
}
nk_tree_pop(dev_ui);
}
}
}
nk_tree_pop(dev_ui);
}
}
nk_end(dev_ui);
}
}
void ToolEntityInspector(void) {
if (nk_begin(dev_ui, "Entity Inspector", nk_rect(660, 100, 240, 800),
NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| NK_WINDOW_TITLE))
{
static ecs_query_t *q = 0;
if (!q) {
q = ecs_query(world_ecs(), {
.filter.terms = {
{ .id = ecs_id(Position) },
{ .id = ecs_id(Classify), .inout = EcsIn }
}
});
}
ecs_iter_t it = ecs_query_iter(world_ecs(), q);
while (ecs_query_next(&it)) {
Position *p = ecs_field(&it, Position, 1);
const Classify *c = ecs_field(&it, Classify, 2);
for (int i = 0; i < it.count; i++) {
if (nk_tree_push_id(dev_ui, NK_TREE_NODE, zpl_bprintf("%lld [%s]", it.entities[i], class_names[c[i].id]), NK_MINIMIZED, (int)it.entities[i])) {
{
nk_label(dev_ui, "position:", NK_TEXT_LEFT);
nk_property_float(dev_ui, "#x:", ZPL_F32_MIN, &p[i].x, ZPL_F32_MAX, 0.1f, 0.2f);
nk_property_float(dev_ui, "#y:", ZPL_F32_MIN, &p[i].y, ZPL_F32_MAX, 0.1f, 0.2f);
if (nk_button_label(dev_ui, "teleport to")) {
ecs_entity_t plr = camera_get().ent_id;
Position const* origin = ecs_get(world_ecs(), it.entities[i], Position);
entity_set_position(plr, origin->x, origin->y);
}
if (nk_button_label(dev_ui, "teleport here")) {
ecs_entity_t plr = camera_get().ent_id;
Position const* origin = ecs_get(world_ecs(), plr, Position);
entity_set_position(it.entities[i], origin->x, origin->y);
}
}
nk_tree_pop(dev_ui);
}
}
}
nk_end(dev_ui);
}
}

View File

@ -0,0 +1,41 @@
#include "ecs/components.h"
ECS_COMPONENT_DECLARE(Vector2D);
ECS_COMPONENT_DECLARE(Position);
ECS_COMPONENT_DECLARE(Velocity);
ECS_COMPONENT_DECLARE(Chunk);
ECS_COMPONENT_DECLARE(Drawable);
ECS_COMPONENT_DECLARE(Input);
ECS_COMPONENT_DECLARE(ClientInfo);
ECS_COMPONENT_DECLARE(Health);
ECS_COMPONENT_DECLARE(Classify);
ECS_COMPONENT_DECLARE(Vehicle);
ECS_COMPONENT_DECLARE(IsInVehicle);
ECS_COMPONENT_DECLARE(ItemDrop);
ECS_COMPONENT_DECLARE(Inventory);
ECS_COMPONENT_DECLARE(ItemContainer);
ECS_COMPONENT_DECLARE(Device);
ECS_COMPONENT_DECLARE(DemoNPC);
ECS_COMPONENT_DECLARE(StreamInfo);
void ComponentsImport(ecs_world_t *ecs) {
ECS_MODULE(ecs, Components);
ECS_COMPONENT_DEFINE(ecs, Vector2D);
ECS_COMPONENT_DEFINE(ecs, Position);
ECS_COMPONENT_DEFINE(ecs, Velocity);
ECS_COMPONENT_DEFINE(ecs, Chunk);
ECS_COMPONENT_DEFINE(ecs, Drawable);
ECS_COMPONENT_DEFINE(ecs, Input);
ECS_COMPONENT_DEFINE(ecs, ClientInfo);
ECS_COMPONENT_DEFINE(ecs, Health);
ECS_COMPONENT_DEFINE(ecs, Classify);
ECS_COMPONENT_DEFINE(ecs, Vehicle);
ECS_COMPONENT_DEFINE(ecs, IsInVehicle);
ECS_COMPONENT_DEFINE(ecs, ItemDrop);
ECS_COMPONENT_DEFINE(ecs, Inventory);
ECS_COMPONENT_DEFINE(ecs, ItemContainer);
ECS_COMPONENT_DEFINE(ecs, Device);
ECS_COMPONENT_DEFINE(ecs, DemoNPC);
ECS_COMPONENT_DEFINE(ecs, StreamInfo);
}

View File

@ -0,0 +1,143 @@
#pragma once
#include "flecs/flecs.h"
#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)
#endif
#define ITEMS_INVENTORY_SIZE 9
#define ITEMS_CONTAINER_SIZE 16
typedef struct {
float x;
float y;
} Vector2D;
typedef struct {
uint32_t id;
int16_t x;
int16_t y;
uint8_t is_dirty;
} Chunk;
typedef struct {
uint16_t id;
} Drawable;
typedef Vector2D Position;
typedef Vector2D Velocity;
typedef struct {
float x;
float y;
float mx;
float my;
float bx;
float by;
uint8_t use;
uint8_t sprint;
uint8_t ctrl;
uint8_t pick;
uint8_t is_blocked;
ecs_entity_t pick_ent;
ecs_entity_t sel_ent;
// NOTE(zaklaus): inventory
ecs_entity_t storage_ent;
uint8_t storage_action;
uint8_t selected_item;
uint8_t storage_selected_item;
uint8_t drop;
uint8_t swap;
uint8_t swap_storage;
uint8_t swap_from;
uint8_t swap_to;
// NOTE(zaklaus): build mode
uint8_t num_placements;
float placements_x[20];
float placements_y[20];
uint8_t deletion_mode;
} Input;
typedef struct {
uintptr_t peer;
uint16_t view_id;
uint8_t active;
} ClientInfo;
typedef struct {
float hp;
float max_hp;
//NOTE(zaklaus): Intentionally global, to allow for creative use of damage combos
float pain_time;
float heal_time;
} Health;
typedef struct {
uint16_t id;
} Classify;
typedef struct {
uint64_t seats[4];
float force;
float heading;
float steer;
float wheel_base;
float speed;
float reverse_speed;
} Vehicle;
typedef struct {
ecs_entity_t veh;
} IsInVehicle;
typedef struct {
uint16_t kind;
uint32_t quantity;
float merger_time;
} ItemDrop;
typedef struct {
ItemDrop items[ITEMS_CONTAINER_SIZE];
float pickup_time;
} Inventory;
typedef struct {
ItemDrop items[ITEMS_CONTAINER_SIZE];
} ItemContainer;
typedef struct {
uint16_t asset;
} Device;
typedef struct {
double last_update;
double tick_delay;
} StreamInfo;
typedef struct {char _unused;} DemoNPC;
extern ECS_COMPONENT_DECLARE(Vector2D);
extern ECS_COMPONENT_DECLARE(Position);
extern ECS_COMPONENT_DECLARE(Velocity);
extern ECS_COMPONENT_DECLARE(Chunk);
extern ECS_COMPONENT_DECLARE(Drawable);
extern ECS_COMPONENT_DECLARE(Input);
extern ECS_COMPONENT_DECLARE(ClientInfo);
extern ECS_COMPONENT_DECLARE(Health);
extern ECS_COMPONENT_DECLARE(Classify);
extern ECS_COMPONENT_DECLARE(Vehicle);
extern ECS_COMPONENT_DECLARE(IsInVehicle);
extern ECS_COMPONENT_DECLARE(ItemDrop);
extern ECS_COMPONENT_DECLARE(Inventory);
extern ECS_COMPONENT_DECLARE(ItemContainer);
extern ECS_COMPONENT_DECLARE(Device);
extern ECS_COMPONENT_DECLARE(DemoNPC);
extern ECS_COMPONENT_DECLARE(StreamInfo);
void ComponentsImport(ecs_world_t *ecs);

View File

@ -0,0 +1,16 @@
#define DEMO_NPC_MOVE_SPEED 500
#define DEMO_NPC_STEER_SPEED 300
#include <math.h>
void DemoNPCMoveAround(ecs_iter_t *it) {
Velocity *v = ecs_field(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((float)(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((float)(rand()%360)))*DEMO_NPC_STEER_SPEED*safe_dt(it));
entity_wake(it->entities[i]);
}
}

View File

@ -0,0 +1,355 @@
#include "ents/items.h"
#define ITEM_PICK_RADIUS 25.0f
#define ITEM_MERGER_RADIUS 75.0f
#define ITEM_ATTRACT_RADIUS 75.0f
#define ITEM_ATTRACT_FORCE .98f
#define ITEM_CONTAINER_REACH_RADIUS 105.0f
void PickItem(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (inv[i].pickup_time > game_time()) continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
Position *p2 = ecs_get_mut(it->world, ents[j], Position);
Velocity *v2 = ecs_get_mut(it->world, ents[j], Velocity);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= ITEM_PICK_RADIUS) {
uint16_t drop_id = item_find(drop->kind);
for (size_t k = 0; k < ITEMS_INVENTORY_SIZE; k += 1) {
ItemDrop *item = &inv[i].items[k];
uint16_t item_id = item_find(item->kind);
if (item_id != ASSET_INVALID && (item->quantity == 0 || (item->quantity != 0 && item->kind == drop->kind)) && item->quantity < item_max_quantity(drop_id)) {
uint32_t picked_count = zpl_max(0, drop->quantity);
picked_count = zpl_clamp(picked_count, 0, item_max_quantity(drop_id) - item->quantity);
item->quantity += picked_count;
drop->quantity -= picked_count;
item->kind = drop->kind;
entity_wake(ents[j]);
if (drop->quantity == 0)
item_despawn(ents[j]);
break;
}
}
} else if (range <= ITEM_ATTRACT_RADIUS) {
v2->x = (p[i].x - p2->x) * ITEM_ATTRACT_FORCE;
v2->y = (p[i].y - p2->y) * ITEM_ATTRACT_FORCE;
}
}
}
}
}
#define ITEM_DROP_PICKUP_TIME 2.5f
#define ITEM_DROP_MERGER_TIME 6.5f
void DropItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].drop) continue;
ItemDrop *items = inv[i].items;
if (in[i].storage_action){
if (world_entity_valid(in[i].storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
items = ic->items;
}else{
continue;
}
}else{
continue;
}
}
ItemDrop *item = &items[in[i].storage_action ? in[i].storage_selected_item : in[i].selected_item];
if (item->quantity <= 0)
continue;
uint32_t dropped_count = item->quantity;
if (in[i].sprint) {
dropped_count /= 2;
} else if (in[i].ctrl) {
dropped_count = dropped_count > 0 ? 1 : 0;
}
if (dropped_count == 0)
continue;
ecs_entity_t te = item_spawn(item->kind, dropped_count);
item->quantity -= dropped_count;
ItemDrop *d = ecs_get_mut(world_ecs(), te, ItemDrop);
*d = (ItemDrop){
.kind = item->kind,
.quantity = dropped_count,
.merger_time = game_time() + ITEM_DROP_MERGER_TIME,
};
Position *ipos = ecs_get_mut(it->world, te, Position);
*ipos = p[i];
Velocity *v = ecs_get_mut(it->world, te, Velocity);
v->x = in[i].mx * 800.0f;
v->y = in[i].my * 800.0f;
inv[i].pickup_time = game_time() + ITEM_DROP_PICKUP_TIME;
in[i].drop = false;
if (item->quantity == 0) {
item->kind = 0;
}
}
}
void MergeItems(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
ItemDrop *id = ecs_field(it, ItemDrop, 2);
for (int i = 0; i < it->count; i += 1) {
ItemDrop *item = &id[i];
if (item->merger_time < game_time())
continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 1);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
if (drop->kind != item->kind || (ecs_entity_t)ents[j] == it->entities[i] || drop->quantity == 0 || item->quantity == 0)
continue;
Position const* p2 = ecs_get(it->world, ents[j], Position);
float dx = p2->x - (p[i].x);
float dy = p2->y - (p[i].y);
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= ITEM_MERGER_RADIUS) {
drop->quantity += item->quantity;
item_despawn(it->entities[i]);
break;
}
}
}
}
}
void SwapItems(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Inventory *inv = ecs_field(it, Inventory, 2);
for (int i = 0; i < it->count; i++) {
if (!in[i].swap) continue;
ItemDrop *items = inv[i].items;
if (in[i].storage_action){
if (world_entity_valid(in[i].storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
items = ic->items;
}else{
continue;
}
}else{
continue;
}
}
ItemDrop *to = 0;
ItemDrop *from = 0;
if (in[i].swap_storage){
in[i].swap_storage = false;
if (in[i].storage_action){
from = &inv[i].items[in[i].swap_from];
to = &items[in[i].swap_to];
}else{
if (world_entity_valid(in[i].storage_ent)){
ItemContainer *ic = 0;
if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
from = &ic->items[in[i].swap_from];
}else{
continue;
}
}else{
continue;
}
to = &items[in[i].swap_to];
}
}else{
from = &items[in[i].swap_from];
to = &items[in[i].swap_to];
}
ZPL_ASSERT(from && to);
uint16_t to_id = item_find(to->kind);
if (to == from) {
// NOTE(zaklaus): do nothing
} else if (to->kind == from->kind && to->quantity > 0) {
uint32_t swapped_count = from->quantity;
if (in[i].sprint) {
swapped_count /= 2;
} else if (in[i].ctrl) {
swapped_count = 1;
}
swapped_count = zpl_clamp(swapped_count, 0, item_max_quantity(to_id) - to->quantity);
to->quantity += swapped_count;
from->quantity -= swapped_count;
if (swapped_count == 0) {
ItemDrop tmp = *to;
*to = *from;
*from = tmp;
}
} else if ((in[i].ctrl || in[i].sprint) && to->quantity == 0 && from->quantity > 0) {
// NOTE(zaklaus): item split
uint32_t split_count = from->quantity / 2;
if (in[i].ctrl) {
split_count = 1;
}
to->quantity = split_count;
from->quantity -= split_count;
to->kind = from->kind;
} else {
ItemDrop tmp = *to;
*to = *from;
*from = tmp;
}
in[i].swap = false;
}
}
void UseItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].use && !in[i].num_placements) continue;
if (in[i].storage_action){
continue;
}
ItemDrop *item = &inv[i].items[in[i].selected_item];
uint16_t item_id = 0;
item_usage usage = UKIND_DELETE;
if (!in[i].deletion_mode){
item_id = item_find(item->kind);
usage = item_get_usage(item_id);
if (!item || item->quantity <= 0) continue;
}
if (!in[i].use && usage == UKIND_DELETE){
for (size_t j = 0; j < in[i].num_placements; j++) {
world_chunk_destroy_block(in[i].placements_x[j], in[i].placements_y[j], true);
}
}
else if (in[i].use && usage > UKIND_END_PLACE)
item_use(it->world, item, p[i], 0);
else if (in[i].num_placements > 0 && usage < UKIND_END_PLACE) {
asset_id ofs = 0;
if (item_get_place_directional(item_id) && in[i].num_placements >= 2) {
float p1x = in[i].placements_x[0];
float p1y = in[i].placements_y[0];
float p2x = in[i].placements_x[1];
float p2y = in[i].placements_y[1];
float sx = zpl_sign0(p2x-p1x);
float sy = zpl_sign0(p2y-p1y);
ofs = (sx < 0.0f) ? 1 : 2;
if (sx == 0.0f) {
ofs = (sy < 0.0f) ? 3 : 4;
}
} else if(item_get_place_directional(item_id)) {
// NOTE(zaklaus): ensure we pick the first variant
ofs = 1;
}
for (size_t j = 0; j < in[i].num_placements; j++) {
Position pos = {.x = in[i].placements_x[j], .y = in[i].placements_y[j]};
item_use(it->world, item, pos, ofs);
}
in[i].num_placements = 0;
}
entity_wake(it->entities[i]);
}
}
void InspectContainers(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; ++i) {
if (!in[i].pick) continue;
if ((in[i].sel_ent && ecs_get(it->world, in[i].sel_ent, ItemContainer)) || !in[i].sel_ent)
in[i].storage_ent = in[i].sel_ent;
}
}
void HarvestIntoContainers(ecs_iter_t *it) {
ItemContainer *in = ecs_field(it, ItemContainer, 1);
Position *p = ecs_field(it, Position, 2);
for (int i = 0; i < it->count; ++i) {
// NOTE(zaklaus): find any item
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 0);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
const Position *p2 = ecs_get(it->world, ents[j], Position);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= ITEM_PICK_RADIUS) {
uint16_t drop_id = item_find(drop->kind);
for (size_t k = 0; k < ITEMS_CONTAINER_SIZE; k += 1) {
ItemDrop *item = &in->items[k];
uint16_t item_id = item_find(item->kind);
if (item_id != ASSET_INVALID && (item->quantity == 0 || (item->quantity != 0 && item->kind == drop->kind)) && item->quantity < item_max_quantity(drop_id)) {
uint32_t picked_count = zpl_max(0, drop->quantity);
picked_count = zpl_clamp(picked_count, 0, item_max_quantity(drop_id) - item->quantity);
item->quantity += picked_count;
drop->quantity -= picked_count;
item->kind = drop->kind;
entity_wake(ents[j]);
entity_wake(it->entities[i]);
if (drop->quantity == 0)
item_despawn(ents[j]);
}
}
}
}
}
}
}

View File

@ -1,4 +1,7 @@
#include "models/entity.h"
#include "ents/entity.h"
#define PLR_MOVE_SPEED 800.0f
#define PLR_MOVE_SPEED_MULT 1.5f
void MovementImpulse(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
@ -8,7 +11,7 @@ void MovementImpulse(ecs_iter_t *it) {
for (int i = 0; i < it->count; i++) {
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.bid), 0.0f, 1.0f);
float speed = game_rules.plr_move_speed * (in[i].sprint ? game_rules.plr_move_speed_mult : 1.0f);
float speed = PLR_MOVE_SPEED * (in[i].sprint ? PLR_MOVE_SPEED_MULT : 1.0f);
v[i].x += in[i].x*speed*drag*safe_dt(it);
v[i].y -= in[i].y*speed*drag*safe_dt(it);

View File

@ -1,14 +1,16 @@
#include "dev/debug_draw.h"
#include "models/entity.h"
#include "debug/debug_draw.h"
#include "ents/entity.h"
#define VEH_ENTER_RADIUS 45.0f
void LeaveVehicle(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
IsInVehicle *vehp = ecs_field(it, IsInVehicle, 2);
Velocity *v = ecs_field(it, Velocity, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].use) continue;
Vehicle *veh = 0;
if ((veh = ecs_get_mut_if(it->world, vehp->veh, Vehicle))) {
for (int k = 0; k < 4; k++) {
@ -17,16 +19,16 @@ void LeaveVehicle(ecs_iter_t *it) {
break;
}
}
in[i].use = false;
ecs_remove(it->world, it->entities[i], IsInVehicle);
// NOTE(zaklaus): push passenger out
{
float px = zpl_cos(veh->heading)*400.0f;
float py = zpl_sin(veh->heading)*400.0f;
v[i].x += py;
v[i].y -= px;
v->x += py;
v->y -= px;
}
} else {
ZPL_PANIC("unreachable code");
@ -37,31 +39,29 @@ void LeaveVehicle(ecs_iter_t *it) {
void EnterVehicle(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
for (int i = 0; i < it->count; i++) {
if (!in[i].use) continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
bool has_entered_veh = false;
for (size_t j = 0; j < ents_count; j++) {
Vehicle *veh = 0;
if (has_entered_veh) break;
veh = ecs_get_mut_if(it->world, ents[j], Vehicle);
if ((veh = ecs_get_mut_if(it->world, ents[j], Vehicle))) {
Position const* p2 = ecs_get(it->world, ents[j], Position);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= game_rules.veh_enter_radius) {
if (range <= VEH_ENTER_RADIUS) {
for (int k = 0; k < 4; k++) {
if (veh->seats[k] != 0) continue;
// NOTE(zaklaus): We can enter the vehicle, yay!
veh->seats[k] = it->entities[i];
ecs_set(it->world, it->entities[i], IsInVehicle, {
@ -78,70 +78,71 @@ void EnterVehicle(ecs_iter_t *it) {
}
}
#define VEHICLE_FORCE 240.8f
#define VEHICLE_ACCEL 0.032f
#define VEHICLE_DECEL 0.28f
#define VEHICLE_STEER 35.89f
#define VEHICLE_STEER_COMPENSATION 4.0f
#define VEHICLE_STEER_REVERT 6.0941816f
#define VEHICLE_POWER 97.89f
#define VEHICLE_BRAKE_FORCE 0.84f
void VehicleHandling(ecs_iter_t *it) {
Vehicle *veh = ecs_field(it, Vehicle, 1);
Position *p = ecs_field(it, Position, 2);
Velocity *v = ecs_field(it, Velocity, 3);
for (int i = 0; i < it->count; i++) {
Vehicle *car = &veh[i];
for (int j = 0; j < 4; j++) {
// NOTE(zaklaus): Perform seat cleanup
if (!world_entity_valid(veh[i].seats[j])) {
veh[i].seats[j] = 0;
continue;
}
ecs_entity_t pe = veh[i].seats[j];
// NOTE(zaklaus): Handle driver input
if (j == 0) {
Input const* in = ecs_get(it->world, pe, Input);
car->force += zpl_lerp(0.0f, in->y * game_rules.vehicle_force, (zpl_sign(in->y) == zpl_sign(car->force) ? 1.0f : 3.0f) * game_rules.vehicle_accel*safe_dt(it));
car->force += zpl_lerp(0.0f, in->y * VEHICLE_FORCE, (zpl_sign(in->y) == zpl_sign(car->force) ? 1.0f : 3.0f) * VEHICLE_ACCEL*safe_dt(it));
if (in->sprint) {
car->force = zpl_lerp(car->force, 0.0f, game_rules.vehicle_brake_force*safe_dt(it));
car->force = zpl_lerp(car->force, 0.0f, VEHICLE_BRAKE_FORCE*safe_dt(it));
if (zpl_abs(car->force) < 5.5f)
car->force = 0.0f;
}
float steer_mod = (1 - zpl_abs(car->force) / car->speed) + game_rules.vehicle_steer_compensation * safe_dt(it);
car->steer = zpl_lerp(car->steer, 0.0f, safe_dt(it)*game_rules.vehicle_steer_revert);
car->steer += (in->x * game_rules.vehicle_steer * steer_mod)*safe_dt(it);
float steer_mod = (1 - zpl_abs(car->force) / car->speed) + VEHICLE_STEER_COMPENSATION * safe_dt(it);
car->steer = zpl_lerp(car->steer, 0.0f, safe_dt(it)*VEHICLE_STEER_REVERT);
car->steer += (in->x * VEHICLE_STEER * steer_mod)*safe_dt(it);
car->steer = zpl_clamp(car->steer, -60.0f, 60.0f);
// if (in->x != 0) {
// // Add a sideways velocity to the car. This will make the car move sideways,
// // giving the appearance of a drift. The actual amount of sideways velocity
// // will need to be fine-tuned.
// v[i].x += in->x * steer_mod * -zpl_sin(car->heading) * car->force;
// v[i].y += in->x * steer_mod * zpl_cos(car->heading) * car->force;
// }
}
}
car->force = zpl_clamp(car->force, car->reverse_speed, car->speed);
// NOTE(zaklaus): Vehicle physics
float fr_x = p[i].x + (car->wheel_base/2.0f) * zpl_cos(car->heading);
float fr_y = p[i].y + (car->wheel_base/2.0f) * zpl_sin(car->heading);
float bk_x = p[i].x - (car->wheel_base/2.0f) * zpl_cos(car->heading);
float bk_y = p[i].y - (car->wheel_base/2.0f) * zpl_sin(car->heading);
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.bid), 0.0f, 1.0f);
bk_x += car->force * drag * zpl_cos(car->heading) * safe_dt(it)*game_rules.vehicle_power;
bk_y += car->force * drag * zpl_sin(car->heading) * safe_dt(it)*game_rules.vehicle_power;
fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*game_rules.vehicle_power;
fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*game_rules.vehicle_power;
bk_x += car->force * drag * zpl_cos(car->heading) * safe_dt(it)*VEHICLE_POWER;
bk_y += car->force * drag * zpl_sin(car->heading) * safe_dt(it)*VEHICLE_POWER;
fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*VEHICLE_POWER;
fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*VEHICLE_POWER;
v[i].x += ((fr_x + bk_x) / 2.0f - p[i].x);
v[i].y += ((fr_y + bk_y) / 2.0f - p[i].y);
car->heading = zpl_arctan2(fr_y - bk_y, fr_x - bk_x);
float check_x = p[i].x+PHY_LOOKAHEAD(v[i].x);
float check_y = p[i].y+PHY_LOOKAHEAD(v[i].y);
world_block_lookup lookahead = world_block_from_realpos(check_x, check_y);
@ -154,11 +155,11 @@ void VehicleHandling(ecs_iter_t *it) {
car->force = 0.0f;
}
}
for (int j = 0; j < 4; j++) {
if (!world_entity_valid(veh[i].seats[j])) continue;
ecs_entity_t pe = veh[i].seats[j];
// NOTE(zaklaus): Update passenger position
{
Velocity *v2 = ecs_get_mut(it->world, pe, Velocity);
@ -166,22 +167,22 @@ void VehicleHandling(ecs_iter_t *it) {
*v2 = v[i];
}
}
if (zpl_abs(car->force) > ENTITY_ACTION_VELOCITY_THRESHOLD) {
entity_wake(it->entities[i]);
}
{
debug_v2 b2 = {p[i].x + zpl_cos(car->heading)*(car->wheel_base), p[i].y + zpl_sin(car->heading)*(car->wheel_base)};
debug_push_line((debug_v2){p[i].x, p[i].y}, b2, 0x0000FFFF);
// NOTE(zaklaus): force
{
float dx = zpl_cos(car->heading);
float dy = zpl_sin(car->heading);
debug_push_circle((debug_v2){p[i].x+dx*car->force, p[i].y+dy*car->force}, 5.0f, 0x00FF00FF);
}
// NOTE(zaklaus): steer
{
float dx = zpl_sin(car->heading);
@ -195,7 +196,7 @@ void VehicleHandling(ecs_iter_t *it) {
void ClearVehicle(ecs_iter_t *it) {
Vehicle *veh = ecs_field(it, Vehicle, 1);
for (int i = 0; i < it->count; i++) {
for (int k = 0; k < 4; k++) {
if (world_entity_valid(veh[i].seats[k])) {

View File

@ -0,0 +1,226 @@
#include "zpl.h"
#include "ecs/systems.h"
#include "ecs/components.h"
#include "world/world.h"
#include "world/blocks.h"
#include "platform/profiler.h"
#include "debug/debug_draw.h"
#include "core/game.h"
#define PHY_BLOCK_COLLISION 1
#define PHY_WALK_DRAG 4.23f
#define PHY_LOOKAHEAD(x) (zpl_sign(x)*16.0f)
#include "modules/system_onfoot.c"
#include "modules/system_demo.c"
#include "modules/system_vehicle.c"
#include "modules/system_items.c"
static inline float physics_correction(float x, float vx, float bounce) {
float r = (((zpl_max(0.0f, (WORLD_BLOCK_SIZE/2.0f) - zpl_abs(x))*zpl_sign(x)))*(WORLD_BLOCK_SIZE/2.0f));
return r + (-vx*bounce);
}
void IntegratePositions(ecs_iter_t *it) {
profile(PROF_INTEGRATE_POS) {
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
if (ecs_get(it->world, it->entities[i], IsInVehicle)) {
continue;
}
if (zpl_abs(v[i].x) >= 0.001f || zpl_abs(v[i].y) >= 0.001f) {
// NOTE(zaklaus): world bounds
{
float w = (float)world_dim();
p[i].x = zpl_clamp(p[i].x, 0, w-1);
p[i].y = zpl_clamp(p[i].y, 0, w-1);
}
#if PHY_BLOCK_COLLISION==1
// NOTE(zaklaus): X axis
{
world_block_lookup lookup = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y);
uint32_t flags = blocks_get_flags(lookup.bid);
float bounce = blocks_get_bounce(lookup.bid);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].x = physics_correction(lookup.ox, v[i].x, bounce);
}
}
// NOTE(zaklaus): Y axis
{
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y+PHY_LOOKAHEAD(v[i].y));
uint32_t flags = blocks_get_flags(lookup.bid);
float bounce = blocks_get_bounce(lookup.bid);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].y = physics_correction(lookup.oy, v[i].y, bounce);
}
}
#endif
entity_set_position(it->entities[i], p[i].x+v[i].x*safe_dt(it), p[i].y+v[i].y*safe_dt(it));
}
{
debug_v2 a = {p[i].x, p[i].y};
debug_v2 b = {p[i].x+v[i].x, p[i].y+v[i].y};
debug_push_line(a, b, 0xFFFFFFFF);
}
}
}
}
#define HAZARD_BLOCK_TIME 1.0f
#define HAZARD_BLOCK_DMG 5.0f
void HurtOnHazardBlock(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
Health *h = ecs_field(it, Health, 2);
for (int i = 0; i < it->count; i++) {
if (h->pain_time < 0.0f) {
h->pain_time = HAZARD_BLOCK_TIME;
world_block_lookup l = world_block_from_realpos(p[i].x, p[i].y);
if (blocks_get_flags(l.bid) & BLOCK_FLAG_HAZARD) {
h->hp -= HAZARD_BLOCK_DMG;
h->hp = zpl_max(0.0f, h->hp);
}
}
}
}
#define HP_REGEN_TIME 2.0f
#define HP_REGEN_PAIN_COOLDOWN 5.0f
#define HP_REGEN_RECOVERY 15.0f
void RegenerateHP(ecs_iter_t *it) {
Health *h = ecs_field(it, Health, 1);
for (int i = 0; i < it->count; i++) {
if (h[i].pain_time < 0.0f) {
if (h[i].heal_time < 0.0f && h[i].hp < h[i].max_hp) {
h[i].heal_time = HP_REGEN_TIME;
h[i].hp += HP_REGEN_RECOVERY;
h[i].hp = zpl_min(h[i].max_hp, h[i].hp);
entity_wake(it->entities[i]);
} else {
h[i].heal_time -= safe_dt(it);
}
} else {
h[i].pain_time -= safe_dt(it);
}
}
}
void ResetActivators(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; i++) {
in[i].use = false;
in[i].swap = false;
in[i].drop = false;
in[i].pick = false;
in[i].num_placements = 0;
}
}
void ApplyWorldDragOnVelocity(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
Velocity *v = ecs_field(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
if (zpl_abs(v[i].x) < 0.001f && zpl_abs(v[i].y) < 0.001f) continue;
if (ecs_get(it->world, it->entities[i], IsInVehicle)) {
continue;
}
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.bid), 0.0f, 1.0f);
float friction = blocks_get_friction(lookup.bid);
float velx = blocks_get_velx(lookup.bid);
float vely = blocks_get_vely(lookup.bid);
v[i].x = zpl_lerp(v[i].x, zpl_max(0.0f, zpl_abs(velx))*zpl_sign(velx), PHY_WALK_DRAG*drag*friction*safe_dt(it));
v[i].y = zpl_lerp(v[i].y, zpl_max(0.0f, zpl_abs(vely))*zpl_sign(vely), PHY_WALK_DRAG*drag*friction*safe_dt(it));
if ( zpl_abs(v[i].x) > ENTITY_ACTION_VELOCITY_THRESHOLD
|| zpl_abs(v[i].y) > ENTITY_ACTION_VELOCITY_THRESHOLD) {
entity_wake(it->entities[i]);
}
}
}
#define PLAYER_MAX_INTERACT_RANGE 35.0f
void PlayerClosestInteractable(ecs_iter_t *it){
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; ++i) {
size_t ents_count;
int64_t *ents = world_chunk_fetch_entities_realpos(in[i].bx, in[i].by, &ents_count);
ecs_entity_t closest_pick = 0;
float min_pick = ZPL_F32_MAX;
for (size_t j = 0; j < ents_count; j++) {
const Position *p2 = ecs_get(it->world, ents[j], Position);
if (!p2) continue;
float dx = p2->x - in[i].bx;
float dy = p2->y - in[i].by;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= PLAYER_MAX_INTERACT_RANGE && range < min_pick) {
min_pick = range;
closest_pick = ents[j];
}
}
in[i].pick_ent = closest_pick;
if (in[i].pick)
in[i].sel_ent = (in[i].sel_ent == closest_pick) ? 0 : closest_pick;
}
}
void EnableWorldEdit(ecs_iter_t *it) {
world_set_stage(it->world);
}
void DisableWorldEdit(ecs_iter_t *it) {
(void)it;
world_set_stage(NULL);
}
void SystemsImport(ecs_world_t *ecs) {
ECS_MODULE(ecs, Systems);
ECS_SYSTEM(ecs, EnableWorldEdit, EcsOnLoad);
ECS_SYSTEM(ecs, MovementImpulse, EcsOnLoad, components.Input, components.Velocity, components.Position, !components.IsInVehicle);
ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.DemoNPC);
ECS_SYSTEM(ecs, ApplyWorldDragOnVelocity, EcsOnUpdate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, HurtOnHazardBlock, EcsOnUpdate, components.Position, components.Health);
ECS_SYSTEM(ecs, RegenerateHP, EcsOnUpdate, components.Health);
ECS_SYSTEM(ecs, VehicleHandling, EcsOnUpdate, components.Vehicle, components.Position, components.Velocity);
ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, components.Position, components.Velocity);
ECS_SYSTEM(ecs, EnterVehicle, EcsPostUpdate, components.Input, components.Position, !components.IsInVehicle);
ECS_SYSTEM(ecs, LeaveVehicle, EcsPostUpdate, components.Input, components.IsInVehicle, components.Velocity);
ECS_SYSTEM(ecs, PlayerClosestInteractable, EcsPostUpdate, components.Input);
ECS_SYSTEM(ecs, PickItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, DropItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, SwapItems, EcsPostUpdate, components.Input, components.Inventory);
//ECS_SYSTEM(ecs, MergeItems, EcsPostUpdate, components.Position, components.ItemDrop);
ECS_SYSTEM(ecs, UseItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, InspectContainers, EcsPostUpdate, components.Input, !components.IsInVehicle);
ECS_SYSTEM(ecs, HarvestIntoContainers, EcsPostUpdate, components.ItemContainer, components.Position);
ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input);
ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle);
ECS_SYSTEM(ecs, DisableWorldEdit, EcsPostUpdate);
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "flecs/flecs.h"
static inline float safe_dt(ecs_iter_t *it) {
return zpl_min(it->delta_time, 0.03334f);
}
void SystemsImport(ecs_world_t *ecs);

View File

@ -1,17 +1,16 @@
#include "models/device.h"
#include "models/entity.h"
#include "ents/device.h"
#include "ents/entity.h"
#include "world/entity_view.h"
#include "world/world.h"
#include "models/components.h"
#include "ecs/components.h"
uint64_t device_spawn(asset_id id) {
uint64_t device_spawn(asset_id id) {
ecs_entity_t e = entity_spawn(EKIND_DEVICE);
Device *dev = ecs_get_mut(world_ecs(), e, Device);
zpl_zero_item(dev);
dev->asset = id;
return (uint64_t)e;
}

View File

@ -1,6 +1,6 @@
#pragma once
#include "platform/system.h"
#include "models/assets.h"
#include "gen/assets.h"
uint64_t device_spawn(asset_id id);
void device_despawn(uint64_t ent_id);

View File

@ -1,73 +1,55 @@
#include "models/entity.h"
#include "ents/entity.h"
#include "world/entity_view.h"
#include "flecs.h"
#include "flecs/flecs.h"
#include "librg.h"
#include "world/world.h"
#include "models/components.h"
#include "systems/systems.h"
#include "ecs/components.h"
#include "ecs/systems.h"
#include "zpl.h"
typedef struct {
asset_id id;
uint64_t (*proc)();
uint64_t (*proc_udata)(void*);
} spawndef;
static spawndef *entity_spawnlist;
void entity_add_spawndef(uint16_t id, uint64_t (*proc)()) {
spawndef def={0};
def.id = id;
def.proc = proc;
zpl_array_append(entity_spawnlist, def);
}
void entity_add_spawndef_data(uint16_t id, uint64_t (*proc)(void*)) {
spawndef def={0};
def.id = id;
def.proc_udata = proc;
zpl_array_append(entity_spawnlist, def);
}
// NOTE(zaklaus): bring in entity spawnlist
// #include "lists/entity_spawnlist.c"
#include "models/prefabs/prefabs_list.c"
#define MAX_ENTITY_SPAWNDEFS ((size_t)zpl_array_count(entity_spawnlist))
#include "entity_spawnlist.c"
void entity_default_spawnlist(void) {
zpl_array_init(entity_spawnlist, zpl_heap());
void entity_spawndef_cleanup() {
zpl_array_free(entity_spawnlist); entity_spawnlist = NULL;
}
void entity_spawndef_register(spawndef def) {
if (!entity_spawnlist) {
zpl_array_init(entity_spawnlist, zpl_heap());
}
entity_add_spawndef(ASSET_CHEST, storage_spawn);
entity_add_spawndef(ASSET_FURNACE, furnace_spawn);
entity_add_spawndef(ASSET_CRAFTBENCH, craftbench_spawn);
entity_add_spawndef(ASSET_SPLITTER, splitter_spawn);
entity_add_spawndef(ASSET_ASSEMBLER, assembler_spawn);
entity_add_spawndef(ASSET_CREATURE, creature_spawn);
entity_add_spawndef_data(ASSET_BLUEPRINT, blueprint_spawn_udata);
zpl_array_append(entity_spawnlist, def);
}
uint64_t entity_spawn(uint16_t class_id) {
ecs_entity_t e = ecs_new(world_ecs(), 0);
ecs_set(world_ecs(), e, Classify, { .id = class_id });
entity_wake(e);
if (class_id != EKIND_SERVER) {
ecs_set(world_ecs(), e, Velocity, {0});
Position *pos = ecs_get_mut(world_ecs(), e, Position);
#if 1
pos->x=(float)(rand() % world_dim());
pos->y=(float)(rand() % world_dim());
entity_set_position(e, pos->x, pos->y);
#else
pos->x=350.0f;
pos->y=88.0f;
#endif
librg_entity_track(world_tracker(), e);
librg_entity_track(world_collision_grid(), e);
ecs_set(world_ecs(), e, Velocity, { 0 });
entity_set_position(e, (float)(rand() % world_dim()), (float)(rand() % world_dim()));
librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), pos->x, pos->y, 0));
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
librg_entity_owner_set(world_collision_grid(), e, (int64_t)e);
}
return (uint64_t)e;
}
uint64_t entity_spawn_id(uint16_t id){
for (size_t i = 0; i < MAX_ENTITY_SPAWNDEFS; ++i){
for (zpl_isize i = 0; i < zpl_array_count(entity_spawnlist); ++i){
if (entity_spawnlist[i].id == id){
ZPL_ASSERT(entity_spawnlist[i].proc);
return entity_spawnlist[i].proc();
@ -76,28 +58,8 @@ uint64_t entity_spawn_id(uint16_t id){
return 0;
}
uint64_t entity_spawn_id_with_data(uint16_t id, void *udata){
for (size_t i = 0; i < MAX_ENTITY_SPAWNDEFS; ++i){
if (entity_spawnlist[i].id == id){
ZPL_ASSERT(entity_spawnlist[i].proc_udata);
return entity_spawnlist[i].proc_udata(udata);
}
}
return 0;
}
bool entity_spawn_provided(uint16_t id) {
for (size_t i = 0; i < MAX_ENTITY_SPAWNDEFS; ++i){
if (entity_spawnlist[i].id == id){
return true;
}
}
return false;
}
void entity_batch_despawn(uint64_t *ids, size_t num_ids) {
for (size_t i = 0; i < num_ids; i++ ) {
librg_entity_untrack(world_collision_grid(), ids[i]);
librg_entity_untrack(world_tracker(), ids[i]);
ecs_delete(world_ecs(), ids[i]);
}
@ -105,17 +67,14 @@ void entity_batch_despawn(uint64_t *ids, size_t num_ids) {
void entity_despawn(uint64_t ent_id) {
librg_entity_untrack(world_tracker(), ent_id);
librg_entity_untrack(world_collision_grid(), ent_id);
ecs_delete(world_ecs(), ent_id);
}
void entity_set_position(uint64_t ent_id, float x, float y) {
ecs_set(world_ecs(), ent_id, Position, {x, y});
Position *p = ecs_get_mut(world_ecs(), ent_id, Position);
p->x = x;
p->y = y;
librg_entity_chunk_set(world_tracker(), ent_id, librg_chunk_from_realpos(world_tracker(), x, y, 0));
librg_entity_chunk_set(world_collision_grid(), ent_id, librg_chunk_from_realpos(world_collision_grid(), x, y, 0));
entity_wake(ent_id);
}
@ -133,12 +92,12 @@ void entity_update_action_timers() {
ecs_streaminfo = ecs_query_new(world_ecs(), "components.StreamInfo");
last_update_time = get_cached_time();
}
ecs_iter_t it = ecs_query_iter(world_ecs(), ecs_streaminfo);
while (ecs_query_next(&it)) {
StreamInfo *si = ecs_field(&it, StreamInfo, 1);
for (int32_t i = 0; i < it.count; i++) {
if (si[i].last_update < get_cached_time()) {
si[i].last_update = get_cached_time() + si[i].tick_delay;
@ -146,7 +105,7 @@ void entity_update_action_timers() {
}
}
}
last_update_time = get_cached_time();
}

View File

@ -1,21 +1,24 @@
#pragma once
#include "platform/system.h"
#include "gen/assets.h"
#define ENTITY_ACTION_VELOCITY_THRESHOLD 0.001f
#define ENTITY_ACTION_VELOCITY_THRESHOLD 0.05f
typedef struct {
asset_id id;
uint64_t (*proc)();
} spawndef;
void entity_spawndef_setup();
void entity_spawndef_cleanup();
void entity_spawndef_register(spawndef def);
uint64_t entity_spawn(uint16_t class_id /* 0 = no streaming */);
uint64_t entity_spawn_id(uint16_t id);
uint64_t entity_spawn_id_with_data(uint16_t id, void* udata);
bool entity_spawn_provided(uint16_t id);
void entity_batch_despawn(uint64_t *ids, size_t num_ids);
void entity_despawn(uint64_t ent_id);
void entity_set_position(uint64_t ent_id, float x, float y);
// NOTE(zaklaus): spawndef manager
void entity_add_spawndef(uint16_t id, uint64_t (*proc)());
void entity_add_spawndef_data(uint16_t id, uint64_t (*proc)(void*));
void entity_default_spawnlist(void);
// NOTE(zaklaus): action-based entity stream throttling
void entity_wake(uint64_t ent_id);

View File

@ -0,0 +1,8 @@
// NOTE(zaklaus): access to spawners
#include "ents/storage.h"
static spawndef *entity_spawnlist = 0;
void entity_spawndef_setup(void) {
entity_spawndef_register((spawndef){ .id = ASSET_CHEST, .proc = storage_spawn });
}

View File

@ -0,0 +1,130 @@
#include "ents/items.h"
#include "ents/entity.h"
#include "world/entity_view.h"
#include "world/world.h"
#include "world/blocks.h"
#include "ecs/components.h"
#include "zpl.h"
#include "items_list.c"
void item_cleanup() {
zpl_array_free(items); items = NULL;
}
void item_register(item_desc desc) {
if (!items) {
zpl_array_init(items, zpl_heap());
}
zpl_array_append(items, desc);
}
static inline item_id item_resolve_proxy(item_id id) {
ZPL_ASSERT(id >= 0 && id < zpl_array_count(items));
item_usage usage = items[id].usage;
if (usage == UKIND_PROXY) {
return item_find(items[id].proxy.id);
}
return id;
}
static inline asset_id item_fix_kind(asset_id id) {
return items[item_find(id)].kind;
}
uint64_t item_spawn(asset_id kind, uint32_t qty) {
ecs_entity_t e = entity_spawn(EKIND_ITEM);
ItemDrop *d = ecs_get_mut(world_ecs(), e, ItemDrop);
*d = (ItemDrop){
.kind = item_fix_kind(kind),
.quantity = qty,
.merger_time = 0,
};
return (uint64_t)e;
}
item_id item_find(asset_id kind) {
for (item_id i=0; i<zpl_array_count(items); i++) {
if (items[i].kind == kind)
return item_resolve_proxy(i);
}
return ASSET_INVALID;
}
void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
(void)ecs;
uint16_t it_id = item_find(it->kind);
item_desc *desc = &items[it_id];
if (it->quantity <= 0) return;
switch (item_get_usage(it_id)) {
case UKIND_HOLD: /* NOOP */ break;
case UKIND_PLACE:{
world_block_lookup l = world_block_from_realpos(p.x, p.y);
if (l.is_outer && l.bid > 0) {
asset_id item_asset = blocks_get_asset(l.bid);
item_id item_asset_id = item_find(item_asset);
if (item_asset_id == ASSET_INVALID) return;
// NOTE(zaklaus): If we replace the same item, refund 1 qty and let it replace it
if (item_asset_id == it_id) {
it->quantity++;
} else {
return;
}
}
// NOTE(zaklaus): This is an inner layer block, we can't build over it if it has a collision!
else if (l.bid > 0 && blocks_get_flags(l.bid) & (BLOCK_FLAG_COLLISION|BLOCK_FLAG_ESSENTIAL)) {
return;
}
world_chunk_replace_block(l.chunk_id, l.id, blocks_find(desc->place.kind + (asset_id)udata));
it->quantity--;
}break;
case UKIND_PLACE_ITEM:{
world_block_lookup l = world_block_from_realpos(p.x, p.y);
if (l.is_outer && l.bid > 0) {
return;
}
// NOTE(zaklaus): This is an inner layer block, we can't build over it if it has a collision!
else if (l.bid > 0 && blocks_get_flags(l.bid) & (BLOCK_FLAG_COLLISION|BLOCK_FLAG_ESSENTIAL)) {
return;
}
ecs_entity_t e = entity_spawn_id(desc->place_item.id);
ZPL_ASSERT(world_entity_valid(e));
entity_set_position(e, p.x, p.y);
it->quantity--;
}break;
case UKIND_DELETE:
case UKIND_END_PLACE:
case UKIND_PROXY:
break;
}
}
void item_despawn(uint64_t id) {
entity_despawn(id);
}
uint32_t item_max_quantity(item_id id) {
ZPL_ASSERT(id >= 0 && id < zpl_array_count(items));
return items[id].max_quantity;
}
item_usage item_get_usage(item_id id) {
ZPL_ASSERT(id >= 0 && id < zpl_array_count(items));
return items[id].usage;
}
bool item_get_place_directional(item_id id) {
ZPL_ASSERT(id >= 0 && id < zpl_array_count(items));
return items[id].place.directional;
}

View File

@ -1,16 +1,15 @@
#pragma once
#include "platform/system.h"
#include "models/assets.h"
#include "gen/assets.h"
#include "world/blocks.h"
#include "models/components.h"
#include "ecs/components.h"
typedef enum {
// NOTE(zaklaus): hardcoded fields for placement ops
UKIND_DELETE,
UKIND_PLACE,
UKIND_PLACE_ITEM,
UKIND_PLACE_ITEM_DATA,
UKIND_END_PLACE,
// NOTE(zaklaus): the rest of possible actions
@ -18,17 +17,10 @@ typedef enum {
UKIND_PROXY,
} item_usage;
typedef enum {
UDATA_NONE,
UDATA_ENERGY_SOURCE,
} item_attachment;
typedef struct {
asset_id kind;
item_usage usage;
item_attachment attachment;
uint32_t max_quantity;
uint8_t has_storage;
// NOTE(zaklaus): usage data
union {
@ -45,42 +37,22 @@ typedef struct {
asset_id id;
} place_item;
};
union {
struct {
asset_id producer;
float energy_level;
} energy_source;
};
// NOTE: item data
union {
struct {
uint8_t w;
uint8_t h;
const asset_id *plan;
} blueprint;
};
} item_desc;
typedef uint16_t item_id;
void item_db(void);
void item_setup();
void item_cleanup();
void item_register(item_desc desc);
// NOTE(zaklaus): item drops
void item_show(uint64_t ent, bool show);
uint64_t item_spawn(asset_id kind, uint32_t qty);
void item_despawn(uint64_t id);
// NOTE(zaklaus): items
item_id item_find(asset_id kind);
item_id item_find_no_proxy(asset_id kind);
void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t udata);
Item *item_get_data(uint64_t ent);
const Item *item_get_data_const(uint64_t ent);
void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata);
uint32_t item_max_quantity(item_id id);
item_usage item_get_usage(item_id id);
bool item_get_place_directional(item_id id);
item_desc item_get_desc(item_id id);

View File

@ -0,0 +1,21 @@
#include "ents/items.h"
#include "world/entity_view.h"
#include "items_list_helpers.h"
static item_desc *items = 0;
void item_setup() {
item_register((item_desc){ .kind = 0, .max_quantity = 0, });
item_register(ITEM_BLOCK(ASSET_DEMO_ICEMAKER, 64, ASSET_WATER));
item_register(ITEM_SELF(ASSET_FENCE, 64));
item_register(ITEM_SELF(ASSET_WOOD, 64));
item_register(ITEM_HOLD(ASSET_TREE, 64));
item_register(ITEM_SELF_DIR(ASSET_BELT, 999));
item_register(ITEM_PROXY(ASSET_BELT_LEFT, ASSET_BELT));
item_register(ITEM_PROXY(ASSET_BELT_RIGHT, ASSET_BELT));
item_register(ITEM_PROXY(ASSET_BELT_UP, ASSET_BELT));
item_register(ITEM_PROXY(ASSET_BELT_DOWN, ASSET_BELT));
item_register(ITEM_ENT(ASSET_CHEST, 32, ASSET_CHEST));
}

View File

@ -0,0 +1,51 @@
#pragma once
#define ITEM_HOLD(asset, qty)\
(item_desc){\
.kind = asset,\
.usage = UKIND_HOLD,\
.max_quantity = qty,\
}
#define ITEM_BLOCK(asset, qty, build_asset)\
(item_desc){\
.kind = asset,\
.usage = UKIND_PLACE,\
.max_quantity = qty,\
.place = {\
.kind = build_asset,\
}\
}
#define ITEM_BLOCK_DIR(asset, qty, build_asset)\
(item_desc){\
.kind = asset,\
.usage = UKIND_PLACE,\
.max_quantity = qty,\
.place = {\
.kind = build_asset,\
.directional = true,\
}\
}
#define ITEM_PROXY(asset, proxy_id)\
(item_desc){\
.kind = asset,\
.usage = UKIND_PROXY,\
.proxy = {\
.id = proxy_id,\
}\
}
#define ITEM_ENT(asset, qty, eid)\
(item_desc){\
.kind = asset,\
.usage = UKIND_PLACE_ITEM,\
.max_quantity = qty,\
.place_item = {\
.id = eid\
}\
}
#define ITEM_SELF(asset, qty) ITEM_BLOCK(asset, qty, asset)
#define ITEM_SELF_DIR(asset, qty) ITEM_BLOCK_DIR(asset, qty, asset)

View File

@ -0,0 +1,34 @@
#include "ents/player.h"
#include "ents/entity.h"
#include "world/entity_view.h"
#include "flecs/flecs.h"
#include "librg.h"
#include "world/world.h"
#include "ecs/components.h"
#include "ecs/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});
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);
}

View File

@ -0,0 +1,19 @@
#include "ents/storage.h"
#include "ents/device.h"
#include "ents/entity.h"
#include "world/entity_view.h"
#include "world/world.h"
#include "ecs/components.h"
uint64_t storage_spawn(void) {
ecs_entity_t e = device_spawn(ASSET_CHEST);
ItemContainer *storage = ecs_get_mut(world_ecs(), e, ItemContainer);
*storage = (ItemContainer){0};
return (uint64_t)e;
}
void storage_despawn(uint64_t ent_id) {
entity_despawn(ent_id);
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "platform/system.h"
uint64_t storage_spawn(void);
void storage_despawn(uint64_t id);

View File

@ -0,0 +1,23 @@
#include "ents/vehicle.h"
#include "ents/entity.h"
#include "world/entity_view.h"
#include "world/world.h"
#include "ecs/components.h"
uint64_t vehicle_spawn(void) {
ecs_entity_t e = entity_spawn(EKIND_VEHICLE);
Vehicle *veh = ecs_get_mut(world_ecs(), e, Vehicle);
*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);
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "platform/system.h"
uint64_t vehicle_spawn(void);
void vehicle_despawn(uint64_t id);

View File

@ -4,7 +4,7 @@
// use your favorite editor to quickly navigate between various files.
// 1) Register a new Asset ID
#include "models/assets.h"
#include "gen/assets.h"
// 2) Add the asset to the asset list
#include "assets_list.c"
@ -20,3 +20,4 @@
// NOTE(zaklaus): Register an item
#include "items_list.c"

View File

@ -0,0 +1,121 @@
#include "gen/assets.h"
#include "raylib.h"
#include "gen/texgen.h"
typedef struct {
asset_id id;
asset_kind kind;
union {
Texture2D tex;
Sound snd;
};
// NOTE(zaklaus): metadata
} asset;
#include "assets_list.c"
#define ASSET_FRAME_RENDER_MS (1.0/5.0)
#define ASSET_FRAME_SKIP 4
static int64_t assets_resources_frame_counter = 1;
static double assets_resources_frame_next_draw = 0.0;
#include <time.h>
void assets_register(asset_desc a) {
if (!assets) {
zpl_array_init(assets, zpl_heap());
}
zpl_array_append(assets, ((asset){ .id = a.id, .kind = a.kind }));
}
void assets_cleanup(void) {
zpl_array_free(assets); assets = NULL;
}
int32_t assets_resources_setup(void) {
for (zpl_isize i=0; i<zpl_array_count(assets); i++) {
asset *b = &assets[i];
switch (b->kind) {
case AKIND_TEXTURE: {
b->tex = texgen_build_sprite(b->id);
}break;
case AKIND_ANIM: {
b->tex = texgen_build_anim(b->id, 0);
}break;
case AKIND_SOUND: {
// TODO(zaklaus): soundgen
}break;
default: break;
}
}
assets_resources_frame_next_draw = get_cached_time() + ASSET_FRAME_RENDER_MS;
return 0;
}
int32_t assets_resources_frame(void) {
if (assets_resources_frame_next_draw < get_cached_time()) {
for (zpl_isize i=0; i<zpl_array_count(assets); i++) {
asset *b = &assets[i];
switch (b->kind) {
case AKIND_ANIM: {
UnloadTexture(b->tex);
b->tex = texgen_build_anim(b->id, assets_resources_frame_counter);
}break;
default: break;
}
}
assets_resources_frame_next_draw = get_cached_time() + ASSET_FRAME_RENDER_MS;
assets_resources_frame_counter += ASSET_FRAME_SKIP;
}
return 0;
}
void assets_resources_destroy(void) {
for (zpl_isize i=0; i<zpl_array_count(assets); i++) {
switch (assets[i].kind) {
case AKIND_ANIM:
case AKIND_TEXTURE: {
UnloadTexture(assets[i].tex);
}break;
case AKIND_SOUND: {
// TODO(zaklaus): soundgen
}break;
default: break;
}
}
}
uint16_t assets_find(asset_id id) {
for (zpl_isize i=0; i<zpl_array_count(assets); i++) {
if (assets[i].id == id)
return i;
}
ZPL_PANIC("Unknown asset id: %d\n", id);
return ASSET_INVALID;
}
asset_kind assets_get_kind(uint16_t id) {
return assets[id].kind;
}
void *assets_get_snd(uint16_t id) {
return (void*)&assets[id].snd;;
}
void *assets_get_tex(uint16_t id) {
return (void*)&assets[id].tex;
}

View File

@ -0,0 +1,85 @@
#pragma once
#include "platform/system.h"
#define ASSET_INVALID 0xFF
#define ASSET_ENTRY(asset, asset_kind)\
(asset_desc){\
.id = asset,\
.kind = asset_kind,\
}
#define ASSET_SND(asset) ASSET_ENTRY(asset, AKIND_SOUND)
#define ASSET_TEX(asset) ASSET_ENTRY(asset, AKIND_TEXTURE)
#define ASSET_ANI(asset) ASSET_ENTRY(asset, AKIND_ANIM)
typedef enum {
// NOTE(zaklaus): Debug
ASSET_EMPTY,
ASSET_BLANK,
ASSET_BUILDMODE_HIGHLIGHT,
// NOTE(zaklaus): entities
ASSET_PLAYER,
ASSET_THING,
ASSET_CHEST,
// NOTE(zaklaus): items
ASSET_DEMO_ICEMAKER,
// NOTE(zaklaus): blocks
ASSET_FENCE,
ASSET_DEV,
ASSET_GROUND,
ASSET_DIRT,
ASSET_WATER,
ASSET_LAVA,
ASSET_WALL,
ASSET_HILL,
ASSET_HILL_SNOW,
ASSET_HOLE,
ASSET_WOOD,
ASSET_TREE,
ASSET_BELT,
ASSET_BELT_LEFT,
ASSET_BELT_RIGHT,
ASSET_BELT_UP,
ASSET_BELT_DOWN,
ASSET_NEXT_FREE,
MAX_ASSETS = 1024,
} asset_id;
typedef enum {
AKIND_TEXTURE,
AKIND_ANIM,
AKIND_SOUND,
FORCE_AKIND_UINT8 = UINT8_MAX
} asset_kind;
typedef struct {
asset_id id;
asset_kind kind;
} asset_desc;
void assets_setup(void);
void assets_cleanup(void);
void assets_register(asset_desc desc);
// resources
int32_t assets_resources_setup(void);
void assets_resources_destroy(void);
int32_t assets_resources_frame(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})

View File

@ -0,0 +1,31 @@
#include "gen/assets.h"
static asset *assets = 0;
void assets_setup() {
assets_register(ASSET_TEX(ASSET_EMPTY));
assets_register(ASSET_TEX(ASSET_BLANK));
assets_register(ASSET_TEX(ASSET_BUILDMODE_HIGHLIGHT));
assets_register(ASSET_TEX(ASSET_DEMO_ICEMAKER));
assets_register(ASSET_TEX(ASSET_CHEST));
// NOTE: blocks
assets_register(ASSET_TEX(ASSET_FENCE));
assets_register(ASSET_TEX(ASSET_DEV));
assets_register(ASSET_TEX(ASSET_GROUND));
assets_register(ASSET_TEX(ASSET_DIRT));
assets_register(ASSET_ANI(ASSET_WATER));
assets_register(ASSET_TEX(ASSET_LAVA));
assets_register(ASSET_TEX(ASSET_WALL));
assets_register(ASSET_TEX(ASSET_HILL));
assets_register(ASSET_TEX(ASSET_HILL_SNOW));
assets_register(ASSET_TEX(ASSET_HOLE));
assets_register(ASSET_TEX(ASSET_WOOD));
assets_register(ASSET_TEX(ASSET_TREE));
assets_register(ASSET_TEX(ASSET_BELT));
assets_register(ASSET_TEX(ASSET_BELT_LEFT));
assets_register(ASSET_TEX(ASSET_BELT_RIGHT));
assets_register(ASSET_TEX(ASSET_BELT_UP));
assets_register(ASSET_TEX(ASSET_BELT_DOWN));
};

View File

@ -0,0 +1,77 @@
#include "gen/texgen.h"
#include "world/world.h"
#include "texgen_data.c"
#include "zpl.h"
static inline
Texture2D LoadTexEco(const char *name) {
static char filename[128];
zpl_snprintf(filename, 128, "art/gen/%s.png", name);
return LoadTexture(filename);
}
static inline
Image LoadImageEco(const char *name) {
static char filename[128];
zpl_snprintf(filename, 128, "art/gen/%s.png", name);
return LoadImage(filename);
}
static inline
Texture2D Image2TexEco(Image image) {
Texture2D tex = LoadTextureFromImage(image);
UnloadImage(image);
return tex;
}
static inline
Texture2D GenColorEco(Color color) {
Image img = GenImageColor(1, 1, color);
return Image2TexEco(img);
}
Texture2D texgen_build_anim(asset_id id, int64_t counter) {
(void)counter;
switch (id) {
case ASSET_WATER: {
Image img = LoadImageEco("water");
ImageColorBrightness(&img, zpl_abs((counter % 64 - 32)*2));
return Image2TexEco(img);
}break;
default: return GenColorEco(PINK); break;
}
}
Texture2D texgen_build_sprite(asset_id id) {
switch (id) {
case ASSET_BLANK: return GenColorEco(WHITE); break;
case ASSET_BUILDMODE_HIGHLIGHT: return GenColorEco(WHITE); break;
// NOTE(zaklaus): items
case ASSET_DEMO_ICEMAKER: return LoadTexEco("demo_icemaker");
// NOTE(zaklaus): blocks
case ASSET_FENCE: return LoadTexEco("fence");
case ASSET_GROUND: return LoadTexEco("grass");
case ASSET_DIRT: return LoadTexEco("dirt");
case ASSET_WALL: return LoadTexEco("asphalt");
case ASSET_HILL_SNOW:
case ASSET_HILL: return LoadTexEco("rock");
case ASSET_LAVA: return LoadTexEco("lava");
case ASSET_WOOD: return LoadTexEco("wood");
case ASSET_TREE: return LoadTexEco("tree");
// case ASSET_WATER: return LoadTexEco("water");
case ASSET_BELT:
case ASSET_BELT_RIGHT: return LoadTexEco("belt_right");
case ASSET_BELT_LEFT: return LoadTexEco("belt_left");
case ASSET_BELT_UP: return LoadTexEco("belt_up");
case ASSET_BELT_DOWN: return LoadTexEco("belt_down");
// NOTE(zaklaus): devices
case ASSET_CHEST: return LoadTexEco("chest");
default: return GenColorEco(PINK); break;
}
}

View File

@ -2,11 +2,7 @@
#include "platform/system.h"
#include "raylib.h"
#include "world/blocks.h"
#include "models/assets.h"
#include "gen/assets.h"
Texture2D texgen_build_anim(asset_id id, int64_t counter);
Texture2D texgen_build_sprite(asset_id id);
// NOTE(zak): this is a fallback for when the asset is not defined by the game
Texture2D texgen_build_anim_fallback(asset_id id, int64_t counter);
Texture2D texgen_build_sprite_fallback(asset_id id);

View File

@ -0,0 +1,2 @@
// NOTE(zaklaus): contains collection of packed images used by texgen
// TODO(zaklaus): we will use files for now

View File

@ -1,68 +0,0 @@
#include "gen/texgen.h"
#include "world/world.h"
#include "zpl.h"
#include "utils/raylib_helpers.h"
Texture2D texgen_build_anim_fallback(asset_id id, int64_t counter) {
(void)counter;
switch (id) {
case ASSET_WATER: {
return LoadTexEco(zpl_bprintf("%s%d", "water", counter%3));
}break;
default: return GenColorEco(PINK); break;
}
}
Texture2D texgen_build_sprite_fallback(asset_id id) {
switch (id) {
case ASSET_BLANK: return GenColorEco(WHITE); break;
case ASSET_BUILDMODE_HIGHLIGHT: return GenColorEco(WHITE); break;
case ASSET_BLOCK_FRAME: return GenFrameRect(); break;
// NOTE(zaklaus): items
case ASSET_COAL: return LoadTexEco("coal");
case ASSET_IRON_ORE: return LoadTexEco("iron_ore");
case ASSET_IRON_INGOT: return LoadTexEco("iron_ingot");
case ASSET_IRON_PLATES: return LoadTexEco("iron_plate");
case ASSET_SCREWS: return LoadTexEco("screws");
case ASSET_LOG: return LoadTexEco("log");
case ASSET_PLANK: return LoadTexEco("plank");
case ASSET_CREATURE: return GenColorEco(YELLOW);
case ASSET_CREATURE_FOOD: return GenColorEco(GREEN);
// NOTE(zaklaus): blocks
case ASSET_FENCE: return LoadTexEco("fence");
case ASSET_GROUND: return LoadTexEco("grass");
case ASSET_DIRT: return LoadTexEco("dirt");
case ASSET_WALL: return LoadTexEco("asphalt");
case ASSET_HILL_SNOW:
case ASSET_HILL: return LoadTexEco("rock");
case ASSET_LAVA: return LoadTexEco("lava");
case ASSET_WOOD: return LoadTexEco("wood");
case ASSET_TREE: return LoadTexEco("bigtree");
case ASSET_TEST_TALL: return LoadTexEco("test-tall");
// case ASSET_WATER: return LoadTexEco("water");
case ASSET_BELT:
case ASSET_BELT_RIGHT: return LoadTexEco("belt_right");
case ASSET_BELT_LEFT: return LoadTexEco("belt_left");
case ASSET_BELT_UP: return LoadTexEco("belt_up");
case ASSET_BELT_DOWN: return LoadTexEco("belt_down");
// NOTE(zaklaus): devices
case ASSET_CHEST: return LoadTexEco("chest");
case ASSET_FURNACE: return LoadTexEco("furnace");
case ASSET_CRAFTBENCH: return LoadTexEco("craftbench");
case ASSET_SPLITTER: return LoadTexEco("item_splitter");
case ASSET_ASSEMBLER: return LoadTexEco("assembler");
default: break;
}
if (id > ASSET_BLUEPRINT_BEGIN && id < ASSET_BLUEPRINT_END) {
return LoadTexEco("blueprint");
}
return GenColorEco(PINK);
}

Some files were not shown because too many files have changed in this diff Show More