diff --git a/CMakeLists.txt b/CMakeLists.txt index 8970334..9948617 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ set(BUILD_TESTS OFF) project( LamasTinyHUD - VERSION 1.0.0.3 + VERSION 1.0.0.4 LANGUAGES CXX ) diff --git a/cmake/sourcelist.cmake b/cmake/sourcelist.cmake index 9382c07..df6a6a8 100644 --- a/cmake/sourcelist.cmake +++ b/cmake/sourcelist.cmake @@ -2,6 +2,8 @@ set(sources ${sources} src/PCH.h src/equip/equip_slot.cpp src/equip/equip_slot.h + src/event/inventory_event.cpp + src/event/inventory_event.h src/event/key_manager.cpp src/event/key_manager.h src/event/sink_event.cpp diff --git a/mcm/Config/LamasTinyHUD/config.json b/mcm/Config/LamasTinyHUD/config.json index 625c032..794208c 100644 --- a/mcm/Config/LamasTinyHUD/config.json +++ b/mcm/Config/LamasTinyHUD/config.json @@ -1090,6 +1090,32 @@ "formatString": "{2}", "sourceType": "ModSettingFloat" } + }, + { + "id": "fSlotCountTextOffset:HudSetting", + "text": "$LamasTinyHUD_HudSetting_SlotCountTextOffset_OptionText", + "type": "slider", + "help": "$LamasTinyHUD_HudSetting_SlotCountTextOffset_InfoText", + "valueOptions": { + "min": 0.0, + "max": 300.0, + "step": 1.0, + "formatString": "{0}", + "sourceType": "ModSettingFloat" + } + }, + { + "id": "fSlotCountTextFontSize:HudSetting", + "text": "$LamasTinyHUD_HudSetting_SlotCountTextFontSize_OptionText", + "type": "slider", + "help": "$LamasTinyHUD_HudSetting_SlotCountTextFontSize_InfoText", + "valueOptions": { + "min": 10.0, + "max": 35.0, + "step": 1.0, + "formatString": "{0}", + "sourceType": "ModSettingFloat" + } } ] } diff --git a/mcm/Config/LamasTinyHUD/settings.ini b/mcm/Config/LamasTinyHUD/settings.ini index 03d9d21..366857b 100644 --- a/mcm/Config/LamasTinyHUD/settings.ini +++ b/mcm/Config/LamasTinyHUD/settings.ini @@ -70,4 +70,6 @@ fKeyIconScaleHeight = 0.400000 uIconOpacity = 125 fIconScaleWidth = 0.110000 fIconScaleHeight = 0.110000 -uSlotButtonFeedback = 200 \ No newline at end of file +uSlotButtonFeedback = 200 +fSlotCountTextOffset = 10 +fSlotCountTextFontSize = 20 \ No newline at end of file diff --git a/mcm/Interface/Translations/LamasTinyHUD_english.txt b/mcm/Interface/Translations/LamasTinyHUD_english.txt index 64e91a6..6563ccd 100644 Binary files a/mcm/Interface/Translations/LamasTinyHUD_english.txt and b/mcm/Interface/Translations/LamasTinyHUD_english.txt differ diff --git a/src/event/inventory_event.cpp b/src/event/inventory_event.cpp new file mode 100644 index 0000000..8b0ab1b --- /dev/null +++ b/src/event/inventory_event.cpp @@ -0,0 +1,50 @@ +#include "inventory_event.h" + +#include "handle/page_handle.h" + +namespace event { + inventory_event* inventory_event::get_singleton() { + static inventory_event singleton; + return std::addressof(singleton); + } + + void inventory_event::sink() { + if (const auto event = RE::Inventory::GetEventSource(); event) { + event->AddEventSink(get_singleton()); + logger::info("Registered {} handler"sv, typeid(RE::Inventory::Event).name()); + } + } + + inventory_event::event_result inventory_event::ProcessEvent(const RE::Inventory::Event* a_event, + RE::BSTEventSource*) { + if (!a_event) { + return event_result::kContinue; + } + + if (!a_event->objRefr->IsPlayer() && !a_event->objRefr->IsPlayerRef()) { + return event_result::kContinue; + } + + //TODO i might use something different, as far as testing goes, removing it does not notify + //TODO use something else + //those should include food and potions + if (a_event->entryData && a_event->entryData->object->IsMagicItem()) { + const auto page_handle = handle::page_handle::get_singleton(); + for (auto pages = page_handle->get_page(); auto [position, page] : pages) { + for (const auto setting : page->slot_settings) { + if (setting->type == util::selection_type::item && setting->form->formID == a_event->entryData-> + object->GetFormID()) { + const auto diff = a_event->newCount - a_event->prevCount; + setting->item_count = setting->item_count + diff; + logger::trace("Name {}, old {}, new {}"sv, + a_event->entryData->GetDisplayName(), + a_event->prevCount, + a_event->newCount); + } + } + } + } + + return event_result::kContinue; + } +} diff --git a/src/event/inventory_event.h b/src/event/inventory_event.h new file mode 100644 index 0000000..a75fee8 --- /dev/null +++ b/src/event/inventory_event.h @@ -0,0 +1,25 @@ +#pragma once + +namespace event { + class inventory_event final : public RE::BSTEventSink { + public: + using event_result = RE::BSEventNotifyControl; + + [[nodiscard]] static inventory_event* get_singleton(); + + static void sink(); + + inventory_event(const inventory_event&) = delete; + inventory_event(inventory_event&&) = delete; + + inventory_event& operator=(const inventory_event&) = delete; + inventory_event& operator=(inventory_event&&) = delete; + + protected: + event_result ProcessEvent(const RE::Inventory::Event* a_event, RE::BSTEventSource*) override; + + private: + inventory_event() = default; + ~inventory_event() override = default; + }; +} diff --git a/src/event/key_manager.cpp b/src/event/key_manager.cpp index 0a9b8a6..15ae05d 100644 --- a/src/event/key_manager.cpp +++ b/src/event/key_manager.cpp @@ -101,7 +101,7 @@ namespace event { page_setting->fade_setting->action = handle::fade_setting::action::out; page_setting->fade_setting->alpha = handle::fade_setting::alpha::min; page_setting->fade_setting->current_alpha = static_cast(handle::fade_setting::alpha::max); - logger::trace("done settinging fade for position {}"sv, static_cast(page_setting->pos)); + logger::trace("done setting fade for position {}"sv, static_cast(page_setting->pos)); }*/ if (button->IsDown() && (key_ == key_top_action_ || key_ == key_right_action_ || key_ == key_bottom_action_ diff --git a/src/event/sink_event.cpp b/src/event/sink_event.cpp index 20afab9..b414a52 100644 --- a/src/event/sink_event.cpp +++ b/src/event/sink_event.cpp @@ -1,9 +1,12 @@ #include "sink_event.h" + +#include "inventory_event.h" #include "key_manager.h" namespace event { void sink_events() { key_manager::sink(); + inventory_event::sink(); logger::info("added all sinks."); } diff --git a/src/handle/page/slot_setting.h b/src/handle/page/slot_setting.h index 54e88fd..3e557c8 100644 --- a/src/handle/page/slot_setting.h +++ b/src/handle/page/slot_setting.h @@ -14,5 +14,6 @@ namespace handle { acton_type action = acton_type::default_action; hand_equip equip = hand_equip::total; RE::BGSEquipSlot* equip_slot = nullptr; + uint32_t item_count = 0; }; } diff --git a/src/handle/page_handle.cpp b/src/handle/page_handle.cpp index a7dab0c..6c13b44 100644 --- a/src/handle/page_handle.cpp +++ b/src/handle/page_handle.cpp @@ -1,5 +1,6 @@ #include "page_handle.h" #include "equip/equip_slot.h" +#include "item/inventory.h" #include "util/string_util.h" namespace handle { @@ -51,6 +52,7 @@ namespace handle { slot->equip = a_hand; RE::BGSEquipSlot* equip_slot = nullptr; get_equip_slots(element->type, a_hand, equip_slot, element->left); + get_item_count(element->form, slot->item_count, element->type); slot->equip_slot = equip_slot; slots->push_back(slot); @@ -73,7 +75,7 @@ namespace handle { page->offset_setting = offset; - //TODO for now + //TODO for now the right hand or the first setting defines the icon page->icon_type = get_icon_type(slots->front()->type, slots->front()->form); page->icon_opacity = a_opacity; @@ -203,7 +205,7 @@ namespace handle { if (actor_value == RE::ActorValue::kNone) { actor_value = effect->data.primaryAV; } - + switch (actor_value) { case RE::ActorValue::kAlteration: case RE::ActorValue::kConjuration: @@ -263,4 +265,20 @@ namespace handle { a_icon = ui::icon_image_type::potion_default; } } + + void page_handle::get_item_count(RE::TESForm*& a_form, uint32_t& a_count, const util::selection_type a_type) { + if (a_type != util::selection_type::item) { + a_count = 0; + return; + } + auto player = RE::PlayerCharacter::GetSingleton(); + for (auto potential_items = item::inventory::get_inventory_magic_items(player); + const auto& [item, invData] : potential_items) { + if (invData.second->object->formID == a_form->formID) { + a_count = invData.first; + break; + } + } + logger::trace("Item {}, count {}"sv, a_form->GetName(), a_count); + } } diff --git a/src/handle/page_handle.h b/src/handle/page_handle.h index 308c0ce..ea92cc4 100644 --- a/src/handle/page_handle.h +++ b/src/handle/page_handle.h @@ -45,6 +45,7 @@ namespace handle { static void get_icon_for_weapon_type(RE::TESForm*& a_form, ui::icon_image_type& a_icon); static void get_icon_for_spell(RE::TESForm*& a_form, ui::icon_image_type& a_icon); static void get_icon_for_item(RE::TESForm*& a_form, ui::icon_image_type& a_icon); + static void get_item_count(RE::TESForm*& a_form, uint32_t& a_count, util::selection_type a_type); struct page_handle_data { std::map page_settings; diff --git a/src/handle/setting_execute.cpp b/src/handle/setting_execute.cpp index e5ed553..8a25a36 100644 --- a/src/handle/setting_execute.cpp +++ b/src/handle/setting_execute.cpp @@ -15,7 +15,7 @@ namespace handle { void setting_execute::execute_settings(const std::vector& a_slots) { logger::trace("got {} settings execute"sv, a_slots.size()); auto player = RE::PlayerCharacter::GetSingleton(); - for (const auto slot : a_slots) { + for (auto slot : a_slots) { logger::trace("executing setting for type {}, action {}, form {} ..."sv, static_cast(slot->type), static_cast(slot->action), @@ -57,13 +57,13 @@ namespace handle { } } - void setting_execute::execute_setting(const slot_setting* a_slot, RE::PlayerCharacter*& a_player) { + void setting_execute::execute_setting(slot_setting*& a_slot, RE::PlayerCharacter*& a_player) { switch (a_slot->type) { case util::selection_type::unset: logger::warn("nothing to do, nothing set"sv); break; case util::selection_type::item: - item::potion::consume_potion(a_slot->form, a_player); + item::potion::consume_potion(a_slot, a_player); break; case util::selection_type::magic: magic::spell::cast_magic(a_slot->form, a_slot->action, a_slot->equip_slot, a_player); diff --git a/src/handle/setting_execute.h b/src/handle/setting_execute.h index 9dd2674..1c8c723 100644 --- a/src/handle/setting_execute.h +++ b/src/handle/setting_execute.h @@ -12,6 +12,6 @@ namespace handle { RE::ActorEquipManager*& a_actor_equip_manager); private: - static void execute_setting(const slot_setting* a_slot, RE::PlayerCharacter*& a_player); + static void execute_setting(slot_setting*& a_slot, RE::PlayerCharacter*& a_player); }; } diff --git a/src/item/potion.cpp b/src/item/potion.cpp index 6acffc6..3a8e6c6 100644 --- a/src/item/potion.cpp +++ b/src/item/potion.cpp @@ -2,15 +2,15 @@ #include "inventory.h" namespace item { - void potion::consume_potion(const RE::TESForm* a_form, RE::PlayerCharacter*& a_player) { - logger::trace("try to consume {}"sv, a_form->GetName()); + void potion::consume_potion(handle::slot_setting*& a_slot, RE::PlayerCharacter*& a_player) { + logger::trace("try to consume {}"sv, a_slot->form->GetName()); RE::TESBoundObject* obj = nullptr; RE::InventoryEntryData inv_data; uint32_t left; for (auto potential_items = inventory::get_inventory_magic_items(a_player); const auto& [item, invData] : potential_items) { - if (invData.second->object->formID == a_form->formID) { + if (invData.second->object->formID == a_slot->form->formID) { obj = item; inv_data = *invData.second; left = invData.first; @@ -27,9 +27,10 @@ namespace item { if (obj->As()->IsFood()) { - logger::trace("trying to equip/eat a food item {}"sv, obj->GetName()); + logger::trace("trying to equip/eat a food item {}, count left {}"sv, obj->GetName(), left); const auto equip_manager = RE::ActorEquipManager::GetSingleton(); equip_manager->EquipObject(a_player, obj); + a_slot->item_count = left -1; logger::trace("equipped/ate a food item {}"sv, obj->GetName()); return; } @@ -42,6 +43,7 @@ namespace item { //build a "cache" with formid and count, validate after consumption a_player->DrinkPotion(alchemy_potion, inv_data.extraLists->front()); + a_slot->item_count = left -1; logger::trace("drank potion {}. return."sv, alchemy_potion->GetName()); } } diff --git a/src/item/potion.h b/src/item/potion.h index b8ea90c..d5034c4 100644 --- a/src/item/potion.h +++ b/src/item/potion.h @@ -1,8 +1,9 @@ #pragma once +#include "handle/page/slot_setting.h" namespace item { class potion { public: - static void consume_potion(const RE::TESForm* a_form, RE::PlayerCharacter*& a_player); + static void consume_potion(handle::slot_setting*& a_slot, RE::PlayerCharacter*& a_player); }; } diff --git a/src/setting/mcm_setting.cpp b/src/setting/mcm_setting.cpp index 2075542..bf2926c 100644 --- a/src/setting/mcm_setting.cpp +++ b/src/setting/mcm_setting.cpp @@ -48,14 +48,14 @@ namespace config { static float hud_image_position_height; static float hud_slot_position_offset; static float hud_key_position_offset; - static float icon_scale_width; static float icon_scale_height; static uint32_t icon_opacity; static uint32_t slot_button_feedback; - static float key_icon_scale_width; static float key_icon_scale_height; + static float slot_count_text_offset; + static float slot_count_text_font_size; void mcm_setting::read_setting() { logger::info("reading mcm ini files"); @@ -138,6 +138,10 @@ namespace config { slot_button_feedback = static_cast(mcm.GetLongValue("HudSetting", "uSlotButtonFeedback", 200)); key_icon_scale_width = static_cast(mcm.GetDoubleValue("HudSetting", "fKeyIconScaleWidth", 0.4)); key_icon_scale_height = static_cast(mcm.GetDoubleValue("HudSetting", "fKeyIconScaleHeight", 0.4)); + slot_count_text_offset = static_cast(mcm.GetDoubleValue("HudSetting", "fSlotCountTextOffset", 10)); + slot_count_text_font_size = static_cast(mcm.GetDoubleValue("HudSetting", + "fSlotCountTextFontSize", + 20)); }; read_mcm(mcm_default_setting); @@ -196,4 +200,6 @@ namespace config { uint32_t mcm_setting::get_slot_button_feedback() { return slot_button_feedback; } float mcm_setting::get_key_icon_scale_width() { return key_icon_scale_width; } float mcm_setting::get_key_icon_scale_height() { return key_icon_scale_height; } + float mcm_setting::get_slot_count_text_offset() { return slot_count_text_offset; } + float mcm_setting::get_slot_count_text_font_size() { return slot_count_text_font_size; } } diff --git a/src/setting/mcm_setting.h b/src/setting/mcm_setting.h index eee7156..8ffb52e 100644 --- a/src/setting/mcm_setting.h +++ b/src/setting/mcm_setting.h @@ -54,5 +54,7 @@ namespace config { static uint32_t get_slot_button_feedback(); static float get_key_icon_scale_width(); static float get_key_icon_scale_height(); + static float get_slot_count_text_offset(); + static float get_slot_count_text_font_size(); }; } diff --git a/src/ui/ui_renderer.cpp b/src/ui/ui_renderer.cpp index a241c33..972ae1d 100644 --- a/src/ui/ui_renderer.cpp +++ b/src/ui/ui_renderer.cpp @@ -174,11 +174,38 @@ namespace ui { ui_renderer::ui_renderer() = default; + void ui_renderer::draw_text(const float a_x, + const float a_y, + const float a_offset_x, + const float a_offset_y, + const char* a_text, + const ImU32 a_color) { + + const ImFont* font = ImGui::GetFont(); + const auto font_size = config::mcm_setting::get_slot_count_text_font_size(); + + //get data from normal hud, and add an offset config for each image + const auto width_setting = config::mcm_setting::get_hud_image_position_width(); + const auto height_setting = config::mcm_setting::get_hud_image_position_height(); + + const auto extra_x = config::mcm_setting::get_slot_count_text_offset(); + const auto extra_y = extra_x; + + ImVec2 position; + if (width_setting > a_x || height_setting > a_y) { + position = ImVec2(0.f, 0.f); + } else { + position = ImVec2(width_setting + a_offset_x + extra_x, height_setting + a_offset_y + extra_y); + } + + ImGui::GetWindowDrawList()->AddText(font, font_size, position, a_color, a_text, nullptr, 0.0f, nullptr); + } + void ui_renderer::draw_element(ID3D11ShaderResourceView* a_texture, const ImVec2 a_center, const ImVec2 a_size, const float a_angle, - const ImU32 col) { + const ImU32 a_color) { const float cos_a = cosf(a_angle); const float sin_a = sinf(a_angle); const ImVec2 pos[4] = { a_center + ImRotate(ImVec2(-a_size.x * 0.5f, -a_size.y * 0.5f), cos_a, sin_a), @@ -191,7 +218,7 @@ namespace ui { ImGui::GetWindowDrawList() - ->AddImageQuad(a_texture, pos[0], pos[1], pos[2], pos[3], uvs[0], uvs[1], uvs[2], uvs[3], col); + ->AddImageQuad(a_texture, pos[0], pos[1], pos[2], pos[3], uvs[0], uvs[1], uvs[2], uvs[3], a_color); } void ui_renderer::draw_hud(const float a_x, const float a_y) { @@ -264,6 +291,15 @@ namespace ui { offset_setting->offset_slot_y, page_setting->icon_type, page_setting->icon_opacity); + if (auto slot_settings = page_setting->slot_settings; + position == handle::page_setting::position::left && !slot_settings.empty() && slot_settings.front()-> + item_count > 0) { + draw_text(a_x, + a_y, + offset_setting->offset_slot_x, + offset_setting->offset_slot_y, + std::to_string(slot_settings.front()->item_count).c_str()); + } } } diff --git a/src/ui/ui_renderer.h b/src/ui/ui_renderer.h index 08c7ddd..a39ed26 100644 --- a/src/ui/ui_renderer.h +++ b/src/ui/ui_renderer.h @@ -37,11 +37,13 @@ namespace ui { ui_renderer(); + static void draw_text(float a_x, float a_y, float a_offset_x, float a_offset_y, const char* a_text, ImU32 a_color = IM_COL32_WHITE); + static void draw_element(ID3D11ShaderResourceView* a_texture, ImVec2 a_center, ImVec2 a_size, float a_angle, - ImU32 col = IM_COL32_WHITE); + ImU32 a_color = IM_COL32_WHITE); static void draw_hud(float a_x, float a_y); static void draw_slot(float a_x, float a_y, float a_offset_x, float a_offset_y, uint32_t a_modify); static void draw_slots(float a_x, float a_y, const std::map& a_settings);