add blueprinting

isolation
Dominik Madarász 2022-09-29 19:28:56 +02:00
parent 24e30dd90a
commit 6ae7aff24c
22 changed files with 196 additions and 7 deletions

View File

@ -17,6 +17,7 @@ add_library(eco2d-foundation STATIC
src/models/prefabs/vehicle.c src/models/prefabs/vehicle.c
src/models/prefabs/storage.c src/models/prefabs/storage.c
src/models/prefabs/furnace.c src/models/prefabs/furnace.c
src/models/prefabs/blueprint.c
src/pkt/packet.c src/pkt/packet.c

View File

@ -141,6 +141,7 @@ static debug_item items[] = {
{ .kind = DITEM_BUTTON, .name = "spawn chest", .on_click = ActSpawnChest }, { .kind = DITEM_BUTTON, .name = "spawn chest", .on_click = ActSpawnChest },
{ .kind = DITEM_BUTTON, .name = "spawn belt", .on_click = ActSpawnBelt }, { .kind = DITEM_BUTTON, .name = "spawn belt", .on_click = ActSpawnBelt },
{ .kind = DITEM_BUTTON, .name = "spawn furnace", .on_click = ActSpawnFurnace }, { .kind = DITEM_BUTTON, .name = "spawn furnace", .on_click = ActSpawnFurnace },
{ .kind = DITEM_BUTTON, .name = "spawn demo blueprint", .on_click = ActSpawnDemoHouseItem },
{ {
.kind = DITEM_LIST, .kind = DITEM_LIST,
.name = "demo npcs", .name = "demo npcs",

View File

@ -71,6 +71,17 @@ ActSpawnFurnace(void) {
entity_set_position(e, dest->x, dest->y); entity_set_position(e, dest->x, dest->y);
} }
void
ActSpawnDemoHouseItem(void) {
ecs_entity_t e = item_spawn(ASSET_BLUEPRINT, 1);
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);
}
void void
ActSpawnCirclingDriver(void) { ActSpawnCirclingDriver(void) {
ecs_entity_t plr = camera_get().ent_id; ecs_entity_t plr = camera_get().ent_id;

View File

@ -15,6 +15,7 @@ typedef enum {
ASSET_THING, ASSET_THING,
ASSET_CHEST, ASSET_CHEST,
ASSET_FURNACE, ASSET_FURNACE,
ASSET_BLUEPRINT,
// NOTE(zaklaus): items // NOTE(zaklaus): items
ASSET_DEMO_ICEMAKER, ASSET_DEMO_ICEMAKER,

View File

@ -18,6 +18,7 @@ static asset assets[] = {
ASSET_TEX(ASSET_DEMO_ICEMAKER), ASSET_TEX(ASSET_DEMO_ICEMAKER),
ASSET_TEX(ASSET_CHEST), ASSET_TEX(ASSET_CHEST),
ASSET_TEX(ASSET_FURNACE), ASSET_TEX(ASSET_FURNACE),
ASSET_TEX(ASSET_BLUEPRINT),
// NOTE(zaklaus): blocks // NOTE(zaklaus): blocks
ASSET_TEX(ASSET_FENCE), ASSET_TEX(ASSET_FENCE),

View File

@ -19,6 +19,7 @@ ECS_COMPONENT_DECLARE(Producer);
ECS_COMPONENT_DECLARE(EnergySource); ECS_COMPONENT_DECLARE(EnergySource);
ECS_COMPONENT_DECLARE(Ingredient); ECS_COMPONENT_DECLARE(Ingredient);
ECS_COMPONENT_DECLARE(Device); ECS_COMPONENT_DECLARE(Device);
ECS_COMPONENT_DECLARE(Blueprint);
ECS_COMPONENT_DECLARE(DemoNPC); ECS_COMPONENT_DECLARE(DemoNPC);
ECS_COMPONENT_DECLARE(StreamInfo); ECS_COMPONENT_DECLARE(StreamInfo);
@ -44,6 +45,7 @@ void ComponentsImport(ecs_world_t *ecs) {
ECS_COMPONENT_DEFINE(ecs, EnergySource); ECS_COMPONENT_DEFINE(ecs, EnergySource);
ECS_COMPONENT_DEFINE(ecs, Ingredient); ECS_COMPONENT_DEFINE(ecs, Ingredient);
ECS_COMPONENT_DEFINE(ecs, Device); ECS_COMPONENT_DEFINE(ecs, Device);
ECS_COMPONENT_DEFINE(ecs, Blueprint);
ECS_COMPONENT_DEFINE(ecs, DemoNPC); ECS_COMPONENT_DEFINE(ecs, DemoNPC);
ECS_COMPONENT_DEFINE(ecs, StreamInfo); ECS_COMPONENT_DEFINE(ecs, StreamInfo);
} }

View File

@ -154,6 +154,12 @@ typedef struct {
float progress_value; float progress_value;
} Device; } Device;
typedef struct {
uint8_t w;
uint8_t h;
char plan[256];
} Blueprint;
typedef struct { typedef struct {
double last_update; double last_update;
double tick_delay; double tick_delay;
@ -180,6 +186,7 @@ extern ECS_COMPONENT_DECLARE(Producer);
extern ECS_COMPONENT_DECLARE(EnergySource); extern ECS_COMPONENT_DECLARE(EnergySource);
extern ECS_COMPONENT_DECLARE(Ingredient); extern ECS_COMPONENT_DECLARE(Ingredient);
extern ECS_COMPONENT_DECLARE(Device); extern ECS_COMPONENT_DECLARE(Device);
extern ECS_COMPONENT_DECLARE(Blueprint);
extern ECS_COMPONENT_DECLARE(DemoNPC); extern ECS_COMPONENT_DECLARE(DemoNPC);
extern ECS_COMPONENT_DECLARE(StreamInfo); extern ECS_COMPONENT_DECLARE(StreamInfo);

View File

@ -47,6 +47,15 @@ uint64_t entity_spawn_id(uint16_t id){
return 0; 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;
}
void entity_batch_despawn(uint64_t *ids, size_t num_ids) { void entity_batch_despawn(uint64_t *ids, size_t num_ids) {
for (size_t i = 0; i < num_ids; i++ ) { for (size_t i = 0; i < num_ids; i++ ) {
librg_entity_untrack(world_tracker(), ids[i]); librg_entity_untrack(world_tracker(), ids[i]);

View File

@ -5,6 +5,7 @@
uint64_t entity_spawn(uint16_t class_id /* 0 = no streaming */); uint64_t entity_spawn(uint16_t class_id /* 0 = no streaming */);
uint64_t entity_spawn_id(uint16_t id); uint64_t entity_spawn_id(uint16_t id);
uint64_t entity_spawn_id_with_data(uint16_t id, void* udata);
void entity_batch_despawn(uint64_t *ids, size_t num_ids); void entity_batch_despawn(uint64_t *ids, size_t num_ids);
void entity_despawn(uint64_t ent_id); void entity_despawn(uint64_t ent_id);
void entity_set_position(uint64_t ent_id, float x, float y); void entity_set_position(uint64_t ent_id, float x, float y);

View File

@ -1,13 +1,16 @@
// NOTE(zaklaus): access to spawners // NOTE(zaklaus): access to spawners
#include "models/prefabs/storage.h" #include "models/prefabs/storage.h"
#include "models/prefabs/furnace.h" #include "models/prefabs/furnace.h"
#include "models/prefabs/blueprint.h"
static struct { static struct {
asset_id id; asset_id id;
uint64_t (*proc)(); uint64_t (*proc)();
uint64_t (*proc_udata)(void*);
} entity_spawnlist[] = { } entity_spawnlist[] = {
{ .id = ASSET_CHEST, .proc = storage_spawn }, { .id = ASSET_CHEST, .proc = storage_spawn },
{ .id = ASSET_FURNACE, .proc = furnace_spawn } { .id = ASSET_FURNACE, .proc = furnace_spawn },
{ .id = ASSET_BLUEPRINT, .proc_udata = blueprint_spawn_udata },
}; };
#define MAX_ENTITY_SPAWNDEFS ((sizeof(entity_spawnlist))/(sizeof(entity_spawnlist[0]))) #define MAX_ENTITY_SPAWNDEFS ((sizeof(entity_spawnlist))/(sizeof(entity_spawnlist[0])))

View File

@ -129,6 +129,22 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
it->quantity--; it->quantity--;
}break; }break;
case UKIND_PLACE_ITEM_DATA:{
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_with_data(desc->place_item.id, desc);
ZPL_ASSERT(world_entity_valid(e));
entity_set_position(e, p.x, p.y);
it->quantity--;
}break;
case UKIND_DELETE: case UKIND_DELETE:
case UKIND_END_PLACE: case UKIND_END_PLACE:

View File

@ -10,6 +10,7 @@ typedef enum {
UKIND_DELETE, UKIND_DELETE,
UKIND_PLACE, UKIND_PLACE,
UKIND_PLACE_ITEM, UKIND_PLACE_ITEM,
UKIND_PLACE_ITEM_DATA,
UKIND_END_PLACE, UKIND_END_PLACE,
// NOTE(zaklaus): the rest of possible actions // NOTE(zaklaus): the rest of possible actions
@ -57,6 +58,15 @@ typedef struct {
asset_id additional_ingredient; asset_id additional_ingredient;
} ingredient; } ingredient;
}; };
// NOTE: item data
union {
struct {
uint8_t w;
uint8_t h;
const char plan[256];
} blueprint;
};
} item_desc; } item_desc;
typedef uint16_t item_id; typedef uint16_t item_id;

View File

@ -9,6 +9,8 @@ static item_desc items[] = {
ITEM_ENERGY(ASSET_WOOD, ASSET_FURNACE, 64, 15.0f), ITEM_ENERGY(ASSET_WOOD, ASSET_FURNACE, 64, 15.0f),
ITEM_HOLD(ASSET_TREE, 64), ITEM_HOLD(ASSET_TREE, 64),
ITEM_BLUEPRINT(ASSET_BLUEPRINT, 1, 4, 4, "]]]]]CF] ]]]]]"),
ITEM_SELF_DIR(ASSET_BELT, 999), ITEM_SELF_DIR(ASSET_BELT, 999),
ITEM_PROXY(ASSET_BELT_LEFT, ASSET_BELT), ITEM_PROXY(ASSET_BELT_LEFT, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_RIGHT, ASSET_BELT), ITEM_PROXY(ASSET_BELT_RIGHT, ASSET_BELT),

View File

@ -20,6 +20,22 @@
}\ }\
} }
#define ITEM_BLUEPRINT(asset, qty, w_, h_, plan_)\
{\
.kind = asset,\
.usage = UKIND_PLACE_ITEM_DATA,\
.attachment = UDATA_NONE,\
.max_quantity = qty,\
.blueprint = {\
.w = w_,\
.h = h_,\
.plan = plan_\
},\
.place_item = {\
.id = asset\
}\
}
#define ITEM_INGREDIENT(asset, qty, _producer, _product, _additional)\ #define ITEM_INGREDIENT(asset, qty, _producer, _product, _additional)\
{\ {\
.kind = asset,\ .kind = asset,\

View File

@ -0,0 +1,31 @@
#include "vehicle.h"
#include "world/entity_view.h"
#include "world/world.h"
#include "models/device.h"
#include "models/entity.h"
#include "models/items.h"
#include "models/components.h"
uint64_t blueprint_spawn(uint8_t w, uint8_t h, const char *plan) {
ZPL_ASSERT((w*h) == zpl_strlen(plan));
ZPL_ASSERT((w*h) < 256);
ecs_entity_t e = device_spawn(ASSET_BLUEPRINT);
Blueprint *blueprint = ecs_get_mut(world_ecs(), e, Blueprint);
blueprint->w = w;
blueprint->h = h;
zpl_memcopy(blueprint->plan, plan, w*h);
return (uint64_t)e;
}
uint64_t blueprint_spawn_udata(void* udata) {
item_desc *it = (item_desc*)udata;
return blueprint_spawn(it->blueprint.w, it->blueprint.h, it->blueprint.plan);
}
void blueprint_despawn(uint64_t id) {
entity_despawn(id);
}

View File

@ -0,0 +1,8 @@
#pragma once
#include "platform/system.h"
uint64_t blueprint_spawn(uint8_t w, uint8_t h, const char *plan);
uint64_t blueprint_spawn_udata(void* udata);
void blueprint_despawn(uint64_t id);

View File

@ -0,0 +1,27 @@
void BuildBlueprints(ecs_iter_t *it) {
Blueprint *blueprint = ecs_field(it, Blueprint, 1);
Device *d = ecs_field(it, Device, 2);
Position *p = ecs_field(it, Position, 3);
for (int i = 0; i < it->count; i++) {
// TODO check storage and only build if we have enough resources
// build blocks over time and show progress bar while building a block
int w = (int)blueprint[i].w;
int h = (int)blueprint[i].h;
for (int y = 0; y < blueprint[i].h; y++) {
for (int x = 0; x < blueprint[i].w; x++) {
char c = blueprint[i].plan[y*w + x];
if (c == ' ') continue;
world_block_lookup l = world_block_from_realpos(p[i].x + x * WORLD_BLOCK_SIZE - (w * WORLD_BLOCK_SIZE)/2, p[i].y + y * WORLD_BLOCK_SIZE - (h * WORLD_BLOCK_SIZE)/2);
world_chunk_place_block(l.chunk_id, l.id, blocks_find_by_symbol(c));
}
}
entity_despawn(it->entities[i]);
// d[i].progress_active = (producer[i].processed_item > 0);
// d[i].progress_value = 1.0f-((producer[i].process_time - game_time()) / game_rules.furnace_cook_time);
}
}

View File

@ -17,6 +17,7 @@
#include "modules/system_vehicle.c" #include "modules/system_vehicle.c"
#include "modules/system_items.c" #include "modules/system_items.c"
#include "modules/system_producer.c" #include "modules/system_producer.c"
#include "modules/system_blueprint.c"
static inline float physics_correction(float x, float vx, float bounce) { 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)); float r = (((zpl_max(0.0f, (WORLD_BLOCK_SIZE/2.0f) - zpl_abs(x))*zpl_sign(x)))*(WORLD_BLOCK_SIZE/2.0f));
@ -219,6 +220,7 @@ void SystemsImport(ecs_world_t *ecs) {
ECS_SYSTEM(ecs, InspectContainers, EcsPostUpdate, components.Input, !components.IsInVehicle); ECS_SYSTEM(ecs, InspectContainers, EcsPostUpdate, components.Input, !components.IsInVehicle);
ECS_SYSTEM(ecs, HarvestIntoContainers, EcsPostUpdate, components.ItemContainer, components.Position, [none] !components.BlockHarvest); ECS_SYSTEM(ecs, HarvestIntoContainers, EcsPostUpdate, components.ItemContainer, components.Position, [none] !components.BlockHarvest);
ECS_SYSTEM(ecs, ProduceItems, EcsPostUpdate, components.ItemContainer, components.Producer, components.Position, components.Device); ECS_SYSTEM(ecs, ProduceItems, EcsPostUpdate, components.ItemContainer, components.Producer, components.Position, components.Device);
ECS_SYSTEM(ecs, BuildBlueprints, EcsPostUpdate, components.Blueprint, components.Device, components.Position);
ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input); ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input);

View File

@ -57,6 +57,14 @@ block_id blocks_find(asset_id kind) {
return 0xF; return 0xF;
} }
block_id blocks_find_by_symbol(char symbol) {
for (block_id i=0; i<BLOCKS_COUNT; i++) {
if (blocks[i].symbol == symbol)
return i;
}
return 0xF;
}
asset_id blocks_get_asset(block_id id) { asset_id blocks_get_asset(block_id id) {
return blocks[id].kind; return blocks[id].kind;
} }

View File

@ -7,6 +7,7 @@ typedef enum {
BLOCK_FLAG_HAZARD = (1 << 2), BLOCK_FLAG_HAZARD = (1 << 2),
BLOCK_FLAG_ESSENTIAL = (1 << 3), BLOCK_FLAG_ESSENTIAL = (1 << 3),
BLOCK_FLAG_DESTROY_ON_COLLISION = (1 << 4), BLOCK_FLAG_DESTROY_ON_COLLISION = (1 << 4),
BLOCK_FLAG_DEVICE = (1 << 5),
} block_flags; } block_flags;
typedef uint16_t block_id; typedef uint16_t block_id;
@ -15,6 +16,7 @@ int32_t blocks_setup(void);
void blocks_destroy(void); void blocks_destroy(void);
block_id blocks_find(asset_id kind); block_id blocks_find(asset_id kind);
block_id blocks_find_by_symbol(char symbol);
asset_id blocks_get_asset(block_id id); asset_id blocks_get_asset(block_id id);
char blocks_get_symbol(block_id id); char blocks_get_symbol(block_id id);

View File

@ -14,9 +14,11 @@ static block blocks[] = {
BLOCK(ASSET_HILL_SNOW, BLOCK_FLAG_COLLISION, '*', .drag = 1.0f , .friction = 1.0f), BLOCK(ASSET_HILL_SNOW, BLOCK_FLAG_COLLISION, '*', .drag = 1.0f , .friction = 1.0f),
BLOCK(ASSET_WATER, 0, '~', .drag = 0.11f , .friction = 1.0f), BLOCK(ASSET_WATER, 0, '~', .drag = 0.11f , .friction = 1.0f),
BLOCK(ASSET_LAVA, BLOCK_FLAG_HAZARD, '!', .drag = 6.2f , .friction = 4.0f), BLOCK(ASSET_LAVA, BLOCK_FLAG_HAZARD, '!', .drag = 6.2f , .friction = 4.0f),
BLOCK(ASSET_FENCE, BLOCK_FLAG_COLLISION, '#', .drag = 1.0f , .friction = 1.0f, .bounce = 1.0f), BLOCK(ASSET_FENCE, BLOCK_FLAG_COLLISION, '[', .drag = 1.0f , .friction = 1.0f, .bounce = 1.0f),
BLOCK(ASSET_WOOD, BLOCK_FLAG_COLLISION, '#', .drag = 1.0f , .friction = 1.0f, .bounce = 0.0f), BLOCK(ASSET_WOOD, BLOCK_FLAG_COLLISION, ']', .drag = 1.0f , .friction = 1.0f, .bounce = 0.0f),
BLOCK(ASSET_TREE, BLOCK_FLAG_COLLISION|BLOCK_FLAG_DESTROY_ON_COLLISION, '@', .drag = 1.0f , .friction = 1.0f, .bounce = 0.0f), BLOCK(ASSET_TREE, BLOCK_FLAG_COLLISION|BLOCK_FLAG_DESTROY_ON_COLLISION, '@', .drag = 1.0f , .friction = 1.0f, .bounce = 0.0f),
BLOCK(ASSET_CHEST, BLOCK_FLAG_DEVICE, 'C'),
BLOCK(ASSET_FURNACE, BLOCK_FLAG_DEVICE, 'F'),
BLOCK(ASSET_BELT_LEFT, 0, '@', .drag = 1.0f , .friction = 1.0f, .velx = -150.0f), BLOCK(ASSET_BELT_LEFT, 0, '@', .drag = 1.0f , .friction = 1.0f, .velx = -150.0f),
BLOCK(ASSET_BELT_RIGHT, 0, '@', .drag = 1.0f , .friction = 1.0f, .velx = 150.0f), BLOCK(ASSET_BELT_RIGHT, 0, '@', .drag = 1.0f , .friction = 1.0f, .velx = 150.0f),

View File

@ -523,10 +523,25 @@ world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) {
bid = world.block_mapping[id][block_idx]; bid = world.block_mapping[id][block_idx];
} }
int32_t size = world.chunk_size * WORLD_BLOCK_SIZE;
int16_t chunk_x, chunk_y;
librg_chunk_to_chunkpos(world.tracker, id, &chunk_x, &chunk_y, NULL);
float chx = (float)chunk_x * size;
float chy = (float)chunk_y * size;
float bx = (float)(block_idx % world.chunk_size) * WORLD_BLOCK_SIZE;
float by = (float)(block_idx / world.chunk_size) * WORLD_BLOCK_SIZE;
float box = chx + bx + WORLD_BLOCK_SIZE/2.0f;
float boy = chy + by + WORLD_BLOCK_SIZE/2.0f;
world_block_lookup lookup = { world_block_lookup lookup = {
.id = block_idx, .id = block_idx,
.bid = bid, .bid = bid,
.chunk_id = id, .chunk_id = id,
.ox = box,
.oy = boy,
.chunk_e = world.chunk_mapping[id], .chunk_e = world.chunk_mapping[id],
}; };
@ -544,21 +559,34 @@ int64_t world_chunk_from_entity(ecs_entity_t id) {
void world_chunk_replace_worldgen_block(int64_t id, uint16_t block_idx, block_id bid) { void world_chunk_replace_worldgen_block(int64_t id, uint16_t block_idx, block_id bid) {
ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size));
ZPL_ASSERT(!(blocks_get_flags(bid) & BLOCK_FLAG_DEVICE));
world.block_mapping[id][block_idx] = bid; world.block_mapping[id][block_idx] = bid;
world_chunk_mark_dirty(world.chunk_mapping[id]); world_chunk_mark_dirty(world.chunk_mapping[id]);
} }
void world_chunk_replace_block(int64_t id, uint16_t block_idx, block_id bid) { void world_chunk_replace_block(int64_t id, uint16_t block_idx, block_id bid) {
ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size));
world.outer_block_mapping[id][block_idx] = bid; if (blocks_get_flags(bid) & BLOCK_FLAG_DEVICE) {
world_chunk_mark_dirty(world.chunk_mapping[id]); ecs_entity_t e = entity_spawn_id(blocks_get_asset(bid));
world_block_lookup l = world_block_from_index(id, block_idx);
entity_set_position(e, l.ox, l.oy);
} else {
world.outer_block_mapping[id][block_idx] = bid;
world_chunk_mark_dirty(world.chunk_mapping[id]);
}
} }
bool world_chunk_place_block(int64_t id, uint16_t block_idx, block_id bid) { bool world_chunk_place_block(int64_t id, uint16_t block_idx, block_id bid) {
ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size)); ZPL_ASSERT(block_idx >= 0 && block_idx < zpl_square(world.chunk_size));
if (world.outer_block_mapping[id][block_idx] != 0 && bid != 0) return false; if (world.outer_block_mapping[id][block_idx] != 0 && bid != 0) return false;
world.outer_block_mapping[id][block_idx] = bid; if (blocks_get_flags(bid) & BLOCK_FLAG_DEVICE) {
world_chunk_mark_dirty(world.chunk_mapping[id]); ecs_entity_t e = entity_spawn_id(blocks_get_asset(bid));
world_block_lookup l = world_block_from_index(id, block_idx);
entity_set_position(e, l.ox, l.oy);
} else {
world.outer_block_mapping[id][block_idx] = bid;
world_chunk_mark_dirty(world.chunk_mapping[id]);
}
return true; return true;
} }