mirror of
https://github.com/xtreme8000/CavEX.git
synced 2025-01-22 09:11:55 -05:00
Local server inventory logic (4x4 crafting)
not the prettiest solution, but it works for now
This commit is contained in:
parent
1064b5e173
commit
beee78a084
15 changed files with 571 additions and 171 deletions
|
@ -97,6 +97,7 @@ add_executable(cavex
|
|||
source/item/inventory.c
|
||||
source/item/items.c
|
||||
source/item/tool.c
|
||||
source/item/recipe.c
|
||||
source/item/items/item_sugarcane.c
|
||||
|
||||
source/network/client_interface.c
|
||||
|
@ -105,6 +106,7 @@ add_executable(cavex
|
|||
source/network/server_interface.c
|
||||
source/network/server_local.c
|
||||
source/network/server_world.c
|
||||
source/network/inventory_player.c
|
||||
|
||||
source/graphics/gfx_util.c
|
||||
source/graphics/gui_util.c
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
* main menu
|
||||
* generation of new chunks
|
||||
* biome colors
|
||||
* player physics
|
||||
* inventory management
|
||||
* ~~player physics~~
|
||||
* ~~inventory management~~
|
||||
* ~~block placement~~ and destruction logic
|
||||
* (random) block updates
|
||||
* item actions
|
||||
* ~~(random)~~ block updates
|
||||
* ~~item actions~~
|
||||
* real texture pack support
|
||||
* Beta 1.7.3 multiplayer support
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ enum block_type {
|
|||
BLOCK_SLAB = 44,
|
||||
BLOCK_MOSSY_COBBLE = 48,
|
||||
BLOCK_OBSIDIAN = 49,
|
||||
BLOCK_WORKBENCH = 58,
|
||||
BLOCK_LADDER = 65,
|
||||
BLOCK_REDSTONE_TORCH = 75,
|
||||
BLOCK_SNOW = 78,
|
||||
|
|
|
@ -94,22 +94,11 @@ static bool entity_server_tick(struct entity* e, struct server_local* s) {
|
|||
e->pos,
|
||||
(vec3) {s->player.x, s->player.y - 0.6F, s->player.z})
|
||||
< 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
|
||||
inventory_collect_inventory(&s->player.inventory, &e->data.item.item,
|
||||
slots_changed);
|
||||
|
||||
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],
|
||||
});
|
||||
}
|
||||
if(s->player.active_inventory && s->player.active_inventory->logic
|
||||
&& s->player.active_inventory->logic->on_collect)
|
||||
s->player.active_inventory->logic->on_collect(
|
||||
s->player.active_inventory, &e->data.item.item);
|
||||
|
||||
clin_rpc_send(&(struct client_rpc) {
|
||||
.type = CRPC_PICKUP_ITEM,
|
||||
|
|
|
@ -21,9 +21,12 @@
|
|||
|
||||
#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);
|
||||
|
||||
inv->user = user;
|
||||
inv->logic = logic;
|
||||
inv->capacity = 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);
|
||||
|
||||
if(inv->logic && inv->logic->on_create)
|
||||
inv->logic->on_create(inv);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -50,6 +56,10 @@ void inventory_copy(struct inventory* inv, struct inventory* from) {
|
|||
|
||||
void inventory_destroy(struct inventory* inv) {
|
||||
assert(inv && inv->items);
|
||||
|
||||
if(inv->logic && inv->logic->on_destroy)
|
||||
inv->logic->on_destroy(inv);
|
||||
|
||||
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) {
|
||||
assert(inv);
|
||||
return inv->hotbar_slot;
|
||||
|
@ -286,15 +230,30 @@ static bool inventory_place_one_item(struct inventory* inv, size_t slot) {
|
|||
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);
|
||||
|
||||
if(right) {
|
||||
return (inv->picked_item.id == 0) ?
|
||||
inventory_pick_item_split(inv, slot) :
|
||||
inventory_place_one_item(inv, slot);
|
||||
} else {
|
||||
return (inv->picked_item.id == 0) ? inventory_pick_item(inv, slot) :
|
||||
inventory_place_item(inv, slot);
|
||||
bool result = true;
|
||||
|
||||
if(inv->logic && inv->logic->pre_action
|
||||
&& !inv->logic->pre_action(inv, slot, right, changes))
|
||||
result = false;
|
||||
|
||||
if(result) {
|
||||
if(right) {
|
||||
result = (inv->picked_item.id == 0) ?
|
||||
inventory_pick_item_split(inv, slot) :
|
||||
inventory_place_one_item(inv, slot);
|
||||
} else {
|
||||
result = (inv->picked_item.id == 0) ?
|
||||
inventory_pick_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;
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
#ifndef INVENTORY_H
|
||||
#define INVENTORY_H
|
||||
|
||||
#include <m-lib/m-dict.h>
|
||||
#include <m-lib/m-i-list.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
@ -40,6 +41,10 @@
|
|||
|
||||
#define SPECIAL_SLOT_PICKED_ITEM 255
|
||||
|
||||
DICT_SET_DEF(set_inv_slot, size_t)
|
||||
|
||||
struct inventory_logic;
|
||||
|
||||
struct inventory {
|
||||
struct item_data picked_item;
|
||||
struct item_data* items;
|
||||
|
@ -50,20 +55,30 @@ struct inventory {
|
|||
bool action_type;
|
||||
size_t action_slot;
|
||||
} revision; // for window container
|
||||
struct inventory_logic* logic;
|
||||
void* user;
|
||||
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)
|
||||
|
||||
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_destroy(struct inventory* inv);
|
||||
void inventory_clear(struct inventory* inv);
|
||||
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);
|
||||
void inventory_set_hotbar(struct inventory* inv, size_t slot);
|
||||
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_set_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
|
||||
|
|
|
@ -34,6 +34,8 @@ struct item_data {
|
|||
enum item_type {
|
||||
ITEM_COAL = 263,
|
||||
ITEM_DIAMOND = 264,
|
||||
ITEM_DIAMOND_PICKAXE = 278,
|
||||
ITEM_STICK = 280,
|
||||
ITEM_SEED = 295,
|
||||
ITEM_WHEAT = 296,
|
||||
ITEM_BREAD = 297,
|
||||
|
@ -52,10 +54,10 @@ enum item_type {
|
|||
struct server_local;
|
||||
|
||||
enum armor_type {
|
||||
ARMOR_TYPE_HELMET,
|
||||
ARMOR_TYPE_CHESTPLATE,
|
||||
ARMOR_TYPE_LEGGINGS,
|
||||
ARMOR_TYPE_BOOTS,
|
||||
ARMOR_TYPE_HELMET = 0,
|
||||
ARMOR_TYPE_CHESTPLATE = 1,
|
||||
ARMOR_TYPE_LEGGINGS = 2,
|
||||
ARMOR_TYPE_BOOTS = 3,
|
||||
};
|
||||
|
||||
enum armor_tier {
|
||||
|
|
147
source/item/recipe.c
Normal file
147
source/item/recipe.c
Normal 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
49
source/item/recipe.h
Normal 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
|
|
@ -68,7 +68,7 @@ bool windowc_new_action(struct window_container* wc, uint16_t* action_id,
|
|||
if(!next)
|
||||
return false;
|
||||
|
||||
if(!inventory_create(next, INVENTORY_SIZE)) // TODO
|
||||
if(!inventory_create(next, NULL, NULL, INVENTORY_SIZE)) // TODO
|
||||
return false;
|
||||
|
||||
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_slot = action_slot;
|
||||
|
||||
inventory_action(next, action_slot, action_type);
|
||||
inventory_action(next, action_slot, action_type, NULL);
|
||||
|
||||
*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);
|
||||
inventory_copy(current, prev);
|
||||
inventory_action(current, current->revision.action_slot,
|
||||
current->revision.action_type);
|
||||
current->revision.action_type, NULL);
|
||||
|
||||
prev = current;
|
||||
ilist_inventory_next(it);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "game/gui/screen.h"
|
||||
#include "graphics/gfx_util.h"
|
||||
#include "graphics/gui_util.h"
|
||||
#include "item/recipe.h"
|
||||
#include "network/client_interface.h"
|
||||
#include "network/server_interface.h"
|
||||
#include "network/server_local.h"
|
||||
|
@ -70,6 +71,7 @@ int main(void) {
|
|||
input_init();
|
||||
blocks_init();
|
||||
items_init();
|
||||
recipe_init();
|
||||
gfx_setup();
|
||||
|
||||
screen_set(&screen_select_world);
|
||||
|
|
22
source/network/inventory_logic.h
Normal file
22
source/network/inventory_logic.h
Normal 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;
|
220
source/network/inventory_player.c
Normal file
220
source/network/inventory_player.c
Normal 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,
|
||||
};
|
|
@ -26,14 +26,15 @@
|
|||
#include "../item/window_container.h"
|
||||
#include "../platform/thread.h"
|
||||
#include "client_interface.h"
|
||||
#include "inventory_logic.h"
|
||||
#include "server_interface.h"
|
||||
#include "server_local.h"
|
||||
|
||||
#define CHUNK_DIST2(x1, x2, z1, z2) \
|
||||
(((x1) - (x2)) * ((x1) - (x2)) + ((z1) - (z2)) * ((z1) - (z2)))
|
||||
|
||||
static struct entity* spawn_item(vec3 pos, struct item_data* it, bool throw,
|
||||
struct server_local* s) {
|
||||
struct entity* server_local_spawn_item(vec3 pos, struct item_data* it,
|
||||
bool throw, struct server_local* s) {
|
||||
uint32_t entity_id = entity_gen_id(s->entities);
|
||||
struct entity* e = dict_entity_safe_get(s->entities, entity_id);
|
||||
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);
|
||||
|
||||
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},
|
||||
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) {
|
||||
assert(call && user);
|
||||
|
||||
|
@ -111,15 +136,12 @@ static void server_local_process(struct server_rpc* call, void* user) {
|
|||
call->payload.hotbar_slot.slot);
|
||||
break;
|
||||
case SRPC_WINDOW_CLICK: {
|
||||
bool accept = false;
|
||||
if(call->payload.window_click.window == WINDOWC_INVENTORY) {
|
||||
if(!(inventory_get_picked_item(&s->player.inventory, NULL)
|
||||
&& call->payload.window_click.slot
|
||||
== INVENTORY_SLOT_OUTPUT))
|
||||
accept = inventory_action(
|
||||
&s->player.inventory, call->payload.window_click.slot,
|
||||
call->payload.window_click.right_click);
|
||||
}
|
||||
set_inv_slot_t changes;
|
||||
set_inv_slot_init(changes);
|
||||
|
||||
bool accept = inventory_action(
|
||||
s->player.active_inventory, call->payload.window_click.slot,
|
||||
call->payload.window_click.right_click, changes);
|
||||
|
||||
clin_rpc_send(&(struct client_rpc) {
|
||||
.type = CRPC_WINDOW_TRANSACTION,
|
||||
|
@ -129,60 +151,20 @@ static void server_local_process(struct server_rpc* call, void* user) {
|
|||
.payload.window_transaction.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;
|
||||
}
|
||||
case SRPC_WINDOW_CLOSE:
|
||||
if(call->payload.window_click.window == WINDOWC_INVENTORY) {
|
||||
bool slots_changed[INVENTORY_SIZE];
|
||||
memset(slots_changed, false, sizeof(slots_changed));
|
||||
|
||||
inventory_clear_slot(&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,
|
||||
});
|
||||
}
|
||||
}
|
||||
case SRPC_WINDOW_CLOSE: {
|
||||
if(s->player.active_inventory && s->player.active_inventory->logic
|
||||
&& s->player.active_inventory->logic->on_close)
|
||||
s->player.active_inventory->logic->on_close(
|
||||
s->player.active_inventory);
|
||||
s->player.active_inventory = &s->player.inventory;
|
||||
break;
|
||||
}
|
||||
case SRPC_BLOCK_DIG:
|
||||
if(s->player.has_pos && call->payload.block_dig.y >= 0
|
||||
&& 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);
|
||||
dict_entity_reset(s->entities);
|
||||
s->player.active_inventory = &s->player.inventory;
|
||||
|
||||
clin_rpc_send(&(struct client_rpc) {
|
||||
.type = CRPC_WORLD_RESET,
|
||||
|
@ -493,7 +476,10 @@ void server_local_create(struct server_local* s) {
|
|||
s->player.has_pos = false;
|
||||
s->player.finished_loading = false;
|
||||
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);
|
||||
|
||||
struct thread t;
|
||||
|
|
|
@ -42,6 +42,7 @@ struct server_local {
|
|||
bool has_pos;
|
||||
bool finished_loading;
|
||||
struct inventory inventory;
|
||||
struct inventory* active_inventory;
|
||||
} player;
|
||||
struct server_world world;
|
||||
dict_entity_t entities;
|
||||
|
@ -51,7 +52,11 @@ struct server_local {
|
|||
};
|
||||
|
||||
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,
|
||||
struct block_info* blk_info);
|
||||
void server_local_send_inv_changes(set_inv_slot_t changes,
|
||||
struct inventory* inv, uint8_t window);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue