new crafting system
parent
70bb456a43
commit
9e368527a3
Binary file not shown.
After Width: | Height: | Size: 424 B |
|
@ -9,10 +9,11 @@ add_library(eco2d-foundation STATIC
|
|||
src/platform/profiler.c
|
||||
|
||||
src/models/assets.c
|
||||
src/models/components.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
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#include "models/crafting.h"
|
||||
|
||||
#define R(id1,qty1)\
|
||||
{\
|
||||
.id = id1,\
|
||||
.qty = qty1\
|
||||
}
|
||||
|
||||
#define RECIPE(id,prod,qty,...)\
|
||||
{\
|
||||
.product = id,\
|
||||
.product_qty = qty,\
|
||||
.producer = prod,\
|
||||
.reagents = (reagent[]){\
|
||||
__VA_ARGS__\
|
||||
}\
|
||||
}
|
||||
|
||||
static recipe recipes[] = {
|
||||
RECIPE(ASSET_BELT, ASSET_FURNACE, 4, R(ASSET_FENCE, 8), R(ASSET_WOOD, 2), {0}),
|
||||
};
|
||||
|
||||
#define MAX_RECIPES (sizeof(recipes)/sizeof(recipes[0]))
|
||||
|
||||
#undef R
|
||||
#undef RECIPE
|
|
@ -4,24 +4,24 @@
|
|||
|
||||
static item_desc items[] = {
|
||||
{ .kind = 0, .max_quantity = 0, },
|
||||
ITEM_INGREDIENT(ASSET_FENCE, 64, ASSET_FURNACE, ASSET_BELT, 0),
|
||||
ITEM_HOLD(ASSET_FENCE, 64),
|
||||
ITEM_ENERGY(ASSET_COAL, ASSET_FURNACE, 64, 15.0f),
|
||||
ITEM_SELF(ASSET_WOOD, 64),
|
||||
ITEM_SELF(ASSET_TREE, 64),
|
||||
ITEM_SELF(ASSET_TEST_TALL, 64),
|
||||
|
||||
|
||||
// ITEM_BLUEPRINT(ASSET_BLUEPRINT, 1, 4, 4, "]]]]]CF] ]]]]]"),
|
||||
ITEM_BLUEPRINT_PROXY(ASSET_BLUEPRINT_DEMO_HOUSE, ASSET_BLUEPRINT, 1, 4, 4, PROT({ ASSET_WOOD,ASSET_WOOD,ASSET_WOOD,ASSET_WOOD,
|
||||
ASSET_WOOD,ASSET_FURNACE,ASSET_CHEST,ASSET_WOOD,
|
||||
ASSET_WOOD,ASSET_EMPTY,ASSET_EMPTY,ASSET_WOOD,
|
||||
ASSET_WOOD,ASSET_WOOD,ASSET_EMPTY,ASSET_WOOD})),
|
||||
|
||||
ASSET_WOOD,ASSET_FURNACE,ASSET_CHEST,ASSET_WOOD,
|
||||
ASSET_WOOD,ASSET_EMPTY,ASSET_EMPTY,ASSET_WOOD,
|
||||
ASSET_WOOD,ASSET_WOOD,ASSET_EMPTY,ASSET_WOOD})),
|
||||
|
||||
ITEM_SELF_DIR(ASSET_BELT, 999),
|
||||
ITEM_PROXY(ASSET_BELT_LEFT, ASSET_BELT),
|
||||
ITEM_PROXY(ASSET_BELT_RIGHT, ASSET_BELT),
|
||||
ITEM_PROXY(ASSET_BELT_UP, ASSET_BELT),
|
||||
ITEM_PROXY(ASSET_BELT_DOWN, ASSET_BELT),
|
||||
|
||||
|
||||
ITEM_ENT(ASSET_CHEST, 32, ASSET_CHEST),
|
||||
ITEM_ENT(ASSET_FURNACE, 32, ASSET_FURNACE),
|
||||
};
|
||||
|
|
|
@ -17,7 +17,6 @@ ECS_COMPONENT_DECLARE(Inventory);
|
|||
ECS_COMPONENT_DECLARE(ItemContainer);
|
||||
ECS_COMPONENT_DECLARE(Producer);
|
||||
ECS_COMPONENT_DECLARE(EnergySource);
|
||||
ECS_COMPONENT_DECLARE(Ingredient);
|
||||
ECS_COMPONENT_DECLARE(ItemRouter);
|
||||
ECS_COMPONENT_DECLARE(Device);
|
||||
ECS_COMPONENT_DECLARE(Blueprint);
|
||||
|
@ -44,7 +43,6 @@ void ComponentsImport(ecs_world_t *ecs) {
|
|||
ECS_COMPONENT_DEFINE(ecs, ItemContainer);
|
||||
ECS_COMPONENT_DEFINE(ecs, Producer);
|
||||
ECS_COMPONENT_DEFINE(ecs, EnergySource);
|
||||
ECS_COMPONENT_DEFINE(ecs, Ingredient);
|
||||
ECS_COMPONENT_DEFINE(ecs, ItemRouter);
|
||||
ECS_COMPONENT_DEFINE(ecs, Device);
|
||||
ECS_COMPONENT_DEFINE(ecs, Blueprint);
|
||||
|
|
|
@ -131,12 +131,13 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
asset_id processed_item;
|
||||
uint32_t processed_item_qty;
|
||||
float process_time;
|
||||
float energy_level;
|
||||
} Producer;
|
||||
|
||||
typedef struct {
|
||||
char _unused;
|
||||
uint32_t push_qty;
|
||||
} ItemRouter;
|
||||
|
||||
typedef struct {
|
||||
|
@ -144,12 +145,6 @@ typedef struct {
|
|||
float energy_level;
|
||||
} EnergySource;
|
||||
|
||||
typedef struct {
|
||||
asset_id producer;
|
||||
asset_id additional_ingredient; // optional - can specify additional item we need in the container to craft this item
|
||||
asset_id product;
|
||||
} Ingredient;
|
||||
|
||||
typedef struct {
|
||||
uint16_t asset;
|
||||
|
||||
|
@ -188,7 +183,6 @@ extern ECS_COMPONENT_DECLARE(Inventory);
|
|||
extern ECS_COMPONENT_DECLARE(ItemContainer);
|
||||
extern ECS_COMPONENT_DECLARE(Producer);
|
||||
extern ECS_COMPONENT_DECLARE(EnergySource);
|
||||
extern ECS_COMPONENT_DECLARE(Ingredient);
|
||||
extern ECS_COMPONENT_DECLARE(ItemRouter);
|
||||
extern ECS_COMPONENT_DECLARE(Device);
|
||||
extern ECS_COMPONENT_DECLARE(Blueprint);
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
#include "crafting.h"
|
||||
#include "models/items.h"
|
||||
|
||||
typedef struct {
|
||||
asset_id id;
|
||||
uint32_t qty;
|
||||
} reagent;
|
||||
|
||||
typedef struct {
|
||||
asset_id product;
|
||||
uint32_t product_qty;
|
||||
asset_id producer;
|
||||
reagent *reagents;
|
||||
} recipe;
|
||||
|
||||
#include "lists/crafting_list.c"
|
||||
|
||||
uint32_t craft__find_num_recipes_by_reagent(asset_id producer, asset_id id) {
|
||||
uint32_t num_recipes=0;
|
||||
for (int i = 0; i < MAX_RECIPES; ++i) {
|
||||
if (recipes[i].producer == producer) {
|
||||
for (int j = 0; recipes[i].reagents[j].id; ++j) {
|
||||
if (recipes[i].reagents[j].id == id) {
|
||||
++num_recipes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_recipes;
|
||||
}
|
||||
|
||||
recipe *craft__find_recipe_by_reagent(asset_id producer, asset_id id, uint32_t slot_id) {
|
||||
for (int i = 0; i < MAX_RECIPES; ++i) {
|
||||
if (recipes[i].producer == producer) {
|
||||
for (int j = 0; recipes[i].reagents[j].id; ++j) {
|
||||
if (recipes[i].reagents[j].id == id) {
|
||||
if (slot_id > 0) {
|
||||
--slot_id;
|
||||
continue;
|
||||
}
|
||||
return &recipes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool craft_is_reagent_used_in_producer(asset_id reagent, asset_id producer) {
|
||||
return craft__find_num_recipes_by_reagent(producer, reagent) > 0;
|
||||
}
|
||||
|
||||
asset_id craft_perform_recipe(ecs_entity_t *items, asset_id producer, uint32_t *quantity) {
|
||||
ZPL_ASSERT_NOT_NULL(items);
|
||||
|
||||
for (int i = 0; i < ITEMS_CONTAINER_SIZE; i++) {
|
||||
ecs_entity_t item_slot_ent = items[i];
|
||||
if (item_slot_ent == 0) continue;
|
||||
Item *item = item_get_data(item_slot_ent);
|
||||
if (!item) continue;
|
||||
|
||||
uint32_t num_recipes = craft__find_num_recipes_by_reagent(producer, item->kind);
|
||||
for (uint32_t rec_i = 0; rec_i < num_recipes; ++rec_i) {
|
||||
// TODO(zaklaus): slow, find a better way to retrieve known recipes
|
||||
recipe *rec = craft__find_recipe_by_reagent(producer, item->kind, rec_i);
|
||||
if (!rec) {
|
||||
// NOTE(zaklaus): this item is not used as a reagent, skip it.
|
||||
// TODO(zaklaus): is this a bug? should we assert?
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t skip_slot=0;
|
||||
|
||||
// NOTE(zaklaus): analyse if all the reagents are present
|
||||
for (int j = 0; rec->reagents[j].id; ++j) {
|
||||
reagent *rea = &rec->reagents[j];
|
||||
uint32_t pending_qty = rea->qty;
|
||||
|
||||
for (int k = 0; k < ITEMS_CONTAINER_SIZE; k++) {
|
||||
ecs_entity_t rea_item_slot_ent = items[k];
|
||||
if (rea_item_slot_ent == 0) continue;
|
||||
Item *rea_item = item_get_data(rea_item_slot_ent);
|
||||
if (!rea_item) continue;
|
||||
|
||||
if (rea->id == rea_item->kind && rea_item->quantity > 0) {
|
||||
pending_qty -= zpl_min(pending_qty, rea_item->quantity);
|
||||
|
||||
if (pending_qty == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pending_qty > 0) {
|
||||
// NOTE(zaklaus): reagent not found, bail
|
||||
skip_slot=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): demand not met, bye!
|
||||
if (skip_slot)
|
||||
continue;
|
||||
|
||||
// NOTE(zaklaus): deplete used reagents
|
||||
for (int j = 0; rec->reagents[j].id; ++j) {
|
||||
reagent *rea = &rec->reagents[j];
|
||||
uint32_t pending_qty = rea->qty;
|
||||
|
||||
for (int k = 0; k < ITEMS_CONTAINER_SIZE; k++) {
|
||||
ecs_entity_t rea_item_slot_ent = items[k];
|
||||
if (rea_item_slot_ent == 0) continue;
|
||||
Item *rea_item = item_get_data(rea_item_slot_ent);
|
||||
if (!rea_item) continue;
|
||||
|
||||
if (rea->id == rea_item->kind && rea_item->quantity > 0) {
|
||||
rea_item->quantity -= zpl_min(pending_qty, rea_item->quantity);
|
||||
pending_qty -= zpl_min(pending_qty, rea_item->quantity);
|
||||
if (rea_item->quantity == 0) {
|
||||
item_despawn(rea_item_slot_ent);
|
||||
items[k] = 0;
|
||||
}
|
||||
|
||||
if (pending_qty == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(zaklaus): all done, return the product and its qty
|
||||
*quantity = rec->product_qty;
|
||||
return rec->product;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO(zaklaus):
|
||||
asset_id craft_has_byproducts(asset_id product) {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
// TODO(zaklaus):
|
||||
uint32_t craft_resolve_graph(asset_id product, uint16_t *hops, uint8_t direct_cost) {
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include "platform/system.h"
|
||||
#include "models/assets.h"
|
||||
|
||||
#include "models/components.h"
|
||||
|
||||
// NOTE(zaklaus): resolves recipe dependencies and consumes reagents
|
||||
// to enqueue a production of a new item.
|
||||
// TODO(zaklaus): "items" is assumed to come from ItemContainer component.
|
||||
asset_id craft_perform_recipe(ecs_entity_t *items, asset_id producer, uint32_t *quantity);
|
||||
|
||||
// NOTE(zaklaus): informs us on whether this product has any byproducts desired.
|
||||
asset_id craft_has_byproducts(asset_id product);
|
||||
|
||||
// NOTE(zaklaus): mostly used by item router so we don't push reagents out
|
||||
bool craft_is_reagent_used_in_producer(asset_id reagent, asset_id producer);
|
||||
|
||||
// NOTE(zaklaus): resolves the production chain and analyses the amount of items required
|
||||
// and a number of hops (production layers) needed to produce the item.
|
||||
// optionally, it allows to calculate "direct_cost" of the product.
|
||||
uint32_t craft_resolve_graph(asset_id product, uint16_t *hops, uint8_t direct_cost);
|
|
@ -32,7 +32,7 @@ void item_show(uint64_t ent, bool show) {
|
|||
|
||||
uint64_t item_spawn(asset_id kind, uint32_t qty) {
|
||||
ecs_entity_t e = entity_spawn(EKIND_ITEM);
|
||||
|
||||
|
||||
Item *d = ecs_get_mut(world_ecs(), e, Item);
|
||||
*d = (Item){
|
||||
.kind = item_fix_kind(kind),
|
||||
|
@ -40,32 +40,26 @@ uint64_t item_spawn(asset_id kind, uint32_t qty) {
|
|||
.merger_time = 0,
|
||||
.durability = 1.0f,
|
||||
};
|
||||
|
||||
|
||||
item_desc *it = &items[item_find(kind)];
|
||||
|
||||
|
||||
if (it->has_storage) {
|
||||
ecs_add(world_ecs(), e, BlockHarvest);
|
||||
ItemContainer *storage = ecs_get_mut(world_ecs(), e, ItemContainer);
|
||||
*storage = (ItemContainer){0};
|
||||
}
|
||||
|
||||
|
||||
switch (it->attachment) {
|
||||
case UDATA_ENERGY_SOURCE: {
|
||||
EnergySource *f = ecs_get_mut(world_ecs(), e, EnergySource);
|
||||
*f = (EnergySource){
|
||||
.kind = it->energy_source.producer,
|
||||
.energy_level = it->energy_source.energy_level,
|
||||
};
|
||||
} break;
|
||||
case UDATA_INGREDIENT: {
|
||||
Ingredient *i = ecs_get_mut(world_ecs(), e, Ingredient);
|
||||
i->producer = it->ingredient.producer;
|
||||
i->product = it->ingredient.product;
|
||||
i->additional_ingredient = it->ingredient.additional_ingredient;
|
||||
} break;
|
||||
default: break;
|
||||
case UDATA_ENERGY_SOURCE: {
|
||||
EnergySource *f = ecs_get_mut(world_ecs(), e, EnergySource);
|
||||
*f = (EnergySource){
|
||||
.kind = it->energy_source.producer,
|
||||
.energy_level = it->energy_source.energy_level,
|
||||
};
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
return (uint64_t)e;
|
||||
}
|
||||
|
||||
|
@ -108,7 +102,7 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
|
|||
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++;
|
||||
|
@ -123,7 +117,7 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
|
|||
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) {
|
||||
|
@ -133,14 +127,14 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
|
|||
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_PLACE_ITEM_DATA:{
|
||||
world_block_lookup l = world_block_from_realpos(p.x, p.y);
|
||||
if (l.is_outer && l.bid > 0) {
|
||||
|
@ -150,14 +144,14 @@ void item_use(ecs_world_t *ecs, ecs_entity_t e, Item *it, Position p, uint64_t u
|
|||
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_END_PLACE:
|
||||
case UKIND_PROXY:
|
||||
|
|
|
@ -12,7 +12,7 @@ typedef enum {
|
|||
UKIND_PLACE_ITEM,
|
||||
UKIND_PLACE_ITEM_DATA,
|
||||
UKIND_END_PLACE,
|
||||
|
||||
|
||||
// NOTE(zaklaus): the rest of possible actions
|
||||
UKIND_HOLD,
|
||||
UKIND_PROXY,
|
||||
|
@ -21,7 +21,6 @@ typedef enum {
|
|||
typedef enum {
|
||||
UDATA_NONE,
|
||||
UDATA_ENERGY_SOURCE,
|
||||
UDATA_INGREDIENT,
|
||||
} item_attachment;
|
||||
|
||||
typedef struct {
|
||||
|
@ -30,36 +29,30 @@ typedef struct {
|
|||
item_attachment attachment;
|
||||
uint32_t max_quantity;
|
||||
uint8_t has_storage;
|
||||
|
||||
|
||||
// NOTE(zaklaus): usage data
|
||||
union {
|
||||
struct {
|
||||
asset_id kind;
|
||||
bool directional; // NOTE(zaklaus): expects next 4 asset entries to be direction assets
|
||||
} place;
|
||||
|
||||
|
||||
struct {
|
||||
asset_id id;
|
||||
} proxy;
|
||||
|
||||
|
||||
struct {
|
||||
asset_id id;
|
||||
} place_item;
|
||||
};
|
||||
|
||||
|
||||
union {
|
||||
struct {
|
||||
asset_id producer;
|
||||
float energy_level;
|
||||
} energy_source;
|
||||
|
||||
struct {
|
||||
asset_id producer;
|
||||
asset_id product;
|
||||
asset_id additional_ingredient;
|
||||
} ingredient;
|
||||
};
|
||||
|
||||
|
||||
// NOTE: item data
|
||||
union {
|
||||
struct {
|
||||
|
|
|
@ -15,7 +15,7 @@ uint64_t furnace_spawn(void) {
|
|||
*producer = (Producer){0};
|
||||
producer->energy_level = 69.0f;
|
||||
|
||||
ecs_add(world_ecs(), e, ItemRouter);
|
||||
ecs_set(world_ecs(), e, ItemRouter, {1});
|
||||
return (uint64_t)e;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include "models/crafting.h"
|
||||
|
||||
static inline
|
||||
asset_id FetchAssetAtPos(float x, float y) {
|
||||
world_block_lookup lookup = world_block_from_realpos(x, y);
|
||||
|
@ -63,6 +65,7 @@ void PushItemsOnNodes(ecs_iter_t *it) {
|
|||
// We need a way to refer to specific blocks in the world so we can do easy block ID checks
|
||||
// and re-build the cache when a change is detected.
|
||||
|
||||
|
||||
float push_dx[4], push_dy[4];
|
||||
uint8_t nodes = CheckForNearbyBelts(&p[i], push_dx, push_dy);
|
||||
uint8_t num_nodes = (uint8_t)zpl_count_set_bits(nodes);
|
||||
|
@ -77,13 +80,11 @@ void PushItemsOnNodes(ecs_iter_t *it) {
|
|||
ecs_entity_t item_slot_ent = storage[i].items[j];
|
||||
if (item_slot_ent == 0) continue;
|
||||
Item *item = item_get_data(item_slot_ent);
|
||||
if (!item) continue;
|
||||
|
||||
const Ingredient *ing = 0;
|
||||
// NOTE(zaklaus): Make sure we don't push out items from input node
|
||||
if ((ing = ecs_get_if(it->world, item_slot_ent, Ingredient))) {
|
||||
if (ing->producer == d->asset) {
|
||||
continue;
|
||||
}
|
||||
if (craft_is_reagent_used_in_producer(item->kind, d->asset)) {
|
||||
// NOTE(zaklaus): this is an input reagent, keep it
|
||||
continue;
|
||||
}
|
||||
|
||||
while (item->quantity > 0 && num_nodes > 0) {
|
||||
|
@ -94,14 +95,14 @@ void PushItemsOnNodes(ecs_iter_t *it) {
|
|||
continue;
|
||||
}
|
||||
|
||||
uint64_t e = item_spawn(item->kind, 1);
|
||||
uint64_t e = item_spawn(item->kind, zpl_min(r->push_qty, item->quantity));
|
||||
entity_set_position(e, p[i].x + push_dx[counter], p[i].y + push_dy[counter]);
|
||||
|
||||
Velocity *e_vel = ecs_get_mut_ex(it->world, e, Velocity);
|
||||
e_vel->x = push_dx[counter];
|
||||
e_vel->y = push_dy[counter];
|
||||
|
||||
--item->quantity;
|
||||
item->quantity -= zpl_min(r->push_qty, item->quantity);
|
||||
--num_nodes;
|
||||
++counter;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include "models/crafting.h"
|
||||
|
||||
void ProduceItems(ecs_iter_t *it) {
|
||||
ItemContainer *storage = ecs_field(it, ItemContainer, 1);
|
||||
Producer *producer = ecs_field(it, Producer, 2);
|
||||
|
@ -7,6 +9,7 @@ void ProduceItems(ecs_iter_t *it) {
|
|||
for (int i = 0; i < it->count; i++) {
|
||||
for (int j = 0; j < ITEMS_CONTAINER_SIZE; j++) {
|
||||
ecs_entity_t item_slot_ent = storage[i].items[j];
|
||||
if (item_slot_ent == 0) continue;
|
||||
Item *item = item_get_data(item_slot_ent);
|
||||
|
||||
const EnergySource *energy_source = 0;
|
||||
|
@ -19,26 +22,18 @@ void ProduceItems(ecs_iter_t *it) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// TODO(zaklaus): handle fuel
|
||||
// if (producer[i].energy_level <= 0.0f) continue;
|
||||
|
||||
// TODO(zaklaus): use ticks
|
||||
if (producer[i].process_time < game_time()) {
|
||||
if (producer[i].processed_item > 0) {
|
||||
uint64_t e = item_spawn(producer[i].processed_item, 1);
|
||||
uint64_t e = item_spawn(producer[i].processed_item, producer[i].processed_item_qty);
|
||||
entity_set_position(e, p[i].x, p[i].y);
|
||||
producer[i].processed_item = 0;
|
||||
} else {
|
||||
const Ingredient *ing = 0;
|
||||
if ((ing = ecs_get_if(it->world, item_slot_ent, Ingredient))) {
|
||||
if (ing->producer == d->asset) {
|
||||
if (item->quantity <= 0) {
|
||||
item_despawn(item_slot_ent);
|
||||
storage[i].items[j] = 0;
|
||||
} else {
|
||||
producer[i].processed_item = ing->product;
|
||||
producer[i].process_time = game_time() + game_rules.furnace_cook_time;
|
||||
}
|
||||
item->quantity--;
|
||||
}
|
||||
}
|
||||
producer[i].processed_item = craft_perform_recipe(storage[i].items, d->asset, &producer[i].processed_item_qty);
|
||||
producer[i].process_time = game_time() + game_rules.furnace_cook_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,86 +26,86 @@ static world_component_cache component_cache;
|
|||
entity_view *world_build_entity_view(int64_t e) {
|
||||
entity_view *cached_ev = world_snapshot_get(&streamer_snapshot, e);
|
||||
if (cached_ev) return cached_ev;
|
||||
|
||||
|
||||
entity_view view = {0};
|
||||
|
||||
|
||||
const Classify *classify = ecs_get(world_ecs(), e, Classify);
|
||||
ZPL_ASSERT(classify);
|
||||
|
||||
|
||||
view.kind = classify->id;
|
||||
|
||||
|
||||
const Position *pos = ecs_get(world_ecs(), e, Position);
|
||||
if (pos) {
|
||||
view.x = pos->x;
|
||||
view.y = pos->y;
|
||||
}
|
||||
|
||||
|
||||
const Velocity *vel = ecs_get(world_ecs(), e, Velocity);
|
||||
if (vel) {
|
||||
view.flag |= EFLAG_INTERP;
|
||||
view.vx = vel->x;
|
||||
view.vy = vel->y;
|
||||
}
|
||||
|
||||
|
||||
const Health *health = ecs_get(world_ecs(), e, Health);
|
||||
if (health) {
|
||||
view.hp = health->hp;
|
||||
view.max_hp = health->max_hp;
|
||||
}
|
||||
|
||||
|
||||
if (ecs_get(world_ecs(), e, Vehicle)) {
|
||||
Vehicle const* veh = ecs_get(world_ecs(), e, Vehicle);
|
||||
view.heading = veh->heading;
|
||||
view.veh_kind = veh->veh_kind;
|
||||
}
|
||||
|
||||
|
||||
if (ecs_get(world_ecs(), e, Item)) {
|
||||
Item const* dr = ecs_get(world_ecs(), e, Item);
|
||||
view.asset = dr->kind;
|
||||
view.quantity = dr->quantity;
|
||||
view.durability = dr->durability;
|
||||
}
|
||||
|
||||
|
||||
if (ecs_get(world_ecs(), e, Device)) {
|
||||
Device const* dev = ecs_get(world_ecs(), e, Device);
|
||||
view.asset = dev->asset;
|
||||
view.progress_active = dev->progress_active;
|
||||
view.progress_value = dev->progress_value;
|
||||
}
|
||||
|
||||
|
||||
view.inside_vehicle = ecs_get(world_ecs(), e, IsInVehicle) != 0 ? true : false;
|
||||
|
||||
|
||||
Inventory *inv = 0;
|
||||
if ((inv = ecs_get_mut_if_ex(world_ecs(), e, Inventory))) {
|
||||
view.has_items = true;
|
||||
|
||||
|
||||
for (int i = 0; i < ITEMS_INVENTORY_SIZE; i += 1) {
|
||||
const Item *it = ecs_get_if(world_ecs(), inv->items[i], Item);
|
||||
view.items[i] = it ? *it : (Item){0};
|
||||
}
|
||||
|
||||
|
||||
const Input *in = ecs_get(world_ecs(), e, Input);
|
||||
if (in){
|
||||
view.selected_item = in->selected_item;
|
||||
view.pick_ent = (uint64_t)in->pick_ent;
|
||||
view.sel_ent = (uint64_t)in->sel_ent;
|
||||
|
||||
|
||||
if (world_entity_valid(in->storage_ent)){
|
||||
ItemContainer *ic = 0;
|
||||
if ((ic = ecs_get_mut_if_ex(world_ecs(), in->storage_ent, ItemContainer))){
|
||||
view.has_storage_items = true;
|
||||
|
||||
|
||||
for (int i = 0; i < ITEMS_CONTAINER_SIZE; i += 1) {
|
||||
const Item *it = ecs_get_if(world_ecs(), ic->items[i], Item);
|
||||
view.storage_items[i] = it ? *it : (Item){0};
|
||||
}
|
||||
|
||||
|
||||
view.storage_selected_item = in->storage_selected_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Chunk *chunk = 0;
|
||||
if ((chunk = ecs_get_mut_if(world_ecs(), e, Chunk))) {
|
||||
view.chk_id = chunk->id;
|
||||
|
@ -114,16 +114,16 @@ entity_view *world_build_entity_view(int64_t e) {
|
|||
view.blocks_used = 1;
|
||||
view.is_dirty = chunk->is_dirty;
|
||||
chunk->is_dirty = false;
|
||||
|
||||
|
||||
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
|
||||
view.blocks[i] = world.block_mapping[chunk->id][i];
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
|
||||
view.outer_blocks[i] = world.outer_block_mapping[chunk->id][i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
world_snapshot_set(&streamer_snapshot, e, view);
|
||||
return world_snapshot_get(&streamer_snapshot, e);
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ int32_t tracker_write_create(librg_world *w, librg_event *e) {
|
|||
#endif
|
||||
size_t actual_length = librg_event_size_get(w, e);
|
||||
char *buffer = librg_event_buffer_get(w, e);
|
||||
|
||||
|
||||
return (int32_t)entity_view_pack_struct(buffer, actual_length, world_build_entity_view(entity_id));
|
||||
}
|
||||
|
||||
|
@ -159,14 +159,14 @@ int32_t tracker_write_update(librg_world *w, librg_event *e) {
|
|||
size_t actual_length = librg_event_size_get(w, e);
|
||||
char *buffer = librg_event_buffer_get(w, e);
|
||||
entity_view *view = world_build_entity_view(entity_id);
|
||||
|
||||
|
||||
// NOTE(zaklaus): exclude chunks from updates as they never move
|
||||
{
|
||||
if (view->kind == EKIND_CHUNK && !view->is_dirty) {
|
||||
return LIBRG_WRITE_REJECT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE(zaklaus): action-based updates
|
||||
#if ECO2D_STREAM_ACTIONFILTER
|
||||
{
|
||||
|
@ -175,7 +175,7 @@ int32_t tracker_write_update(librg_world *w, librg_event *e) {
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
return (int32_t)entity_view_pack_struct(buffer, actual_length, view);
|
||||
}
|
||||
|
||||
|
@ -198,15 +198,15 @@ void world_chunk_setup_grid(void) {
|
|||
world.outer_block_mapping[i] = zpl_malloc(sizeof(block_id)*zpl_square(world.chunk_size));
|
||||
chunk->id = i;
|
||||
chunk->is_dirty = false;
|
||||
|
||||
|
||||
for (int y = 0; y < world.chunk_size; y += 1) {
|
||||
for (int x = 0; x < world.chunk_size; x += 1) {
|
||||
int chk_x = chunk->x * world.chunk_size;
|
||||
int chk_y = chunk->y * world.chunk_size;
|
||||
|
||||
|
||||
block_id *c = &world.block_mapping[i][(y*world.chunk_size)+x];
|
||||
*c = world.data[(chk_y+y)*world.dim + (chk_x+x)];
|
||||
|
||||
|
||||
c = &world.outer_block_mapping[i][(y*world.chunk_size)+x];
|
||||
*c = world.outer_data[(chk_y+y)*world.dim + (chk_x+x)];
|
||||
}
|
||||
|
@ -217,14 +217,14 @@ void world_chunk_setup_grid(void) {
|
|||
static inline
|
||||
void world_configure_tracker(void) {
|
||||
world.tracker = librg_world_create();
|
||||
|
||||
|
||||
ZPL_ASSERT_MSG(world.tracker, "[ERROR] An error occurred while trying to create a server world.");
|
||||
|
||||
|
||||
/* config our world grid */
|
||||
librg_config_chunksize_set(world.tracker, WORLD_BLOCK_SIZE * world.chunk_size, WORLD_BLOCK_SIZE * world.chunk_size, 1);
|
||||
librg_config_chunkamount_set(world.tracker, world.chunk_amount, world.chunk_amount, 0);
|
||||
librg_config_chunkoffset_set(world.tracker, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG, LIBRG_OFFSET_BEG);
|
||||
|
||||
|
||||
librg_event_set(world.tracker, LIBRG_WRITE_CREATE, tracker_write_create);
|
||||
librg_event_set(world.tracker, LIBRG_WRITE_REMOVE, tracker_write_remove);
|
||||
librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, tracker_write_update);
|
||||
|
@ -234,14 +234,14 @@ static inline
|
|||
void world_init_worldgen_data(void) {
|
||||
world.data = zpl_malloc(sizeof(block_id)*world.size);
|
||||
world.outer_data = zpl_malloc(sizeof(block_id)*world.size);
|
||||
|
||||
|
||||
ZPL_ASSERT(world.data && world.outer_data);
|
||||
}
|
||||
|
||||
static inline
|
||||
void world_setup_ecs(void) {
|
||||
world.ecs = ecs_init();
|
||||
|
||||
|
||||
ECS_IMPORT(world.ecs, Components);
|
||||
ECS_IMPORT(world.ecs, Systems);
|
||||
world.ecs_update = ecs_query_new(world.ecs, "components.ClientInfo, components.Position");
|
||||
|
@ -261,7 +261,7 @@ static inline
|
|||
void world_generate_instance(void) {
|
||||
int32_t world_build_status = worldgen_build(&world);
|
||||
ZPL_ASSERT(world_build_status >= 0);
|
||||
|
||||
|
||||
for (int i = 0; i < zpl_square(world.dim); ++i) {
|
||||
if (world.data[i] == 0) {
|
||||
ZPL_PANIC("Worldgen failure! Block %d is unset!\n", i);
|
||||
|
@ -283,10 +283,10 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
|
|||
world.seed = seed;
|
||||
world.chunk_size = chunk_size;
|
||||
world.chunk_amount = chunk_amount;
|
||||
|
||||
|
||||
world.dim = (world.chunk_size * world.chunk_amount);
|
||||
world.size = world.dim * world.dim;
|
||||
|
||||
|
||||
world_configure_tracker();
|
||||
world_setup_ecs();
|
||||
world_init_worldgen_data();
|
||||
|
@ -294,9 +294,9 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
|
|||
world_init_mapping();
|
||||
world_chunk_setup_grid();
|
||||
world_free_worldgen_data();
|
||||
|
||||
|
||||
zpl_printf("[INFO] Created a new server world\n");
|
||||
|
||||
|
||||
return WORLD_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
@ -322,33 +322,33 @@ int32_t world_destroy(void) {
|
|||
static void world_tracker_update(uint8_t ticker, float freq, uint8_t radius) {
|
||||
if (world.tracker_update[ticker] > (float)(get_cached_time())) return;
|
||||
world.tracker_update[ticker] = (float)(get_cached_time()) + freq;
|
||||
|
||||
|
||||
profile(PROF_WORLD_WRITE) {
|
||||
ecs_iter_t it = ecs_query_iter(world_ecs(), world.ecs_update);
|
||||
static char buffer[WORLD_LIBRG_BUFSIZ] = {0};
|
||||
world.active_layer_id = ticker;
|
||||
|
||||
|
||||
while (ecs_query_next(&it)) {
|
||||
ClientInfo *p = ecs_field(&it, ClientInfo, 1);
|
||||
|
||||
|
||||
for (int i = 0; i < it.count; i++) {
|
||||
size_t datalen = WORLD_LIBRG_BUFSIZ;
|
||||
|
||||
|
||||
if (!p[i].active)
|
||||
continue;
|
||||
|
||||
|
||||
int32_t result = librg_world_write(world_tracker(), it.entities[i], radius, buffer, &datalen, NULL);
|
||||
|
||||
|
||||
if (result > 0) {
|
||||
zpl_printf("[info] buffer size was not enough, please increase it by at least: %d\n", result);
|
||||
} else if (result < 0) {
|
||||
zpl_printf("[error] an error happened writing the world %d\n", result);
|
||||
}
|
||||
|
||||
|
||||
pkt_send_librg_update((uint64_t)p[i].peer, p[i].view_id, ticker, buffer, datalen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
world_snapshot_clear(&streamer_snapshot);
|
||||
}
|
||||
}
|
||||
|
@ -358,11 +358,11 @@ int32_t world_update() {
|
|||
world_component_cache_clear(&component_cache);
|
||||
ecs_progress(world.ecs, 0.0f);
|
||||
}
|
||||
|
||||
|
||||
float fast_ms = WORLD_TRACKER_UPDATE_MP_FAST_MS;
|
||||
float normal_ms = WORLD_TRACKER_UPDATE_MP_NORMAL_MS;
|
||||
float slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS;
|
||||
|
||||
|
||||
#if 1
|
||||
if (game_get_kind() == GAMEKIND_SINGLE) {
|
||||
fast_ms = WORLD_TRACKER_UPDATE_FAST_MS;
|
||||
|
@ -370,11 +370,11 @@ int32_t world_update() {
|
|||
slow_ms = WORLD_TRACKER_UPDATE_SLOW_MS;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
world_tracker_update(0, fast_ms, 1);
|
||||
world_tracker_update(1, normal_ms, 2);
|
||||
world_tracker_update(2, slow_ms, 3);
|
||||
|
||||
|
||||
entity_update_action_timers();
|
||||
debug_replay_update();
|
||||
return 0;
|
||||
|
@ -470,11 +470,11 @@ world_block_lookup world_block_from_realpos(float x, float y) {
|
|||
int32_t size = world.chunk_size * WORLD_BLOCK_SIZE;
|
||||
int16_t chunk_x, chunk_y;
|
||||
librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL);
|
||||
|
||||
|
||||
// NOTE(zaklaus): pos relative to chunk
|
||||
float chx = x - chunk_x * size;
|
||||
float chy = y - chunk_y * size;
|
||||
|
||||
|
||||
uint16_t bx = (uint16_t)chx / WORLD_BLOCK_SIZE;
|
||||
uint16_t by = (uint16_t)chy / WORLD_BLOCK_SIZE;
|
||||
uint16_t block_idx = (by*world.chunk_size)+bx;
|
||||
|
@ -484,15 +484,15 @@ world_block_lookup world_block_from_realpos(float x, float y) {
|
|||
bid = world.block_mapping[chunk_id][block_idx];
|
||||
is_outer = false;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(zaklaus): pos relative to block's center
|
||||
float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
|
||||
float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
|
||||
|
||||
|
||||
// NOTE(zaklaus): absolute pos in world.
|
||||
float abox = (uint16_t)(x / WORLD_BLOCK_SIZE) * (float)WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2.0f;
|
||||
float aboy = (uint16_t)(y / WORLD_BLOCK_SIZE) * (float)WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2.0f;
|
||||
|
||||
|
||||
world_block_lookup lookup = {
|
||||
.id = block_idx,
|
||||
.bid = bid,
|
||||
|
@ -504,7 +504,7 @@ world_block_lookup world_block_from_realpos(float x, float y) {
|
|||
.aoy = aboy,
|
||||
.is_outer = is_outer,
|
||||
};
|
||||
|
||||
|
||||
return lookup;
|
||||
}
|
||||
|
||||
|
@ -512,12 +512,12 @@ void world_chunk_destroy_block(float x, float y, bool drop_item) {
|
|||
world_block_lookup l = world_block_from_realpos(x, y);
|
||||
if (blocks_get_flags(l.bid) & BLOCK_FLAG_ESSENTIAL) return;
|
||||
world_chunk_replace_block(l.chunk_id, l.id, 0);
|
||||
|
||||
|
||||
if (l.is_outer && l.bid > 0 && drop_item) {
|
||||
asset_id item_asset = blocks_get_asset(l.bid);
|
||||
if (item_find(item_asset) == ASSET_INVALID) return;
|
||||
uint64_t e = item_spawn(item_asset, 1);
|
||||
|
||||
|
||||
Position *dest = ecs_get_mut(world_ecs(), e, Position);
|
||||
dest->x = x;
|
||||
dest->y = y;
|
||||
|
@ -530,20 +530,20 @@ world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) {
|
|||
if (bid == 0) {
|
||||
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 = {
|
||||
.id = block_idx,
|
||||
.bid = bid,
|
||||
|
@ -552,7 +552,7 @@ world_block_lookup world_block_from_index(int64_t id, uint16_t block_idx) {
|
|||
.oy = boy,
|
||||
.chunk_e = world.chunk_mapping[id],
|
||||
};
|
||||
|
||||
|
||||
return lookup;
|
||||
}
|
||||
|
||||
|
@ -644,18 +644,18 @@ void * world_component_cached(ecs_world_t *world_ecs, ecs_entity_t entity, ecs_i
|
|||
if (!component_cache.entries || world.ecs_stage == NULL) {
|
||||
return ecs_get_mut_id(world_ecs, entity, id);
|
||||
}
|
||||
|
||||
|
||||
static char buffer[256] = {0};
|
||||
zpl_snprintf(buffer, 256, "%llu_%llu", entity, id);
|
||||
|
||||
|
||||
uint64_t uid = zpl_crc64(buffer, zpl_strlen(buffer));
|
||||
zpl_uintptr *value = world_component_cache_get(&component_cache, uid);
|
||||
|
||||
|
||||
if (!value) {
|
||||
void *the_value = ecs_get_mut_id(world_ecs, entity, id);
|
||||
world_component_cache_set(&component_cache, uid, (zpl_uintptr)the_value);
|
||||
value = world_component_cache_get(&component_cache, uid);
|
||||
}
|
||||
|
||||
|
||||
return (void *)*value;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue