diff --git a/code/foundation/src/core/game.c b/code/foundation/src/core/game.c index e6b922d..bfd4967 100644 --- a/code/foundation/src/core/game.c +++ b/code/foundation/src/core/game.c @@ -273,7 +273,7 @@ static game_world_render_entry* render_queue = NULL; static void game__world_view_render_push_entry(uint64_t key, entity_view * data) { if (!data) return; - + if (data->kind == EKIND_CHUNK) { world_view *view = game_world_view_get_active(); float size = (float)(view->chunk_size * WORLD_BLOCK_SIZE); @@ -290,18 +290,18 @@ static void game__world_view_render_push_entry(uint64_t key, entity_view * data) .y = (data->y*size + offset) + (float)ty*WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2, .cy = (data->y*size + offset) + (float)ty*WORLD_BLOCK_SIZE + WORLD_BLOCK_SIZE/2, }; - + if (!(blocks_get_flags(blk_id) & BLOCK_FLAG_COLLISION)) { entry.cy = ZPL_F32_MIN; } - + zpl_array_append(render_queue, entry); } } } return; } - + game_world_render_entry entry = { .key = key, .data = data, @@ -322,16 +322,16 @@ void game_world_view_render_world(void) { if (!render_queue) { zpl_array_init(render_queue, zpl_heap()); } - + zpl_array_clear(render_queue); - + profile(PROF_RENDER_PUSH_AND_SORT_ENTRIES) { game_world_view_active_entity_map(game__world_view_render_push_entry); zpl_sort_array(render_queue, zpl_array_count(render_queue), zpl_f32_cmp(zpl_offset_of(game_world_render_entry, cy))); } - + game_world_view_active_entity_map(game__world_view_render_ground); - + for (zpl_isize i = 0; i < zpl_array_count(render_queue); i++) { renderer_draw_entry(render_queue[i].key, render_queue[i].data, &render_queue[i]); } diff --git a/code/foundation/src/gui/inventory.c b/code/foundation/src/gui/inventory.c index 379929f..41389bd 100644 --- a/code/foundation/src/gui/inventory.c +++ b/code/foundation/src/gui/inventory.c @@ -1,16 +1,19 @@ +#include "models/crafting.h" + typedef struct { uint8_t selected_item; bool drop_item; - + bool item_is_held; uint8_t held_item_idx; Item held_item; - + bool is_inside; bool storage_action; bool swap; uint8_t swap_from; uint8_t swap_to; + uint16_t craft_item; } inv_keystate; static inv_keystate player_inv = {0}; @@ -21,31 +24,127 @@ bool inv_is_inside = false; bool inv_is_storage_action = false; bool inv_swap_storage = false; +// TODO(zaklaus): +// TODO(zaklaus): MOVE TO COMMON UI MODULE +// TODO(zaklaus): + +typedef struct { + float x, y; +} inv_draw_result; + +static inline +int UIMeasureText(const char *text, int fontSize) { + Vector2 vec = { 0.0f, 0.0f }; + + // Check if default font has been loaded + if (GetFontDefault().texture.id != 0) { + int defaultFontSize = 10; // Default Font chars height in pixel + int new_spacing = fontSize/defaultFontSize; + + vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)new_spacing); + } + + return (int)vec.x; +} + +static inline +void UIDrawText(const char *text, float posX, float posY, int fontSize, Color color) { + // Check if default font has been loaded + if (GetFontDefault().texture.id != 0) { + Vector2 position = { (float)posX , (float)posY }; + + int defaultFontSize = 22; // Default Font chars height in pixel + int new_spacing = fontSize/defaultFontSize; + + DrawTextEx(GetFontDefault(), text, position, (float)fontSize , (float)new_spacing , color); + } +} + + +static inline inv_draw_result +DrawColoredText(float xpos, float ypos, char const *text, Color color) { + ZPL_ASSERT(text); + UIDrawText(text, xpos, ypos, 22, color); + + char const *p = text; + uint8_t newlines = 1; + + do { + if (*p == '\n') + ++newlines; + } while (*p++ != 0); + + return (inv_draw_result){.x = xpos + UIMeasureText(text, 22), .y = ypos + 22*newlines}; +} + + +static inline +inv_draw_result inventory_draw_crafting_btn(float xpos, float ypos, const char *name, uint16_t id, Color color) { + float name_width=0.0f; + char const *text = TextFormat("> %s", name); + name_width = (float)UIMeasureText(text, 22); + + Color new_color = color; + if (is_btn_pressed(xpos, ypos, name_width, 22, &new_color)) { + inv_is_inside = true; + player_inv.craft_item = id; + } + + Color _c_compare_lol = BLACK; + if (!zpl_memcompare(&color, &_c_compare_lol, sizeof(Color))) { + new_color = BLACK; + } + + inv_draw_result res = DrawColoredText(xpos, ypos, text, new_color); + ypos = res.y; + return res; +} + +static inline +bool inventory_draw_crafting_list(float xpos, float ypos) { + // NOTE(zaklaus): collect the list of supported recipes + // TODO(zaklaus): too lazy, draw all recipes everywhere for now + + float start_xpos = xpos; + float start_ypos = ypos; + for (uint16_t i = 0; i < craft_get_num_recipes(); ++i) { + asset_id id = craft_get_recipe_asset(i); + inventory_draw_crafting_btn(start_xpos+1, ypos+1, asset_names[id], id, BLACK); + inv_draw_result entry = inventory_draw_crafting_btn(start_xpos, ypos, asset_names[id], id, RAYWHITE); + ypos = entry.y; + xpos = zpl_max(xpos, entry.x); + } + + return check_mouse_area(start_xpos, start_ypos, xpos-start_xpos, ypos-start_ypos) != DAREA_OUTSIDE; +} + void inventory_draw_panel(entity_view *e, bool is_player, float sx, float sy){ if (!e->has_items && is_player) return; if (!e->has_storage_items && !is_player) return; - + float x = sx; float y = sy; - + const int32_t grid_size = (is_player) ? (64*3) : (64*4); const int32_t inv_size = (is_player) ? ITEMS_INVENTORY_SIZE : ITEMS_CONTAINER_SIZE; const int32_t inv_cols = (is_player) ? 3 : 4; - + inv_keystate *inv = (!is_player) ? &storage_inv : &player_inv; inv_keystate *inv2 = (is_player) ? &storage_inv : &player_inv; - + + bool inside_craft = !is_player && inventory_draw_crafting_list(screenWidth/2.0f - 684, screenHeight/2.0f - 128); + inv->is_inside = check_mouse_area(sx, sy, (float)grid_size, (float)grid_size) != DAREA_OUTSIDE; - inv_is_inside |= inv->is_inside; - + inv_is_inside |= inv->is_inside || inside_craft; + for (int32_t i = 0; i < inv_size; i += 1) { { debug_area_status area = check_mouse_area(x, y, 64, 64); Color color = RAYWHITE; Item *item = (is_player) ? &e->items[i] : &e->storage_items[i]; - + if (area == DAREA_HOVER) { color = YELLOW; } else if (area == DAREA_PRESS && inv2->item_is_held){ @@ -79,9 +178,9 @@ void inventory_draw_panel(entity_view *e, bool is_player, float sx, float sy){ } else if (i == inv->selected_item) { color = RED; } - + DrawRectangleLinesEco(x, y, 64, 64, color); - + if (item->quantity > 0) { Texture2D tex = GetSpriteTexture2D(assets_find(item->kind)); float aspect = tex.width/(float)tex.height; @@ -89,24 +188,24 @@ void inventory_draw_panel(entity_view *e, bool is_player, float sx, float sy){ float ofs_x = (WORLD_BLOCK_SIZE-size)/2.0f; DrawTexturePro(tex, ASSET_SRC_RECT_TEX(tex.width, tex.height), ASSET_DST_RECT_TEX(x+ofs_x, y, size, WORLD_BLOCK_SIZE), (Vector2){0.5f,0.5f}, 0.0f, WHITE); } - + if (item->quantity > 1) { DrawTextEco(zpl_bprintf("%d", item->quantity), x+5, y+5, 16, RAYWHITE, 0.0f); } - + if (item->quantity > 0 && item->durability < 1.0f) { DrawRectangleEco(x, y+56, 64, 8, BLACK); DrawRectangleEco(x, y+56, 64*item->durability, 8, BlendColor(RED, GREEN, item->durability)); } } x += 64; - + if ((i+1) % inv_cols == 0) { x = sx; y += 64; } } - + // NOTE(zaklaus): switch it off if is_player if (is_player) inv_is_storage_action = false; @@ -115,7 +214,7 @@ void inventory_draw_panel(entity_view *e, bool is_player, float sx, float sy){ void inventory_render_held_item(bool is_player){ inv_keystate *inv = (!is_player) ? &storage_inv : &player_inv; inv_keystate *inv2 = (is_player) ? &storage_inv : &player_inv; - + if (inv->item_is_held) { Vector2 mpos = GetMousePosition(); mpos.x -= 32; @@ -126,7 +225,7 @@ void inventory_render_held_item(bool is_player){ float ofs_x = (WORLD_BLOCK_SIZE-size)/2.0f; DrawTexturePro(tex, ASSET_SRC_RECT_TEX(tex.width, tex.height), ASSET_DST_RECT_TEX(mpos.x+ofs_x, mpos.y, size, WORLD_BLOCK_SIZE), (Vector2){0.5f,0.5f}, 0.0f, ColorAlpha(WHITE, 0.8f)); DrawTextEco(zpl_bprintf("%d", inv->held_item.quantity), mpos.x, mpos.y, 16, RAYWHITE, 0.0f); - + if (!inv->is_inside && IsMouseButtonReleased(MOUSE_LEFT_BUTTON) && !inv2->is_inside) { inv->drop_item = true; inv->item_is_held = false; @@ -138,6 +237,7 @@ void inventory_render_held_item(bool is_player){ void inventory_reset_states(inv_keystate *ik) { ik->drop_item = false; ik->swap = false; + ik->craft_item = 0; } void inventory_draw() { @@ -146,22 +246,27 @@ void inventory_draw() { inv_swap_storage = false; inventory_reset_states(&player_inv); inventory_reset_states(&storage_inv); - + camera cam = camera_get(); entity_view *e = game_world_view_active_get_entity(cam.ent_id); if (!e || !e->has_items) return; - + if (IsKeyPressed(KEY_TAB)) { inv_is_open = !inv_is_open; } - + if (!inv_is_open || build_is_in_draw_mode) { return; } - + inventory_draw_panel(e, true, screenWidth/2.0f + 128, screenHeight/2.0f - 96); inventory_draw_panel(e, false, screenWidth/2.0f - 384, screenHeight/2.0f - 128); - + inventory_render_held_item(true); inventory_render_held_item(false); + + // TODO(zaklaus): draw craft list + if (IsKeyPressed(KEY_O)) { + player_inv.craft_item = ASSET_SCREWS; + } } diff --git a/code/foundation/src/models/components.h b/code/foundation/src/models/components.h index 17e5194..9b765b1 100644 --- a/code/foundation/src/models/components.h +++ b/code/foundation/src/models/components.h @@ -63,6 +63,7 @@ typedef struct { uint8_t swap_storage; uint8_t swap_from; uint8_t swap_to; + asset_id craft_item; // NOTE(zaklaus): build mode uint8_t num_placements; diff --git a/code/foundation/src/models/crafting.c b/code/foundation/src/models/crafting.c index 08b539b..004a65a 100644 --- a/code/foundation/src/models/crafting.c +++ b/code/foundation/src/models/crafting.c @@ -51,6 +51,15 @@ bool craft_is_reagent_used_in_producer(asset_id reagent, asset_id producer) { return craft__find_num_recipes_by_reagent(producer, reagent) > 0; } +uint16_t craft_get_num_recipes(void) { + return MAX_RECIPES; +} + +asset_id craft_get_recipe_asset(uint16_t id) { + ZPL_ASSERT(id < MAX_RECIPES); + return recipes[id].product; +} + asset_id craft_perform_recipe(ecs_entity_t *items, asset_id producer, asset_id target, uint32_t *quantity) { ZPL_ASSERT_NOT_NULL(items); @@ -143,12 +152,12 @@ asset_id craft_perform_recipe(ecs_entity_t *items, asset_id producer, asset_id t return 0; } -// TODO(zaklaus): +// TODO(zaklaus): might be removed asset_id craft_has_byproducts(asset_id product) { return 0xFF; } -// TODO(zaklaus): +// TODO(zaklaus): might be removed uint32_t craft_resolve_graph(asset_id product, uint16_t *hops, uint8_t direct_cost) { return 0; } diff --git a/code/foundation/src/models/crafting.h b/code/foundation/src/models/crafting.h index fa48675..8fc86e1 100644 --- a/code/foundation/src/models/crafting.h +++ b/code/foundation/src/models/crafting.h @@ -9,12 +9,18 @@ // TODO(zaklaus): "items" is assumed to come from ItemContainer component. asset_id craft_perform_recipe(ecs_entity_t *items, asset_id producer, asset_id target, 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): utilities +uint16_t craft_get_num_recipes(void); +asset_id craft_get_recipe_asset(uint16_t id); + +//~TODO(zaklaus): not implemented and might get removed + +// NOTE(zaklaus): informs us on whether this product has any byproducts desired. +asset_id craft_has_byproducts(asset_id product); + // 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. diff --git a/code/foundation/src/models/prefabs/craftbench.c b/code/foundation/src/models/prefabs/craftbench.c index af96173..704adc8 100644 --- a/code/foundation/src/models/prefabs/craftbench.c +++ b/code/foundation/src/models/prefabs/craftbench.c @@ -16,8 +16,6 @@ uint64_t craftbench_spawn(void) { producer->energy_level = 69.0f; producer->pending_task = PRODUCER_CRAFT_WAITING; producer->push_filter = PRODUCER_PUSH_NONE; - - ecs_set(world_ecs(), e, ItemRouter, {1}); return (uint64_t)e; } diff --git a/code/foundation/src/packets/pkt_send_keystate.c b/code/foundation/src/packets/pkt_send_keystate.c index c4d2d30..a21f530 100644 --- a/code/foundation/src/packets/pkt_send_keystate.c +++ b/code/foundation/src/packets/pkt_send_keystate.c @@ -25,6 +25,7 @@ pkt_desc pkt_send_keystate_desc[] = { { PKT_UINT(pkt_send_keystate, swap_storage) }, { PKT_UINT(pkt_send_keystate, swap_from) }, { PKT_UINT(pkt_send_keystate, swap_to) }, + { PKT_UINT(pkt_send_keystate, craft_item) }, { PKT_UINT(pkt_send_keystate, placement_num) }, { PKT_UINT(pkt_send_keystate, deletion_mode) }, { PKT_ARRAY(pkt_send_keystate, placements) }, @@ -73,9 +74,9 @@ int32_t pkt_send_keystate_handler(pkt_header *header) { i->swap_storage |= table.swap_storage; i->swap_from = zpl_clamp(table.swap_from, 0, ITEMS_CONTAINER_SIZE-1); i->swap_to = zpl_clamp(table.swap_to, 0, ITEMS_CONTAINER_SIZE-1); + i->craft_item = table.craft_item; i->storage_action = table.storage_action; i->deletion_mode = table.deletion_mode; - if (table.placement_num > 0) { i->num_placements = zpl_clamp(table.placement_num, 0, BUILD_MAX_PLACEMENTS); for (uint8_t j = 0; j < i->num_placements; j++) { diff --git a/code/foundation/src/packets/pkt_send_keystate.h b/code/foundation/src/packets/pkt_send_keystate.h index aeccc9c..2f24063 100644 --- a/code/foundation/src/packets/pkt_send_keystate.h +++ b/code/foundation/src/packets/pkt_send_keystate.h @@ -22,6 +22,7 @@ typedef struct { uint8_t swap_storage; uint8_t swap_from; uint8_t swap_to; + uint16_t craft_item; // TODO(zaklaus): build mode uint8_t placement_num; diff --git a/code/foundation/src/platform/arch.h b/code/foundation/src/platform/arch.h index 0330ac0..184de4c 100644 --- a/code/foundation/src/platform/arch.h +++ b/code/foundation/src/platform/arch.h @@ -141,6 +141,7 @@ void platform_input_update_input_frame(game_keystate_data data) { if (data.swap != last_input_data.swap) goto send_data; if (data.swap_from != last_input_data.swap_from) goto send_data; if (data.swap_to != last_input_data.swap_to) goto send_data; + if (data.craft_item != last_input_data.craft_item) goto send_data; if (data.placement_num != last_input_data.placement_num) goto send_data; if (data.deletion_mode != last_input_data.deletion_mode) goto send_data; if (zpl_memcompare(data.placements, last_input_data.placements, zpl_size_of(data.placements))) goto send_data; diff --git a/code/foundation/src/systems/modules/system_items.c b/code/foundation/src/systems/modules/system_items.c index b96de1f..a3a789b 100644 --- a/code/foundation/src/systems/modules/system_items.c +++ b/code/foundation/src/systems/modules/system_items.c @@ -56,6 +56,25 @@ void PickItem(ecs_iter_t *it) { } } +void CraftItem(ecs_iter_t *it) { + Input *in = ecs_field(it, Input, 1); + + for (int i = 0; i < it->count; i++) { + if (in[i].craft_item == 0) continue; + zpl_printf("id: %d\n", in[i].craft_item); + if (world_entity_valid(in[i].storage_ent)){ + Producer *ic = 0; + if ((ic = ecs_get_mut_if_ex(it->world, in[i].storage_ent, Producer))){ + ic->target_item = in[i].craft_item; + if (ic->pending_task == PRODUCER_CRAFT_WAITING) { + ic->pending_task = PRODUCER_CRAFT_ENQUEUED; + } + in[i].craft_item = 0; + } + } + } +} + void DropItem(ecs_iter_t *it) { Input *in = ecs_field(it, Input, 1); Position *p = ecs_field(it, Position, 2); diff --git a/code/foundation/src/systems/systems.c b/code/foundation/src/systems/systems.c index ae1f9ce..cf31927 100644 --- a/code/foundation/src/systems/systems.c +++ b/code/foundation/src/systems/systems.c @@ -263,6 +263,7 @@ void SystemsImport(ecs_world_t *ecs) { ECS_SYSTEM(ecs, SwapItems, EcsPostUpdate, components.Input, components.Inventory); //ECS_SYSTEM(ecs, MergeItems, EcsPostUpdate, components.Position, components.ItemDrop); ECS_SYSTEM(ecs, UseItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle); + ECS_SYSTEM(ecs, CraftItem, EcsPostUpdate, components.Input, !components.IsInVehicle); ECS_SYSTEM(ecs, InspectContainers, EcsPostUpdate, components.Input, !components.IsInVehicle); ECS_SYSTEM_TICKED(ecs, HarvestIntoContainers, EcsPostUpdate, components.ItemContainer, components.Position, !components.BlockHarvest); diff --git a/code/games/sandbox/src/platform.c b/code/games/sandbox/src/platform.c index bdade5b..d406e0d 100644 --- a/code/games/sandbox/src/platform.c +++ b/code/games/sandbox/src/platform.c @@ -27,7 +27,7 @@ void platform_init() { platform_create_window("eco2d"); renderer_init(); - + target_zoom = 2.70f; } @@ -52,11 +52,11 @@ void platform_shutdown() { void platform_input() { float mouse_z = (GetMouseWheelMove()*0.5f); float mouse_modified = target_zoom < 4 ? mouse_z / (zpl_exp(4 - (target_zoom))) : mouse_z; - + if (mouse_z != 0.0f) { target_zoom = zpl_clamp(target_zoom + mouse_modified, 0.1f, 11.0f); } - + // NOTE(zaklaus): keystate handling { float x=0.0f, y=0.0f; @@ -65,12 +65,12 @@ void platform_input() { if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f; if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f; if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f; - + use = IsKeyPressed(KEY_SPACE); sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT); ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL); drop = IsKeyPressed(KEY_G) || player_inv.drop_item || storage_inv.drop_item; - + // NOTE(zaklaus): NEW! mouse movement Vector2 mouse_pos = GetMousePosition(); mouse_pos.x /= screenWidth; @@ -78,18 +78,18 @@ void platform_input() { mouse_pos.x -= 0.5f; mouse_pos.y -= 0.5f; mouse_pos = Vector2Normalize(mouse_pos); - + if (game_get_kind() == GAMEKIND_SINGLE && IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) { x = mouse_pos.x; y = -mouse_pos.y; } - + inv_keystate *inv = (inv_is_storage_action) ? &storage_inv : &player_inv; inv_keystate *inv2 = (!inv_is_storage_action) ? &storage_inv : &player_inv; - + // NOTE(zaklaus): don't perform picking if we manipulate our inventories pick = (inv_is_inside||inv->item_is_held||inv2->item_is_held) ? false : IsMouseButtonDown(MOUSE_LEFT_BUTTON); - + game_keystate_data in_data = { .x = x, .y = y, @@ -99,7 +99,7 @@ void platform_input() { .sprint = sprint, .ctrl = ctrl, .pick = pick, - + .drop = drop, .storage_action = inv_is_storage_action, .selected_item = player_inv.selected_item, @@ -108,18 +108,19 @@ void platform_input() { .swap_storage = inv_swap_storage, .swap_from = inv->swap_from, .swap_to = inv->swap_to, - + .craft_item = inv->craft_item, + .deletion_mode = build_is_deletion_mode, }; - + if (build_submit_placements) { in_data.placement_num = build_num_placements; zpl_memcopy(in_data.placements, build_placements, build_num_placements*zpl_size_of(item_placement)); } - + platform_input_update_input_frame(in_data); } - + // NOTE(zaklaus): cycle through viewers { if (IsKeyPressed(KEY_Q)) { @@ -129,7 +130,7 @@ void platform_input() { game_world_view_cycle_active(1); } } - + // NOTE(zaklaus): toggle debug drawing #ifndef ECO2D_PROD { @@ -146,13 +147,13 @@ void draw_selected_item() { if (oe) { // NOTE(zaklaus): sel item entity_view *e = game_world_view_active_get_entity(oe->sel_ent); - + if (e && e->kind == EKIND_DEVICE) { renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.4f)); }else{ // NOTE(zaklaus): hover item entity_view *e = game_world_view_active_get_entity(oe->pick_ent); - + if (e && e->kind == EKIND_DEVICE) { renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.1f)); } @@ -162,14 +163,14 @@ void draw_selected_item() { void platform_render() { platform_resize_window(); - + profile(PROF_ENTITY_LERP) { game_world_view_active_entity_map(lerp_entity_positions); game_world_view_active_entity_map(do_entity_fadeinout); } - + assets_frame(); - + BeginDrawing(); { profile (PROF_RENDER) { @@ -186,7 +187,7 @@ void platform_render() { debug_draw(); } EndDrawing(); - + if (request_shutdown) { CloseWindow(); }