Local server inventory logic (4x4 crafting)

not the prettiest solution, but it works for now
This commit is contained in:
xtreme8000 2023-12-01 17:58:15 +01:00
parent 1064b5e173
commit beee78a084
15 changed files with 571 additions and 171 deletions

View file

@ -97,6 +97,7 @@ add_executable(cavex
source/item/inventory.c source/item/inventory.c
source/item/items.c source/item/items.c
source/item/tool.c source/item/tool.c
source/item/recipe.c
source/item/items/item_sugarcane.c source/item/items/item_sugarcane.c
source/network/client_interface.c source/network/client_interface.c
@ -105,6 +106,7 @@ add_executable(cavex
source/network/server_interface.c source/network/server_interface.c
source/network/server_local.c source/network/server_local.c
source/network/server_world.c source/network/server_world.c
source/network/inventory_player.c
source/graphics/gfx_util.c source/graphics/gfx_util.c
source/graphics/gui_util.c source/graphics/gui_util.c

View file

@ -19,11 +19,11 @@
* main menu * main menu
* generation of new chunks * generation of new chunks
* biome colors * biome colors
* player physics * ~~player physics~~
* inventory management * ~~inventory management~~
* ~~block placement~~ and destruction logic * ~~block placement~~ and destruction logic
* (random) block updates * ~~(random)~~ block updates
* item actions * ~~item actions~~
* real texture pack support * real texture pack support
* Beta 1.7.3 multiplayer support * Beta 1.7.3 multiplayer support

View file

@ -63,6 +63,7 @@ enum block_type {
BLOCK_SLAB = 44, BLOCK_SLAB = 44,
BLOCK_MOSSY_COBBLE = 48, BLOCK_MOSSY_COBBLE = 48,
BLOCK_OBSIDIAN = 49, BLOCK_OBSIDIAN = 49,
BLOCK_WORKBENCH = 58,
BLOCK_LADDER = 65, BLOCK_LADDER = 65,
BLOCK_REDSTONE_TORCH = 75, BLOCK_REDSTONE_TORCH = 75,
BLOCK_SNOW = 78, BLOCK_SNOW = 78,

View file

@ -94,22 +94,11 @@ static bool entity_server_tick(struct entity* e, struct server_local* s) {
e->pos, e->pos,
(vec3) {s->player.x, s->player.y - 0.6F, s->player.z}) (vec3) {s->player.x, s->player.y - 0.6F, s->player.z})
< glm_pow2(2.0F)) { // allow pickup after 2s < glm_pow2(2.0F)) { // allow pickup after 2s
bool slots_changed[INVENTORY_SIZE];
memset(slots_changed, false, sizeof(slots_changed));
// TODO: case where item cannot be picked up completely // TODO: case where item cannot be picked up completely
inventory_collect_inventory(&s->player.inventory, &e->data.item.item, if(s->player.active_inventory && s->player.active_inventory->logic
slots_changed); && s->player.active_inventory->logic->on_collect)
s->player.active_inventory->logic->on_collect(
for(size_t k = 0; k < INVENTORY_SIZE; k++) { s->player.active_inventory, &e->data.item.item);
if(slots_changed[k])
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_INVENTORY_SLOT,
.payload.inventory_slot.window = WINDOWC_INVENTORY,
.payload.inventory_slot.slot = k,
.payload.inventory_slot.item = s->player.inventory.items[k],
});
}
clin_rpc_send(&(struct client_rpc) { clin_rpc_send(&(struct client_rpc) {
.type = CRPC_PICKUP_ITEM, .type = CRPC_PICKUP_ITEM,

View file

@ -21,9 +21,12 @@
#include "inventory.h" #include "inventory.h"
bool inventory_create(struct inventory* inv, size_t capacity) { bool inventory_create(struct inventory* inv, struct inventory_logic* logic,
void* user, size_t capacity) {
assert(inv && capacity > 0); assert(inv && capacity > 0);
inv->user = user;
inv->logic = logic;
inv->capacity = capacity; inv->capacity = capacity;
inv->items = malloc(sizeof(struct item_data) * capacity); inv->items = malloc(sizeof(struct item_data) * capacity);
@ -34,6 +37,9 @@ bool inventory_create(struct inventory* inv, size_t capacity) {
ilist_inventory_init_field(inv); ilist_inventory_init_field(inv);
if(inv->logic && inv->logic->on_create)
inv->logic->on_create(inv);
return true; return true;
} }
@ -50,6 +56,10 @@ void inventory_copy(struct inventory* inv, struct inventory* from) {
void inventory_destroy(struct inventory* inv) { void inventory_destroy(struct inventory* inv) {
assert(inv && inv->items); assert(inv && inv->items);
if(inv->logic && inv->logic->on_destroy)
inv->logic->on_destroy(inv);
free(inv->items); free(inv->items);
} }
@ -82,72 +92,6 @@ void inventory_consume(struct inventory* inv, size_t slot) {
} }
} }
#define min(a, b) ((a) < (b) ? (a) : (b))
bool inventory_collect(struct inventory* inv, struct item_data* item,
uint8_t* slot_priority, size_t slot_length, bool* mask) {
assert(inv && item && item->id != 0 && mask);
struct item* it = item_get(item);
if(!it)
return false;
while(item->count > 0) {
bool has_canidate_equal = false;
size_t candidate_equal = 0;
bool has_canidate_empty = false;
size_t candidate_empty = 0;
for(size_t k = 0; k < slot_length; k++) {
uint8_t slot = slot_priority[k];
if(inv->items[slot].id == item->id
&& inv->items[slot].durability == item->durability
&& inv->items[slot].count < it->max_stack) {
has_canidate_equal = true;
candidate_equal = slot;
break;
}
if(!has_canidate_empty && inv->items[slot].id == 0) {
has_canidate_empty = true;
candidate_empty = slot;
}
}
if(has_canidate_equal || has_canidate_empty) {
size_t candidate
= has_canidate_equal ? candidate_equal : candidate_empty;
size_t additional
= min(it->max_stack - inv->items[candidate].count, item->count);
inv->items[candidate].id = item->id;
inv->items[candidate].durability = item->durability;
inv->items[candidate].count += additional;
item->count -= additional;
mask[candidate] = true;
} else {
return false;
}
}
return true;
}
bool inventory_collect_inventory(struct inventory* inv, struct item_data* item,
bool* mask) {
uint8_t priorities[INVENTORY_SIZE_HOTBAR + INVENTORY_SIZE_MAIN];
for(size_t k = 0; k < INVENTORY_SIZE_HOTBAR; k++)
priorities[k] = k + INVENTORY_SLOT_HOTBAR;
for(size_t k = 0; k < INVENTORY_SIZE_MAIN; k++)
priorities[k + INVENTORY_SIZE_HOTBAR] = k + INVENTORY_SLOT_MAIN;
return inventory_collect(inv, item, priorities,
sizeof(priorities) / sizeof(*priorities), mask);
}
size_t inventory_get_hotbar(struct inventory* inv) { size_t inventory_get_hotbar(struct inventory* inv) {
assert(inv); assert(inv);
return inv->hotbar_slot; return inv->hotbar_slot;
@ -286,15 +230,30 @@ static bool inventory_place_one_item(struct inventory* inv, size_t slot) {
return true; return true;
} }
bool inventory_action(struct inventory* inv, size_t slot, bool right) { bool inventory_action(struct inventory* inv, size_t slot, bool right,
set_inv_slot_t changes) {
assert(inv && slot < inv->capacity); assert(inv && slot < inv->capacity);
bool result = true;
if(inv->logic && inv->logic->pre_action
&& !inv->logic->pre_action(inv, slot, right, changes))
result = false;
if(result) {
if(right) { if(right) {
return (inv->picked_item.id == 0) ? result = (inv->picked_item.id == 0) ?
inventory_pick_item_split(inv, slot) : inventory_pick_item_split(inv, slot) :
inventory_place_one_item(inv, slot); inventory_place_one_item(inv, slot);
} else { } else {
return (inv->picked_item.id == 0) ? inventory_pick_item(inv, slot) : result = (inv->picked_item.id == 0) ?
inventory_pick_item(inv, slot) :
inventory_place_item(inv, slot); inventory_place_item(inv, slot);
} }
}
if(inv->logic && inv->logic->post_action)
inv->logic->post_action(inv, slot, right, result, changes);
return result;
} }

View file

@ -20,6 +20,7 @@
#ifndef INVENTORY_H #ifndef INVENTORY_H
#define INVENTORY_H #define INVENTORY_H
#include <m-lib/m-dict.h>
#include <m-lib/m-i-list.h> #include <m-lib/m-i-list.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
@ -40,6 +41,10 @@
#define SPECIAL_SLOT_PICKED_ITEM 255 #define SPECIAL_SLOT_PICKED_ITEM 255
DICT_SET_DEF(set_inv_slot, size_t)
struct inventory_logic;
struct inventory { struct inventory {
struct item_data picked_item; struct item_data picked_item;
struct item_data* items; struct item_data* items;
@ -50,20 +55,30 @@ struct inventory {
bool action_type; bool action_type;
size_t action_slot; size_t action_slot;
} revision; // for window container } revision; // for window container
struct inventory_logic* logic;
void* user;
ILIST_INTERFACE(ilist_inventory, struct inventory); ILIST_INTERFACE(ilist_inventory, struct inventory);
}; };
struct inventory_logic {
void (*on_create)(struct inventory* inv);
void (*on_destroy)(struct inventory* inv);
bool (*pre_action)(struct inventory* inv, size_t slot, bool right,
set_inv_slot_t changes);
void (*post_action)(struct inventory* inv, size_t slot, bool right,
bool accepted, set_inv_slot_t changes);
bool (*on_collect)(struct inventory* inv, struct item_data* item);
void (*on_close)(struct inventory* inv);
};
ILIST_DEF(ilist_inventory, struct inventory, M_POD_OPLIST) ILIST_DEF(ilist_inventory, struct inventory, M_POD_OPLIST)
bool inventory_create(struct inventory* inv, size_t capacity); bool inventory_create(struct inventory* inv, struct inventory_logic* logic,
void* user, size_t capacity);
void inventory_copy(struct inventory* inv, struct inventory* from); void inventory_copy(struct inventory* inv, struct inventory* from);
void inventory_destroy(struct inventory* inv); void inventory_destroy(struct inventory* inv);
void inventory_clear(struct inventory* inv); void inventory_clear(struct inventory* inv);
void inventory_consume(struct inventory* inv, size_t slot); void inventory_consume(struct inventory* inv, size_t slot);
bool inventory_collect(struct inventory* inv, struct item_data* item,
uint8_t* slot_priority, size_t slot_length, bool* mask);
bool inventory_collect_inventory(struct inventory* inv, struct item_data* item,
bool* mask);
size_t inventory_get_hotbar(struct inventory* inv); size_t inventory_get_hotbar(struct inventory* inv);
void inventory_set_hotbar(struct inventory* inv, size_t slot); void inventory_set_hotbar(struct inventory* inv, size_t slot);
bool inventory_get_hotbar_item(struct inventory* inv, struct item_data* item); bool inventory_get_hotbar_item(struct inventory* inv, struct item_data* item);
@ -75,6 +90,7 @@ bool inventory_get_slot(struct inventory* inv, size_t slot,
void inventory_clear_picked_item(struct inventory* inv); void inventory_clear_picked_item(struct inventory* inv);
void inventory_set_picked_item(struct inventory* inv, struct item_data item); void inventory_set_picked_item(struct inventory* inv, struct item_data item);
bool inventory_get_picked_item(struct inventory* inv, struct item_data* item); bool inventory_get_picked_item(struct inventory* inv, struct item_data* item);
bool inventory_action(struct inventory* inv, size_t slot, bool right); bool inventory_action(struct inventory* inv, size_t slot, bool right,
set_inv_slot_t changes);
#endif #endif

View file

@ -34,6 +34,8 @@ struct item_data {
enum item_type { enum item_type {
ITEM_COAL = 263, ITEM_COAL = 263,
ITEM_DIAMOND = 264, ITEM_DIAMOND = 264,
ITEM_DIAMOND_PICKAXE = 278,
ITEM_STICK = 280,
ITEM_SEED = 295, ITEM_SEED = 295,
ITEM_WHEAT = 296, ITEM_WHEAT = 296,
ITEM_BREAD = 297, ITEM_BREAD = 297,
@ -52,10 +54,10 @@ enum item_type {
struct server_local; struct server_local;
enum armor_type { enum armor_type {
ARMOR_TYPE_HELMET, ARMOR_TYPE_HELMET = 0,
ARMOR_TYPE_CHESTPLATE, ARMOR_TYPE_CHESTPLATE = 1,
ARMOR_TYPE_LEGGINGS, ARMOR_TYPE_LEGGINGS = 2,
ARMOR_TYPE_BOOTS, ARMOR_TYPE_BOOTS = 3,
}; };
enum armor_tier { enum armor_tier {

147
source/item/recipe.c Normal file
View file

@ -0,0 +1,147 @@
/*
Copyright (c) 2023 ByteBit/xtreme8000
This file is part of CavEX.
CavEX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CavEX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CavEX. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include "recipe.h"
array_recipe_t recipes_crafting;
void recipe_init() {
array_recipe_init(recipes_crafting);
recipe_add(
recipes_crafting,
(struct item_data) {.id = BLOCK_PLANKS, .durability = 0, .count = 4}, 1,
1, (uint8_t[]) {1}, (struct item_data) {.id = BLOCK_LOG}, false);
recipe_add(
recipes_crafting,
(struct item_data) {.id = ITEM_STICK, .durability = 0, .count = 4}, 1,
2, (uint8_t[]) {1, 1}, (struct item_data) {.id = BLOCK_PLANKS}, false);
recipe_add(
recipes_crafting,
(struct item_data) {.id = BLOCK_WORKBENCH, .durability = 0, .count = 1},
2, 2, (uint8_t[]) {1, 1, 1, 1}, (struct item_data) {.id = BLOCK_PLANKS},
false);
recipe_add(recipes_crafting,
(struct item_data) {
.id = ITEM_DIAMOND_PICKAXE, .durability = 0, .count = 1},
3, 3, (uint8_t[]) {1, 1, 1, 0, 2, 0, 0, 2, 0},
(struct item_data) {.id = ITEM_DIAMOND}, false,
(struct item_data) {.id = ITEM_STICK}, false);
}
void recipe_add(array_recipe_t recipes, struct item_data result, size_t width,
size_t height, uint8_t* shape, ...) {
assert(recipes && width > 0 && height > 0 && width * height <= 9 && shape);
int count = 0;
for(size_t k = 0; k < width * height; k++) {
if(shape[k] > count)
count = shape[k];
}
assert(count > 0 && count <= 9);
struct recipe_ingredients ingredients[9];
va_list inputs;
va_start(inputs, shape);
for(size_t k = 0; k < count; k++) {
ingredients[k].item = va_arg(inputs, struct item_data);
ingredients[k].match_durability = (bool)va_arg(inputs, int);
}
va_end(inputs);
struct recipe r = (struct recipe) {
.result = result,
.width = width,
.height = height,
};
for(size_t k = 0; k < width * height; k++) {
if(shape[k] > 0) {
r.shape[k] = ingredients[shape[k] - 1];
} else {
r.shape[k].item.id = 0;
}
}
array_recipe_push_back(recipes, r);
}
bool recipe_match(array_recipe_t recipes, struct item_data slots[9],
bool slot_empty[9], struct item_data* result) {
assert(recipes && slots && slot_empty && result);
array_recipe_it_t it;
array_recipe_it(it, recipes);
while(!array_recipe_end_p(it)) {
struct recipe* current = array_recipe_ref(it);
for(size_t y = 0; y <= 3 - current->height; y++) {
for(size_t x = 0; x <= 3 - current->width; x++) {
bool match = true;
// check that outside of pattern is empty
for(size_t py = 0; py < 3 && match; py++) {
for(size_t px = 0; px < 3 && match; px++) {
if((px < x || px >= x + current->width || py < y
|| py >= y + current->height)
&& !slot_empty[px + py * 3])
match = false;
}
}
// check pattern itself
for(size_t py = 0; py < current->height && match; py++) {
for(size_t px = 0; px < current->width && match; px++) {
size_t slots_idx = (px + x) + (py + y) * 3;
size_t shape_idx = px + py * current->width;
if(current->shape[shape_idx].item.id == 0) {
if(!slot_empty[slots_idx])
match = false;
} else {
if(slot_empty[slots_idx]
|| current->shape[shape_idx].item.id
!= slots[slots_idx].id
|| (current->shape->match_durability
&& current->shape[shape_idx].item.durability
!= slots[slots_idx].durability))
match = false;
}
}
}
if(match) {
*result = current->result;
return true;
}
}
}
array_recipe_next(it);
}
return false;
}

49
source/item/recipe.h Normal file
View file

@ -0,0 +1,49 @@
/*
Copyright (c) 2023 ByteBit/xtreme8000
This file is part of CavEX.
CavEX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CavEX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CavEX. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef RECIPE_H
#define RECIPE_H
#include <m-lib/m-array.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "items.h"
struct recipe {
struct item_data result;
size_t width, height;
struct recipe_ingredients {
struct item_data item;
bool match_durability;
} shape[9];
};
ARRAY_DEF(array_recipe, struct recipe, M_POD_OPLIST)
extern array_recipe_t recipes_crafting;
void recipe_init(void);
void recipe_add(array_recipe_t recipes, struct item_data result, size_t width,
size_t height, uint8_t* shape, ...);
bool recipe_match(array_recipe_t recipes, struct item_data slots[9],
bool slot_empty[9], struct item_data* result);
#endif

View file

@ -68,7 +68,7 @@ bool windowc_new_action(struct window_container* wc, uint16_t* action_id,
if(!next) if(!next)
return false; return false;
if(!inventory_create(next, INVENTORY_SIZE)) // TODO if(!inventory_create(next, NULL, NULL, INVENTORY_SIZE)) // TODO
return false; return false;
if(!ilist_inventory_empty_p(wc->invs)) if(!ilist_inventory_empty_p(wc->invs))
@ -78,7 +78,7 @@ bool windowc_new_action(struct window_container* wc, uint16_t* action_id,
next->revision.action_type = action_type; next->revision.action_type = action_type;
next->revision.action_slot = action_slot; next->revision.action_slot = action_slot;
inventory_action(next, action_slot, action_type); inventory_action(next, action_slot, action_type, NULL);
*action_id = wc->next_action_id; *action_id = wc->next_action_id;
wc->next_action_id++; wc->next_action_id++;
@ -150,7 +150,7 @@ void windowc_slot_change(struct window_container* wc, size_t slot,
struct inventory* current = ilist_inventory_ref(it); struct inventory* current = ilist_inventory_ref(it);
inventory_copy(current, prev); inventory_copy(current, prev);
inventory_action(current, current->revision.action_slot, inventory_action(current, current->revision.action_slot,
current->revision.action_type); current->revision.action_type, NULL);
prev = current; prev = current;
ilist_inventory_next(it); ilist_inventory_next(it);

View file

@ -34,6 +34,7 @@
#include "game/gui/screen.h" #include "game/gui/screen.h"
#include "graphics/gfx_util.h" #include "graphics/gfx_util.h"
#include "graphics/gui_util.h" #include "graphics/gui_util.h"
#include "item/recipe.h"
#include "network/client_interface.h" #include "network/client_interface.h"
#include "network/server_interface.h" #include "network/server_interface.h"
#include "network/server_local.h" #include "network/server_local.h"
@ -70,6 +71,7 @@ int main(void) {
input_init(); input_init();
blocks_init(); blocks_init();
items_init(); items_init();
recipe_init();
gfx_setup(); gfx_setup();
screen_set(&screen_select_world); screen_set(&screen_select_world);

View file

@ -0,0 +1,22 @@
/*
Copyright (c) 2023 ByteBit/xtreme8000
This file is part of CavEX.
CavEX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CavEX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CavEX. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../item/inventory.h"
extern struct inventory_logic inventory_logic_player;

View file

@ -0,0 +1,220 @@
/*
Copyright (c) 2023 ByteBit/xtreme8000
This file is part of CavEX.
CavEX is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CavEX is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CavEX. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../item/recipe.h"
#include "../item/window_container.h"
#include "server_local.h"
static bool inv_match_crafting(struct inventory* inv,
struct item_data* result) {
struct item_data slots[9];
bool slot_empty[9];
memset(slot_empty, true, sizeof(slot_empty));
for(size_t k = 0; k < INVENTORY_SIZE_CRAFTING; k++)
slot_empty[k + k / 2] = !inventory_get_slot(
inv, INVENTORY_SLOT_CRAFTING + k, slots + k + k / 2);
return recipe_match(recipes_crafting, slots, slot_empty, result);
}
static bool inv_pre_action(struct inventory* inv, size_t slot, bool right,
set_inv_slot_t changes) {
if(slot >= INVENTORY_SLOT_ARMOR
&& slot < INVENTORY_SLOT_ARMOR + INVENTORY_SIZE_ARMOR) {
struct item_data it;
if(inventory_get_picked_item(inv, &it) && item_get(&it)
&& (!item_get(&it)->armor.is_armor
|| item_get(&it)->armor.type
!= ARMOR_TYPE_HELMET + slot - INVENTORY_SLOT_ARMOR))
return false;
}
if(slot == INVENTORY_SLOT_OUTPUT) {
struct item_data output;
if(!right && inventory_get_slot(inv, INVENTORY_SLOT_OUTPUT, &output)) {
for(size_t k = INVENTORY_SLOT_CRAFTING;
k < INVENTORY_SLOT_CRAFTING + INVENTORY_SIZE_CRAFTING; k++) {
struct item_data it;
if(inventory_get_slot(inv, k, &it) && it.count > 1) {
it.count--;
inventory_set_slot(inv, k, it);
} else {
inventory_clear_slot(inv, k);
}
set_inv_slot_push(changes, k);
}
struct item_data picked;
if(inventory_get_picked_item(inv, &picked)) {
struct item* it_type = item_get(&picked);
if(it_type && picked.id == output.id
&& picked.durability == output.durability
&& picked.count + output.count <= it_type->max_stack) {
picked.count += output.count;
inventory_set_picked_item(inv, picked);
set_inv_slot_push(changes, SPECIAL_SLOT_PICKED_ITEM);
return false;
}
} else {
return true;
}
}
return false;
}
return true;
}
static void inv_post_action(struct inventory* inv, size_t slot, bool right,
bool accepted, set_inv_slot_t changes) {
if((slot >= INVENTORY_SLOT_CRAFTING
&& slot < INVENTORY_SLOT_CRAFTING + INVENTORY_SIZE_CRAFTING)
|| slot == INVENTORY_SLOT_OUTPUT) {
struct item_data result;
if(inv_match_crafting(inv, &result)) {
inventory_set_slot(inv, INVENTORY_SLOT_OUTPUT, result);
} else {
inventory_clear_slot(inv, INVENTORY_SLOT_OUTPUT);
}
set_inv_slot_push(changes, INVENTORY_SLOT_OUTPUT);
}
}
static void inv_on_close(struct inventory* inv) {
struct server_local* s = inv->user;
set_inv_slot_t changes;
set_inv_slot_init(changes);
inventory_clear_slot(inv, INVENTORY_SLOT_OUTPUT);
set_inv_slot_push(changes, INVENTORY_SLOT_OUTPUT);
for(size_t k = INVENTORY_SLOT_CRAFTING;
k < INVENTORY_SLOT_CRAFTING + INVENTORY_SIZE_CRAFTING; k++) {
struct item_data item;
inventory_get_slot(inv, k, &item);
if(item.id != 0) {
inventory_clear_slot(inv, k);
set_inv_slot_push(changes, k);
server_local_spawn_item(
(vec3) {s->player.x, s->player.y, s->player.z}, &item, true, s);
}
}
struct item_data picked_item;
if(inventory_get_picked_item(inv, &picked_item)) {
inventory_clear_picked_item(inv);
set_inv_slot_push(changes, SPECIAL_SLOT_PICKED_ITEM);
server_local_spawn_item((vec3) {s->player.x, s->player.y, s->player.z},
&picked_item, true, s);
}
server_local_send_inv_changes(changes, inv, WINDOWC_INVENTORY);
set_inv_slot_clear(changes);
}
#define min(a, b) ((a) < (b) ? (a) : (b))
static bool inventory_collect(struct inventory* inv, struct item_data* item,
uint8_t* slot_priority, size_t slot_length,
set_inv_slot_t changes) {
assert(inv && item && item->id != 0 && changes);
struct item* it = item_get(item);
if(!it)
return false;
while(item->count > 0) {
bool has_canidate_equal = false;
size_t candidate_equal = 0;
bool has_canidate_empty = false;
size_t candidate_empty = 0;
for(size_t k = 0; k < slot_length; k++) {
uint8_t slot = slot_priority[k];
if(inv->items[slot].id == item->id
&& inv->items[slot].durability == item->durability
&& inv->items[slot].count < it->max_stack) {
has_canidate_equal = true;
candidate_equal = slot;
break;
}
if(!has_canidate_empty && inv->items[slot].id == 0) {
has_canidate_empty = true;
candidate_empty = slot;
}
}
if(has_canidate_equal || has_canidate_empty) {
size_t candidate
= has_canidate_equal ? candidate_equal : candidate_empty;
size_t additional
= min(it->max_stack - inv->items[candidate].count, item->count);
inv->items[candidate].id = item->id;
inv->items[candidate].durability = item->durability;
inv->items[candidate].count += additional;
item->count -= additional;
set_inv_slot_push(changes, candidate);
} else {
return false;
}
}
return true;
}
static bool inv_on_collect(struct inventory* inv, struct item_data* item) {
uint8_t priorities[INVENTORY_SIZE_HOTBAR + INVENTORY_SIZE_MAIN];
for(size_t k = 0; k < INVENTORY_SIZE_HOTBAR; k++)
priorities[k] = k + INVENTORY_SLOT_HOTBAR;
for(size_t k = 0; k < INVENTORY_SIZE_MAIN; k++)
priorities[k + INVENTORY_SIZE_HOTBAR] = k + INVENTORY_SLOT_MAIN;
set_inv_slot_t changes;
set_inv_slot_init(changes);
bool success
= inventory_collect(inv, item, priorities,
sizeof(priorities) / sizeof(*priorities), changes);
server_local_send_inv_changes(changes, inv, WINDOWC_INVENTORY);
set_inv_slot_clear(changes);
return success;
}
struct inventory_logic inventory_logic_player = {
.pre_action = inv_pre_action,
.post_action = inv_post_action,
.on_collect = inv_on_collect,
.on_create = NULL,
.on_destroy = NULL,
.on_close = inv_on_close,
};

View file

@ -26,14 +26,15 @@
#include "../item/window_container.h" #include "../item/window_container.h"
#include "../platform/thread.h" #include "../platform/thread.h"
#include "client_interface.h" #include "client_interface.h"
#include "inventory_logic.h"
#include "server_interface.h" #include "server_interface.h"
#include "server_local.h" #include "server_local.h"
#define CHUNK_DIST2(x1, x2, z1, z2) \ #define CHUNK_DIST2(x1, x2, z1, z2) \
(((x1) - (x2)) * ((x1) - (x2)) + ((z1) - (z2)) * ((z1) - (z2))) (((x1) - (x2)) * ((x1) - (x2)) + ((z1) - (z2)) * ((z1) - (z2)))
static struct entity* spawn_item(vec3 pos, struct item_data* it, bool throw, struct entity* server_local_spawn_item(vec3 pos, struct item_data* it,
struct server_local* s) { bool throw, struct server_local* s) {
uint32_t entity_id = entity_gen_id(s->entities); uint32_t entity_id = entity_gen_id(s->entities);
struct entity* e = dict_entity_safe_get(s->entities, entity_id); struct entity* e = dict_entity_safe_get(s->entities, entity_id);
entity_item(entity_id, e, true, &s->world, *it); entity_item(entity_id, e, true, &s->world, *it);
@ -82,12 +83,36 @@ void server_local_spawn_block_drops(struct server_local* s,
&s->rand_src); &s->rand_src);
for(size_t k = 0; k < count; k++) for(size_t k = 0; k < count; k++)
spawn_item((vec3) {blk_info->x + 0.5F, blk_info->y + 0.5F, server_local_spawn_item((vec3) {blk_info->x + 0.5F,
blk_info->y + 0.5F,
blk_info->z + 0.5F}, blk_info->z + 0.5F},
items + k, false, s); items + k, false, s);
} }
} }
void server_local_send_inv_changes(set_inv_slot_t changes,
struct inventory* inv, uint8_t window) {
assert(changes && inv);
set_inv_slot_it_t it;
set_inv_slot_it(it, changes);
while(!set_inv_slot_end_p(it)) {
size_t slot = *set_inv_slot_ref(it);
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_INVENTORY_SLOT,
.payload.inventory_slot.window = window,
.payload.inventory_slot.slot = slot,
.payload.inventory_slot.item = (slot == SPECIAL_SLOT_PICKED_ITEM) ?
inv->picked_item :
inv->items[slot],
});
set_inv_slot_next(it);
}
}
static void server_local_process(struct server_rpc* call, void* user) { static void server_local_process(struct server_rpc* call, void* user) {
assert(call && user); assert(call && user);
@ -111,15 +136,12 @@ static void server_local_process(struct server_rpc* call, void* user) {
call->payload.hotbar_slot.slot); call->payload.hotbar_slot.slot);
break; break;
case SRPC_WINDOW_CLICK: { case SRPC_WINDOW_CLICK: {
bool accept = false; set_inv_slot_t changes;
if(call->payload.window_click.window == WINDOWC_INVENTORY) { set_inv_slot_init(changes);
if(!(inventory_get_picked_item(&s->player.inventory, NULL)
&& call->payload.window_click.slot bool accept = inventory_action(
== INVENTORY_SLOT_OUTPUT)) s->player.active_inventory, call->payload.window_click.slot,
accept = inventory_action( call->payload.window_click.right_click, changes);
&s->player.inventory, call->payload.window_click.slot,
call->payload.window_click.right_click);
}
clin_rpc_send(&(struct client_rpc) { clin_rpc_send(&(struct client_rpc) {
.type = CRPC_WINDOW_TRANSACTION, .type = CRPC_WINDOW_TRANSACTION,
@ -129,60 +151,20 @@ static void server_local_process(struct server_rpc* call, void* user) {
.payload.window_transaction.window .payload.window_transaction.window
= call->payload.window_click.window, = call->payload.window_click.window,
}); });
server_local_send_inv_changes(changes, s->player.active_inventory,
call->payload.window_click.window);
set_inv_slot_clear(changes);
break; break;
} }
case SRPC_WINDOW_CLOSE: case SRPC_WINDOW_CLOSE: {
if(call->payload.window_click.window == WINDOWC_INVENTORY) { if(s->player.active_inventory && s->player.active_inventory->logic
bool slots_changed[INVENTORY_SIZE]; && s->player.active_inventory->logic->on_close)
memset(slots_changed, false, sizeof(slots_changed)); s->player.active_inventory->logic->on_close(
s->player.active_inventory);
inventory_clear_slot(&s->player.inventory, s->player.active_inventory = &s->player.inventory;
INVENTORY_SLOT_OUTPUT);
slots_changed[INVENTORY_SLOT_OUTPUT] = true;
for(size_t k = INVENTORY_SLOT_CRAFTING;
k < INVENTORY_SLOT_CRAFTING + INVENTORY_SIZE_CRAFTING;
k++) {
struct item_data item;
inventory_get_slot(&s->player.inventory, k, &item);
if(item.id != 0) {
inventory_clear_slot(&s->player.inventory, k);
slots_changed[k] = true;
spawn_item(
(vec3) {s->player.x, s->player.y, s->player.z},
&item, true, s);
}
}
for(size_t k = 0; k < INVENTORY_SIZE; k++) {
if(slots_changed[k])
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_INVENTORY_SLOT,
.payload.inventory_slot.window = WINDOWC_INVENTORY,
.payload.inventory_slot.slot = k,
.payload.inventory_slot.item
= s->player.inventory.items[k],
});
}
struct item_data picked_item;
if(inventory_get_picked_item(&s->player.inventory,
&picked_item)) {
inventory_clear_picked_item(&s->player.inventory);
spawn_item((vec3) {s->player.x, s->player.y, s->player.z},
&picked_item, true, s);
clin_rpc_send(&(struct client_rpc) {
.type = CRPC_INVENTORY_SLOT,
.payload.inventory_slot.window = WINDOWC_INVENTORY,
.payload.inventory_slot.slot = SPECIAL_SLOT_PICKED_ITEM,
.payload.inventory_slot.item
= s->player.inventory.picked_item,
});
}
}
break; break;
}
case SRPC_BLOCK_DIG: case SRPC_BLOCK_DIG:
if(s->player.has_pos && call->payload.block_dig.y >= 0 if(s->player.has_pos && call->payload.block_dig.y >= 0
&& call->payload.block_dig.y < WORLD_HEIGHT && call->payload.block_dig.y < WORLD_HEIGHT
@ -328,6 +310,7 @@ static void server_local_process(struct server_rpc* call, void* user) {
level_archive_read(&s->level, LEVEL_TIME, &s->world_time, 0); level_archive_read(&s->level, LEVEL_TIME, &s->world_time, 0);
dict_entity_reset(s->entities); dict_entity_reset(s->entities);
s->player.active_inventory = &s->player.inventory;
clin_rpc_send(&(struct client_rpc) { clin_rpc_send(&(struct client_rpc) {
.type = CRPC_WORLD_RESET, .type = CRPC_WORLD_RESET,
@ -493,7 +476,10 @@ void server_local_create(struct server_local* s) {
s->player.has_pos = false; s->player.has_pos = false;
s->player.finished_loading = false; s->player.finished_loading = false;
string_init(s->level_name); string_init(s->level_name);
inventory_create(&s->player.inventory, INVENTORY_SIZE);
inventory_create(&s->player.inventory, &inventory_logic_player, s,
INVENTORY_SIZE);
s->player.active_inventory = &s->player.inventory;
dict_entity_init(s->entities); dict_entity_init(s->entities);
struct thread t; struct thread t;

View file

@ -42,6 +42,7 @@ struct server_local {
bool has_pos; bool has_pos;
bool finished_loading; bool finished_loading;
struct inventory inventory; struct inventory inventory;
struct inventory* active_inventory;
} player; } player;
struct server_world world; struct server_world world;
dict_entity_t entities; dict_entity_t entities;
@ -51,7 +52,11 @@ struct server_local {
}; };
void server_local_create(struct server_local* s); void server_local_create(struct server_local* s);
struct entity* server_local_spawn_item(vec3 pos, struct item_data* it,
bool throw, struct server_local* s);
void server_local_spawn_block_drops(struct server_local* s, void server_local_spawn_block_drops(struct server_local* s,
struct block_info* blk_info); struct block_info* blk_info);
void server_local_send_inv_changes(set_inv_slot_t changes,
struct inventory* inv, uint8_t window);
#endif #endif