diff --git a/README.md b/README.md index f8d7dae..15ac242 100644 --- a/README.md +++ b/README.md @@ -70,10 +70,26 @@ This is a plugin with a set of many quality-of-life and utility features made fo - World speed will cycle in the order of set multipliers after pressing `Z`. Shortcut can also be changed with `KeyTimeMultiplier` option. - Currently used multiplier will be shown next to the time icon. -- Displays protection icon and value next to the focused npc hp bar. - - - Setting `ShowTargetProtection` option to `0` disables this feature, `1` displays the protection that matches currently used weapon and `2` shows all protection stats. - - `ShowProtOnlyInFight` and `ShowProtAllDamageTypes` are extra options that can be used, to alter what is displayed. +- Displays protection icons and values next to the focused npc hp bar. + + - Setting are split in two modes and works separately, fight and no fight. Fight means when melee/distance weapon or spell is drawn, No fight means otherwise. + - `ShowTargetProtectionNoFight` option for no fight mode. + - `ShowTargetProtectionInFight` option for fight mode. + - There are 3 options for above settings: + - `0` - _Disabled_ No icons and values will be shown. + - `1` - _CurrentWeapon_ Only icons and values for current drawn or equipped weapon will be shown. In no fight mode, there is the possibility of more than one icon and value being shown, depending on the equipped distance/melee weapon and/or spell. + - `2` - _All_ Displays all icons and values for all protections. This option could be filtered with some of the options described in section below. + - Note that: + - Transformation into a monster is treated as fight mode. + - When option is set higher than `0` and target has flag `NPC_FLAG_IMMORTAL` then only one icon (cracked shield) will be shown. There is no point in showing all the icons if target is immortal. + - Immunity to a specific protection type is treated as non-zero value and is utilized in the hide options described in the section below. + - Icon style is determined by `TargetProtectionIconStyle` option. Set it to `0` - _DamagePopup_ then corresponding protection icons and colors will be same as damage popup. Set it to `1` - _Shields_ for simple shield icons distinguished only by colors. + - Icon position is defined by `TargetProtectionIconPosition` option. Set it to `0` - _Top_ then protection icons will be positioned above focused health bar in row. Set it to `1` - _Right_ then icons will be positioned in column to the right of focused health bar. Note that if there will be only one icon to show then this setting will be omitted temporary and icon will be shown as close as possible to the right of the focused health bar. + - Some protection icons can be hidden but only when `ShowTargetProtectionInFight` or `ShowTargetProtectionNoFight` option is set to `2` - _All_. Conditions are checked from top to bottom, so for example if `HideTargetProtectionZeroValues` is enabled and `HideTargetProtectionFallDamage` is disabled and target has fall damage protection equal to zero then fall damage icon will not be shown. + - `HideTargetProtectionZeroValues` option hides protection icons with zero value, but they will still be shown if target is immune. + - `HideTargetProtectionFallDamage` option hides fall damage protection icon even if target is immune. + - `HideTargetProtectionFlyDamage` option hides fly damage protection icon even if target is immune. + - `HideTargetProtectionFireDamage` option hides fire damage protection icon even if target is immune. - Displays coin icon next to the focused npc name if player can pickpocket him. @@ -199,14 +215,33 @@ SaveReminder=5 ; ... Time in minutes after which the reminder to save the game appears on the screen ; ... set to -1 to disable -ShowTargetProtection=1 -; ... enables for currently equipped weapon (1) or shows all protection stats (2) or disables (0) protection icon and value next to the focused npc hp bar +ShowTargetProtectionNoFight=0 +;... specifies mode for showing target protection in no fight mode by +;... (0) - 'Disabled', (1) - 'CurrentWeapon', (2) - 'All' + +ShowTargetProtectionInFight=1 +;... specifies mode for showing target protection in fight mode by +;... (0) - 'Disabled', (1) - 'CurrentWeapon', (2) - 'All' + +TargetProtectionIconStyle=0 +;... specifies style for protection icons +;... (0) - 'DamagePopup', (1) - 'Shields' + +TargetProtectionIconPosition=0 +;... specifies position for protection icons +;... (0) - 'Top', (1) - 'Right' -ShowProtOnlyInFight=1 -; ... enables (1) or disables (0) showing protection stats only during combat +HideTargetProtectionZeroValues=0 +... hides protection icons with zero value; (0) - 'Disabled', (1) - 'Enabled' -ShowProtAllDamageTypes=0 -; ... enables (1) or disables (0) showing all protection stats, even if they are 0 +HideTargetProtectionFallDamage=0 +... hides protection icon for fall damage; (0) - 'Disabled', (1) - 'Enabled' + +HideTargetProtectionFlyDamage=0 +... hides protection icon for fly damage; (0) - 'Disabled', (1) - 'Enabled' + +HideTargetProtectionFireDamage=0 +... hides protection icon for fire damage; (0) - 'Disabled', (1) - 'Enabled' RecoveryVisualization=1 ; ... enables (1) or disables (0) visualization of healing that hovered in the inventory item gives @@ -313,6 +348,9 @@ DamagePopupColorDmgTypes=1 DamagePopupColorOnlyIcon=0 ; ... enables (1) or disables (0) coloring only the popup icon + +DistanceWeaponDamageType=64 +; ... This value is used to override distance weapon protection icon type. It's maintained by plugin itself. Do not change it. ``` diff --git a/vdf-include/SYSTEM/AUTORUN/ZUTILITIES.D b/vdf-include/SYSTEM/AUTORUN/ZUTILITIES.D index 9e96b10..fe6a408 100644 --- a/vdf-include/SYSTEM/AUTORUN/ZUTILITIES.D +++ b/vdf-include/SYSTEM/AUTORUN/ZUTILITIES.D @@ -30,6 +30,11 @@ const int Choice_PX = 6400; // Position at left side const int Choice_SX = 1500; // Size X const int Choice_SY = 350; // Size Y const int Choice_DY = 120; // Delta alignment +// Choice for Protection Option +const int ChoiceProtection_PX = 6000; // Position at left side +const int ChoiceProtection_SX = 1900; // Size X +const int ChoiceProtection_SY = 350; // Size Y +const int ChoiceProtection_DY = 120; // Delta alignment const string MenuBackPic = "UnionMenu_BackPic.tga"; const string ItemBackPic = ""; @@ -87,6 +92,19 @@ instance C_MENUITEM_CHOICE_BASE(C_MENU_ITEM_DEF) flags = flags | IT_TXT_CENTER; }; +instance C_MENUITEM_CHOICE_PROTECTION(C_MENU_ITEM_DEF) +{ + backpic = ChoiceBackPic; + type = MENU_ITEM_CHOICEBOX; + fontname = FontSmall; + posx = ChoiceProtection_PX; + posy = Start_PY + ChoiceProtection_DY; + dimx = ChoiceProtection_SX; + dimy = ChoiceProtection_SY; + flags = flags & ~IT_SELECTABLE; + flags = flags | IT_TXT_CENTER; +}; + instance C_MENUITEM_SLIDER_BASE(C_MENU_ITEM_DEF) { backpic = SliderBackPic; @@ -886,58 +904,176 @@ instance MenuItem_Opt_Page5_ShowTargetProtection(C_MENU_ITEM) fontname = FontSmall; posy += Menu_DY * CurrentMenuItem_PY + Text_DY; - text[0] = "ShowTargetProtection"; + text[0] = "ShowTargetProtectionNoFight"; text[1] = "protection icon and value next to the focused npc hp bar"; }; -instance MenuItem_Opt_Page5_ShowTargetProtection_Choice(C_MENU_ITEM_DEF) +instance MenuItem_Opt_Page5_ShowTargetProtectionNoFight_Choice(C_MENU_ITEM_DEF) { - C_MENUITEM_CHOICE_BASE(); + C_MENUITEM_CHOICE_PROTECTION(); posy += Menu_DY * CurrentMenuItem_PY; - onchgsetoption = "ShowTargetProtection"; + onchgsetoption = "ShowTargetProtectionNoFight"; onchgsetoptionsection = "zUtilities"; - text[0] = "Off|Weapon|All"; + text[0] = "Disabled|CurrentWeapon|All"; }; -instance MenuItem_Opt_Page5_ShowProtOnlyInFight(C_MENU_ITEM) +instance MenuItem_Opt_Page5_ShowTargetProtectionInFight(C_MENU_ITEM) { CurrentMenuItem_PY = 2; C_MENU_ITEM_TEXT_BASE(); fontname = FontSmall; posy += Menu_DY * CurrentMenuItem_PY + Text_DY; - text[0] = "ShowProtOnlyInFight"; - text[1] = "shows icons only if hero is in fight mode"; + text[0] = "ShowTargetProtectionInFight"; + text[1] = "protection icon and value next to the focused npc hp bar"; }; -instance MenuItem_Opt_Page5_ShowProtOnlyInFight_Choice(C_MENU_ITEM_DEF) +instance MenuItem_Opt_Page5_ShowTargetProtectionInFight_Choice(C_MENU_ITEM_DEF) { - C_MENUITEM_CHOICE_BASE(); + C_MENUITEM_CHOICE_PROTECTION(); posy += Menu_DY * CurrentMenuItem_PY; - onchgsetoption = "ShowProtOnlyInFight"; + onchgsetoption = "ShowTargetProtectionInFight"; onchgsetoptionsection = "zUtilities"; - text[0] = "Off|On"; + text[0] = "Disabled|CurrentWeapon|All"; }; -instance MenuItem_Opt_Page5_ShowProtAllDamageTypes(C_MENU_ITEM) +instance MenuItem_Opt_Page5_TargetProtectionIconStyle(C_MENU_ITEM) { CurrentMenuItem_PY = 3; C_MENU_ITEM_TEXT_BASE(); fontname = FontSmall; posy += Menu_DY * CurrentMenuItem_PY + Text_DY; - text[0] = "ShowProtAllDamageTypes"; - text[1] = "shows icons for all damage types, even if 0 protection"; + text[0] = "TargetProtectionIconStyle"; + text[1] = "protection icon style"; }; -instance MenuItem_Opt_Page5_ShowProtAllDamageTypes_Choice(C_MENU_ITEM_DEF) +instance MenuItem_Opt_Page5_TargetProtectionIconStyle_Choice(C_MENU_ITEM_DEF) { - C_MENUITEM_CHOICE_BASE(); + C_MENUITEM_CHOICE_PROTECTION(); posy += Menu_DY * CurrentMenuItem_PY; - onchgsetoption = "ShowProtAllDamageTypes"; + onchgsetoption = "TargetProtectionIconStyle"; onchgsetoptionsection = "zUtilities"; - text[0] = "Off|On"; + text[0] = "DamagePopup|Shields"; +}; + +instance MenuItem_Opt_Page5_TargetProtectionIconPosition(C_MENU_ITEM) +{ + CurrentMenuItem_PY = 4; + C_MENU_ITEM_TEXT_BASE(); + fontname = FontSmall; + posy += Menu_DY * CurrentMenuItem_PY + Text_DY; + + text[0] = "TargetProtectionIconPosition"; + text[1] = "protection icon position"; }; + +instance MenuItem_Opt_Page5_TargetProtectionIconPosition_Choice(C_MENU_ITEM_DEF) +{ + C_MENUITEM_CHOICE_PROTECTION(); + posy += Menu_DY * CurrentMenuItem_PY; + + onchgsetoption = "TargetProtectionIconPosition"; + onchgsetoptionsection = "zUtilities"; + text[0] = "Top|Right"; +}; + +instance MenuItem_Opt_Page5_Disclaimer(C_MENU_ITEM_DEF) +{ + CurrentMenuItem_PY = 5; + fontname = FontSmall; + type = MENU_ITEM_TEXT; + posx = 0; + posy = Menu_DY * CurrentMenuItem_PY + Text_DY + Start_PY; + dimx = 8100; + flags = flags & ~IT_SELECTABLE; + flags = flags | IT_TXT_CENTER; + text[0] = "Below section works only when ShowTargetProtection* = `All`"; +}; + +instance MenuItem_Opt_Page5_HideTargetProtectionZeroValues(C_MENU_ITEM) +{ + CurrentMenuItem_PY = 6; + C_MENU_ITEM_TEXT_BASE(); + fontname = FontSmall; + posy += Menu_DY * CurrentMenuItem_PY + Text_DY; + + text[0] = "HideTargetProtectionZeroValues"; + text[1] = "hide protection icon when value equals 0"; +}; + +instance MenuItem_Opt_Page5_HideTargetProtectionZeroValues_Choice(C_MENU_ITEM_DEF) +{ + C_MENUITEM_CHOICE_PROTECTION(); + posy += Menu_DY * CurrentMenuItem_PY; + + onchgsetoption = "HideTargetProtectionZeroValues"; + onchgsetoptionsection = "zUtilities"; + text[0] = "Disabled|Enabled"; +}; + +instance MenuItem_Opt_Page5_HideTargetProtectionFallDamage(C_MENU_ITEM) +{ + CurrentMenuItem_PY = 7; + C_MENU_ITEM_TEXT_BASE(); + fontname = FontSmall; + posy += Menu_DY * CurrentMenuItem_PY + Text_DY; + + text[0] = "HideTargetProtectionFallDamage"; + text[1] = "hide protection icon for fall damage"; +}; + +instance MenuItem_Opt_Page5_HideTargetProtectionFallDamage_Choice(C_MENU_ITEM_DEF) +{ + C_MENUITEM_CHOICE_PROTECTION(); + posy += Menu_DY * CurrentMenuItem_PY; + + onchgsetoption = "HideTargetProtectionFallDamage"; + onchgsetoptionsection = "zUtilities"; + text[0] = "Disabled|Enabled"; +}; + +instance MenuItem_Opt_Page5_HideTargetProtectionFlyDamage(C_MENU_ITEM) +{ + CurrentMenuItem_PY = 8; + C_MENU_ITEM_TEXT_BASE(); + fontname = FontSmall; + posy += Menu_DY * CurrentMenuItem_PY + Text_DY; + + text[0] = "HideTargetProtectionFlyDamage"; + text[1] = "hide protection icon for fly damage"; +}; + +instance MenuItem_Opt_Page5_HideTargetProtectionFlyDamage_Choice(C_MENU_ITEM_DEF) +{ + C_MENUITEM_CHOICE_PROTECTION(); + posy += Menu_DY * CurrentMenuItem_PY; + + onchgsetoption = "HideTargetProtectionFlyDamage"; + onchgsetoptionsection = "zUtilities"; + text[0] = "Disabled|Enabled"; +}; + +instance MenuItem_Opt_Page5_HideTargetProtectionFireDamage(C_MENU_ITEM) +{ + CurrentMenuItem_PY = 9; + C_MENU_ITEM_TEXT_BASE(); + fontname = FontSmall; + posy += Menu_DY * CurrentMenuItem_PY + Text_DY; + + text[0] = "HideTargetProtectionFireDamage"; + text[1] = "hide protection icon for fire damage"; +}; + +instance MenuItem_Opt_Page5_HideTargetProtectionFireDamage_Choice(C_MENU_ITEM_DEF) +{ + C_MENUITEM_CHOICE_PROTECTION(); + posy += Menu_DY * CurrentMenuItem_PY; + + onchgsetoption = "HideTargetProtectionFireDamage"; + onchgsetoptionsection = "zUtilities"; + text[0] = "Disabled|Enabled"; +}; \ No newline at end of file diff --git a/zUtilities/DamageMaskHelper.cpp b/zUtilities/DamageMaskHelper.cpp new file mode 100644 index 0000000..1c42e9e --- /dev/null +++ b/zUtilities/DamageMaskHelper.cpp @@ -0,0 +1,63 @@ +// Supported with union (c) 2020 Union team +// Union SOURCE file + +namespace GOTHIC_ENGINE +{ + void DamageMaskHelper::MarkIntDamageType(const int& damageTypeMask, DamageMask& mask) + { + for (const auto& mapItem : DAMAGE_MAP) { + if (damageTypeMask & mapItem.type) { + mask.set(mapItem.index); + } + } + } + + void DamageMaskHelper::MarkWeaponDamage(const oCItem* weapon, DamageMask& mask) + { + for (int i = 0; i < oEDamageIndex::oEDamageIndex_MAX; ++i) { + if (weapon->damage[i] > 0) { + mask.set(i); + } + } + } + + void DamageMaskHelper::MarkMunitionDamage(const oCItem* weapon, DamageMask& mask) + { + const bool hasMunition = weapon->munition != 0; + const bool isCrossbow = (weapon->flags & ITM_FLAG_CROSSBOW) != 0; + + const oCItem* leftHand = player->GetLeftHand()->CastTo(); + const oCItem* rightHand = player->GetRightHand()->CastTo(); + const oCItem* handItem = isCrossbow ? leftHand : rightHand; + + const int handInstanz = + reinterpret_cast(handItem) + ? handItem->instanz + : 0; + + const bool useMunition = + static_cast( + hasMunition & + (handItem != nullptr) & + (handInstanz == weapon->munition) + ); + + const oCItem* damageSource = useMunition ? handItem : weapon; + + MarkWeaponDamage(damageSource, mask); + } + + bool DamageMaskHelper::ItemHasDistanceOrMunitionCategoryFlag(const oCItem* item) { + return item->mainflag & (ITM_CAT_FF | ITM_CAT_MUN); + } + + /* In G1 default damageType for spell is `oEDamageType_Blunt` so for summon/transformation etc. spells +it's best to reset this flag in mask to hide incorrect protection icon. +In G2 default damageType for spell is `oEDamageType_Magic` so it's left untouched.*/ + void DamageMaskHelper::FixupSpellDamageMask(DamageMask& mask) + { +#if ENGINE <= Engine_G1A + mask.reset(oEDamageIndex_Blunt); +#endif + } +} \ No newline at end of file diff --git a/zUtilities/DamageMaskHelper.h b/zUtilities/DamageMaskHelper.h new file mode 100644 index 0000000..0ad123f --- /dev/null +++ b/zUtilities/DamageMaskHelper.h @@ -0,0 +1,35 @@ +// Supported with union (c) 2020 Union team +// Union SOURCE file + +namespace GOTHIC_ENGINE { + using DamageMask = std::bitset; + + // Mapping between oEDamageType bit flags and oEIndexDamage enum values. + // This is needed because: + // - oEDamageType uses bit flags (1, 2, 4, 8, ...) + // - oEIndexDamage uses sequential indices (0..7) + struct DamageMap { + oEDamageType type; + oEIndexDamage index; + }; + + static constexpr DamageMap DAMAGE_MAP[] = { + { oEDamageType_Barrier, oEDamageIndex_Barrier }, + { oEDamageType_Blunt, oEDamageIndex_Blunt }, + { oEDamageType_Edge, oEDamageIndex_Edge }, + { oEDamageType_Fire, oEDamageIndex_Fire }, + { oEDamageType_Fly, oEDamageIndex_Fly }, + { oEDamageType_Magic, oEDamageIndex_Magic }, + { oEDamageType_Point, oEDamageIndex_Point }, + { oEDamageType_Fall, oEDamageIndex_Fall } + }; + + class DamageMaskHelper { + public: + static void MarkIntDamageType(const int& damageTypeMask, DamageMask& mask); + static void MarkMunitionDamage(const oCItem* weapon, DamageMask& mask); + static void MarkWeaponDamage(const oCItem* weapon, DamageMask& mask); + static bool ItemHasDistanceOrMunitionCategoryFlag(const oCItem* item); + static void FixupSpellDamageMask(DamageMask& mask); + }; +} \ No newline at end of file diff --git a/zUtilities/FocusStatusBar.cpp b/zUtilities/FocusStatusBar.cpp index 3fbb184..4428188 100644 --- a/zUtilities/FocusStatusBar.cpp +++ b/zUtilities/FocusStatusBar.cpp @@ -1,10 +1,93 @@ -// Supported with union (c) 2020 Union team +// Supported with union (c) 2020 Union team // Union SOURCE file namespace GOTHIC_ENGINE { + bool FocusStatusBar::IsDistanceWeaponDamageTypeOverwritten = false; + + const oEIndexDamage FocusStatusBar::ProtectionDamageOrderedIndexes[ProtectionCount] = { + oEDamageIndex_Edge, + oEDamageIndex_Blunt, + oEDamageIndex_Point, + oEDamageIndex_Fire, + oEDamageIndex_Magic, + oEDamageIndex_Fly, + oEDamageIndex_Fall + }; + + HOOK Ivk_OnDamage_Hit_DistanceWeapon PATCH(&oCNpc::OnDamage_Hit, &oCNpc::OnDamage_Hit_DistanceWeapon); + void oCNpc::OnDamage_Hit_DistanceWeapon(oSDamageDescriptor& desc) { + THISCALL(Ivk_OnDamage_Hit_DistanceWeapon)(desc); + + if (FocusStatusBar::IsDistanceWeaponDamageTypeOverwritten) + return; + + if (desc.pNpcAttacker != player) + return; + + if (!desc.pItemWeapon) + return; + + if (!DamageMaskHelper::ItemHasDistanceOrMunitionCategoryFlag(desc.pItemWeapon)) + return; + + if (Options::DistanceWeaponDamageTypeFromIni != desc.enuModeDamage) { + DamageMask tmp{}; + DamageMaskHelper::MarkIntDamageType(desc.enuModeDamage, tmp); + Options::DistanceWeaponDamageType = tmp; + Options::DistanceWeaponDamageTypeFromIni = desc.enuModeDamage; + zoptions->WriteInt(PLUGIN_NAME, "DistanceWeaponDamageType", desc.enuModeDamage, 0); + } + FocusStatusBar::IsDistanceWeaponDamageTypeOverwritten = true; + } + + HOOK Ivk_EquipItem_Union PATCH(&oCNpc::EquipItem, &oCNpc::EquipItem_Union); + void oCNpc::EquipItem_Union(oCItem* item) { + THISCALL(Ivk_EquipItem_Union)(item); + + if (this != player) + return; + + if (!DamageMaskHelper::ItemHasDistanceOrMunitionCategoryFlag(item)) + return; + + FocusStatusBar::IsDistanceWeaponDamageTypeOverwritten = false; + } FocusStatusBar::FocusStatusBar() : StatusBar(ogame->focusBar) { + iconCache[oEDamageIndex_Barrier] = "DMGICON_BARRIER"; + iconCache[oEDamageIndex_Blunt] = "DMGICON_BLUNT"; // https://game-icons.net/1x1/lorc/cross-mark.html + iconCache[oEDamageIndex_Edge] = "DMGICON_EDGE"; // https://game-icons.net/1x1/lorc/ragged-wound.html + iconCache[oEDamageIndex_Fire] = "DMGICON_FIRE"; // https://game-icons.net/1x1/lorc/burning-embers.html + iconCache[oEDamageIndex_Fly] = "DMGICON_FLY"; // https://game-icons.net/1x1/lorc/fluffy-trefoil.html + iconCache[oEDamageIndex_Magic] = "DMGICON_MAGIC"; // https://game-icons.net/1x1/delapouite/polar-star.html + iconCache[oEDamageIndex_Point] = "DMGICON_POINT"; // https://game-icons.net/1x1/skoll/bullseye.html + iconCache[oEDamageIndex_Fall] = "DMGICON_FALL"; // https://game-icons.net/1x1/sbed/falling.html + iconCache[oEDamageIndex_MAX] = "DMGICON_UNKNOWN"; // https://game-icons.net/1x1/lorc/scar-wound.html + } + + bool FocusStatusBar::NeedAdjustPosition(int x, int y, oCNpc* npc) { + if (!bar || !npc || npc->attribute[NPC_ATR_HITPOINTS] <= 0) + return false; + + valueOffsetY = 0; + MoveFocusBar(x, y, npc); + + if (BuildProtectionModel(npc)) + { + ProtectionLayout layout; + BuildProtectionLayout(layout); + + if (layout.placement == FocusStatusProtectionPlacement::TOP) + { + valueOffsetY = layout.startY - bar->vposy; + } + + RenderProtectionWithLayout(layout); + } + + PrintValue(); + return Options::ShowEnemyBarAboveHim; } bool FocusStatusBar::Init() { @@ -21,131 +104,321 @@ namespace GOTHIC_ENGINE { del(protView); } - int FocusStatusBar::GetProtMargin() { - return protView->FontY() * 0.1f; - } + void FocusStatusBar::Loop() { + if (protView) { + protView->ClrPrintwin(); + } + else { + protView = new zCView(0, 0, 8192, 8192); + screen->InsertItem(protView); + } - int FocusStatusBar::GetProtSize() { - return protView->FontY() * 0.75f; + if (valueView) { + valueView->ClrPrintwin(); + } } - int FocusStatusBar::GetProtStartX(FocusStatusProtectionPlacement placement) { - if (placement == FocusStatusProtectionPlacement::TOP) { - return bar->vposx; + bool FocusStatusBar::BuildProtectionModel(oCNpc* npc) + { + activeStatusesCount = 0; + protectionModel.isInFightMode = player->fmode != 0; + protectionModel.targetProtectionMode = protectionModel.isInFightMode + ? (TargetProtectionMode)Options::ShowTargetProtectionInFight + : (TargetProtectionMode)Options::ShowTargetProtectionNoFight; + + if (protectionModel.targetProtectionMode == TargetProtectionMode::Disabled) + return false; + + if (npc->HasFlag(NPC_FLAG_IMMORTAL)) + { + protectionModel.renderMode = ProtectionRenderMode::Immortal; + return true; } - return bar->vposx + bar->vsizex; + FillDamageIndexesBuffer(); + + for (int i = 0; i < activeDamageIndexesCount; ++i) { + oEIndexDamage index = activeDamageIndexes[i]; + + if (CanRenderProtectionStatus(npc, index)) { + NpcProtectionStatus& status = activeStatuses[activeStatusesCount++]; + status.value = npc->GetProtectionByIndex(index); + status.damageIndex = index; + status.immune = status.value < 0; + } + } + protectionModel.renderMode = ProtectionRenderMode::Normal; + + return activeStatusesCount > 0; } - FocusStatusProtectionPlacement FocusStatusBar::GetProtPlacement(oCNpc* npc) + void FocusStatusBar::FillDamageIndexesBuffer() { - auto count = npcHelper.GetProtectionStatusesVisibleCount(npc); - if (count <= 0) { - return FocusStatusProtectionPlacement::NONE; + activeDamageIndexesCount = 0; + + if (protectionModel.targetProtectionMode == TargetProtectionMode::All) { + for (int i = 0; i < ProtectionCount; ++i) + { + activeDamageIndexes[activeDamageIndexesCount++] = ProtectionDamageOrderedIndexes[i]; + } + return; } - if (count == 1) { - return FocusStatusProtectionPlacement::RIGHT; + DamageMask mask; + mask.reset(); + + if (protectionModel.isInFightMode) { + BuildFightModeDamage(mask); + } + else { + BuildNoFightModeDamage(mask); } - return FocusStatusProtectionPlacement::TOP; + for (int i = 0; i < oEDamageIndex_MAX; i++) { + if (mask.test(i)) { + activeDamageIndexes[activeDamageIndexesCount++] = (oEIndexDamage)i; + } + } } - int FocusStatusBar::GetProtStartY(FocusStatusProtectionPlacement placement) { - if (placement == FocusStatusProtectionPlacement::TOP) { - return bar->vposy - GetProtMargin() - GetProtSize() - bar->vsizey; + void FocusStatusBar::BuildFightModeDamage(DamageMask& mask) + { + // Check for active spell + if (player->IsInFightMode_S(NPC_WEAPON_MAG)) { + if (auto spell = player->mag_book->GetSelectedSpell()) { + DamageMaskHelper::MarkIntDamageType(spell->damageType, mask); + DamageMaskHelper::FixupSpellDamageMask(mask); + } + return; } - return bar->vposy + bar->vsizey / 2 - GetProtSize(); + // Check for active melee/distance(munition) weapon + if (auto weapon = player->GetWeapon()) { + if (DamageMaskHelper::ItemHasDistanceOrMunitionCategoryFlag(weapon)) { + mask = Options::DistanceWeaponDamageType; + } + else { + DamageMaskHelper::MarkWeaponDamage(weapon, mask); + } + } + else { + mask.set(oEDamageIndex_Blunt); // Fist damage + } } - void FocusStatusBar::PrintValueOutside(zSTRING str, oCNpc * npc) + void FocusStatusBar::BuildNoFightModeDamage(DamageMask& mask) { - auto protPlacement = GetProtPlacement(npc); - int offsetY = bar->vsizey / 2 + valueView->FontY(); - int x = bar->vposx + bar->vsizex / 2 - valueView->FontSize(str) / 2; - int y = bar->vposy - offsetY; + // Check for all selected spells + if (player->mag_book) { + auto& spells = player->mag_book->spells; + for (int i = 0; i < spells.GetNum(); ++i) { + DamageMaskHelper::MarkIntDamageType(spells[i]->damageType, mask); + } + DamageMaskHelper::FixupSpellDamageMask(mask); + } - if (protPlacement == FocusStatusProtectionPlacement::TOP) { - y = GetProtStartY(protPlacement) - offsetY; + // Check for melee weapon + auto weapon = player->GetEquippedMeleeWeapon(); + if (weapon) { + DamageMaskHelper::MarkWeaponDamage(weapon, mask); + } + else { + mask.set(oEDamageIndex_Blunt); // Fist damage } - valueView->SetFontColor(zCOLOR(valueView->color.r, valueView->color.g, valueView->color.b, bar->alpha)); - valueView->Print(x, y, str); + // Check for distance weapon - munition + weapon = player->GetEquippedRangedWeapon(); + if (weapon) { + mask |= Options::DistanceWeaponDamageType; + } } - int FocusStatusBar::CalcProtRenderWidth(std::vector statuses) { - int width = 0; - int size = GetProtSize(); - int margin = GetProtMargin(); - auto statusCount = statuses.size(); + void FocusStatusBar::BuildProtectionLayout(ProtectionLayout& layout) + { + layout.fontY = protView->FontY(); + layout.iconSize = layout.fontY * 0.75f; + + if (protectionModel.renderMode == ProtectionRenderMode::Immortal) { + layout.placement = FocusStatusProtectionPlacement::CLOSE; + layout.margin = GetHorizontalProtMargin(layout.fontY); + layout.totalContentSize = layout.iconSize; + layout.startX = bar->vposx + bar->vsizex; + layout.startY = bar->vposy + bar->vsizey / 2 - layout.iconSize; + return; + } - for (int i = 0; i < statusCount; i++) + layout.placement = GetProtectionPlacement(); + switch (layout.placement) + { + case FocusStatusProtectionPlacement::TOP: { - auto status = statuses[i]; - if (status.immune && statusCount == 1) + layout.margin = GetHorizontalProtMargin(layout.fontY); + + const int iconSpacing = layout.fontY * 0.1f; + const int immuneStringSize = screen->FontSize(ImmuneAbbreviation); + + for (int i = 0; i < activeStatusesCount; ++i) { - width += size; - continue; + const int textSize = activeStatuses[i].immune + ? immuneStringSize + : screen->FontSize(zSTRING(activeStatuses[i].value)); + + layout.totalContentSize += layout.iconSize + iconSpacing + textSize + layout.margin; } + layout.totalContentSize -= layout.margin; + layout.startX = bar->vposx; + layout.startY = bar->vposy - layout.margin - layout.iconSize - bar->vsizey; + break; + } + case FocusStatusProtectionPlacement::RIGHT: + { + layout.margin = GetVerticalProtMargin(layout.fontY); + layout.totalContentSize = activeStatusesCount * (layout.iconSize + layout.margin) - layout.margin; + layout.startX = bar->vposx + bar->vsizex + ProtectionPlacementRightMargin; + layout.startY = bar->vposy + bar->vsizey / 2 - layout.iconSize; + break; + } + case FocusStatusProtectionPlacement::CLOSE: + { + layout.margin = GetHorizontalProtMargin(layout.fontY); + const int iconSpacing = layout.fontY * 0.1f; - width += size + screen->FontY() / 10 + screen->FontSize(zSTRING(status.value)); + const int textSize = activeStatuses[0].immune + ? screen->FontSize(ImmuneAbbreviation) + : screen->FontSize(zSTRING(activeStatuses[0].value)); - if (i + 1 != statuses.size()) { - width += margin; - } + layout.totalContentSize = layout.iconSize + iconSpacing + textSize; + layout.startX = bar->vposx + bar->vsizex; + layout.startY = bar->vposy + bar->vsizey / 2 - layout.iconSize; + break; + } } + } + + FocusStatusProtectionPlacement FocusStatusBar::GetProtectionPlacement() const + { + if (activeStatusesCount == 1) { + return FocusStatusProtectionPlacement::CLOSE; + } + + if (Options::TargetProtectionIconPosition == 0) { + return FocusStatusProtectionPlacement::TOP; + } + + if (activeStatusesCount <= 0) { + return FocusStatusProtectionPlacement::NONE; + } + + return FocusStatusProtectionPlacement::RIGHT; + } - return width; + int FocusStatusBar::GetHorizontalProtMargin(int fontY) const { + return fontY * 0.1f + 30; } - bool FocusStatusBar::TryShowProt(oCNpc* npc) { - const zSTRING texture = "ICON_PROTECTIONS"; // https://game-icons.net/1x1/lorc/cracked-shield.html + int FocusStatusBar::GetVerticalProtMargin(int fontY) const { + return fontY + 85; + } - if (!Options::ShowTargetProtection) - return false; + void FocusStatusBar::RenderProtectionWithLayout(const ProtectionLayout& layout) + { + if (protectionModel.renderMode == ProtectionRenderMode::Immortal) + { + auto color = Colors::Gray; + if (ogame->hpBar) + color.alpha = ogame->hpBar->alpha; - if (npc->attribute[NPC_ATR_HITPOINTS] <= 0) - return false; + IconInfo(layout.startX + layout.margin, layout.startY, layout.iconSize, color, CrackedShieldTexture); + return; + } - if (!bar) - return false; + switch (layout.placement) + { + case FocusStatusProtectionPlacement::CLOSE: + RenderProtectionIconsClose(layout); + break; + + case FocusStatusProtectionPlacement::TOP: + RenderProtectionIconsTop(layout); + break; + + case FocusStatusProtectionPlacement::RIGHT: + RenderProtectionIconsRight(layout); + break; + } + } + bool FocusStatusBar::CanRenderProtectionStatus(oCNpc* npc, oEIndexDamage damageIndex) const + { + if (protectionModel.targetProtectionMode != TargetProtectionMode::All) + return true; - auto statuses = npcHelper.GetProtectionVisibleStatuses(npc); - auto statusCount = statuses.size(); - if (statusCount == 0) { + if (Options::HideTargetProtectionZeroValues && npc->GetProtectionByIndex(damageIndex) == 0) { return false; } - auto placement = GetProtPlacement(npc); - int margin = GetProtMargin(); - int size = GetProtSize(); - int startX = GetProtStartX(placement); - int startY = GetProtStartY(placement); - int offset = 0; + switch (damageIndex) { + case oEDamageIndex_Fall: + return !Options::HideTargetProtectionFallDamage; + case oEDamageIndex_Fly: + return !Options::HideTargetProtectionFlyDamage; + case oEDamageIndex_Fire: + return !Options::HideTargetProtectionFireDamage; + default: + return true; + } + } + + void FocusStatusBar::RenderProtectionIconsClose(const ProtectionLayout& layout) + { + unsigned char alpha = ogame->hpBar ? ogame->hpBar->alpha : 255; + auto data = BuildIconRenderData(activeStatuses[0], alpha); + + IconInfo(layout.startX + layout.margin, layout.startY, layout.iconSize, data.color, data.texture, data.text); + } + + void FocusStatusBar::RenderProtectionIconsRight(const ProtectionLayout& layout) + { + int currentY = layout.startY; + unsigned char alpha = ogame->hpBar ? ogame->hpBar->alpha : 255; - if (placement == FocusStatusProtectionPlacement::TOP) + for (int i = 0; i < activeStatusesCount; ++i) { - startX = startX + bar->vsizex / 2 - CalcProtRenderWidth(statuses) / 2 ; + auto data = BuildIconRenderData(activeStatuses[i], alpha); + + IconInfo(layout.startX, currentY, layout.iconSize, data.color, data.texture, data.text); + currentY += layout.iconSize + layout.margin; } + } - for (int i = 0; i < statuses.size(); i++) { - auto status = statuses[i]; - auto canRenderImmune = status.immune && statusCount == 1; + void FocusStatusBar::RenderProtectionIconsTop(const ProtectionLayout& layout) + { + int currentX = layout.startX + bar->vsizex / 2 - layout.totalContentSize / 2; + unsigned char alpha = ogame->hpBar ? ogame->hpBar->alpha : 255; - auto protectionText = !canRenderImmune ? zSTRING(status.value) : ""; - auto color = canRenderImmune ? Colors::Gray : Colors::GetColorByDamageIndex(status.damageIndex); - if (ogame->hpBar) - { - color.alpha = ogame->hpBar->alpha; - } + for (int i = 0; i < activeStatusesCount; ++i) + { + auto data = BuildIconRenderData(activeStatuses[i], alpha); - auto icon = IconInfo(startX + offset + margin, startY, size, color, texture, protectionText); - offset += icon.GetSize(); + auto icon = IconInfo(currentX, layout.startY, layout.iconSize, data.color, data.texture, data.text); + currentX += icon.GetSize() + layout.margin; } + } - return true; + ProtectionIconRenderData FocusStatusBar::BuildIconRenderData(const NpcProtectionStatus& status, unsigned char alpha) + { + ProtectionIconRenderData data; + + data.text = status.immune ? ImmuneAbbreviation : zSTRING(status.value); + + data.texture = Options::TargetProtectionIconStyle + ? CrackedShieldTexture + : GetIconNameByDamageIndex(status.damageIndex); + + data.color = Colors::GetColorByDamageIndex(status.damageIndex); + data.color.alpha = alpha; + + return data; } void FocusStatusBar::MoveFocusBar(int x, int y, oCNpc* npc) { @@ -172,27 +445,22 @@ namespace GOTHIC_ENGINE { bar->SetPos(x, y); } - bool FocusStatusBar::NeedAdjustPosition(int x, int y, oCNpc* npc) { - if (!bar || !npc || npc->attribute[NPC_ATR_HITPOINTS] <= 0) - return false; + void FocusStatusBar::PrintValueOutside(zSTRING& str) + { + int offsetY = bar->vsizey / 2 + valueView->FontY(); + int x = bar->vposx + bar->vsizex / 2 - valueView->FontSize(str) / 2; - MoveFocusBar(x, y, npc); - TryShowProt(npc); - PrintValue(npc); - return Options::ShowEnemyBarAboveHim; + int y = bar->vposy - offsetY + valueOffsetY; + + valueView->SetFontColor(zCOLOR(valueView->color.r, valueView->color.g, valueView->color.b, bar->alpha)); + valueView->Print(x, y, str); } - void FocusStatusBar::Loop() { - if (protView) { - protView->ClrPrintwin(); - } - else { - protView = new zCView(0, 0, 8192, 8192); - screen->InsertItem(protView); - } + const zSTRING& FocusStatusBar::GetIconNameByDamageIndex(const oEIndexDamage& index) + { + if (index < 0 || index >= oEDamageIndex_MAX) + return iconCache[oEDamageIndex_MAX]; // fallback - if (valueView) { - valueView->ClrPrintwin(); - } + return iconCache[index]; } } \ No newline at end of file diff --git a/zUtilities/FocusStatusBar.h b/zUtilities/FocusStatusBar.h index e4c9ed3..add71d9 100644 --- a/zUtilities/FocusStatusBar.h +++ b/zUtilities/FocusStatusBar.h @@ -2,31 +2,107 @@ // Union HEADER file namespace GOTHIC_ENGINE { + struct NpcProtectionStatus { + bool immune; + int value; + oEIndexDamage damageIndex; + }; + enum FocusStatusProtectionPlacement { NONE = 0, TOP = 1, - RIGHT = 2 + RIGHT = 2, + CLOSE = 3 }; - class FocusStatusBar : public StatusBar { - private: - zCView* protView; - void MoveFocusBar(int x, int y, oCNpc* npc); - bool TryShowProt(oCNpc* npc); - int GetProtMargin(); - int GetProtSize(); - int GetProtStartX(FocusStatusProtectionPlacement placement); - int GetProtStartY(FocusStatusProtectionPlacement placement); - int CalcProtRenderWidth(std::vector statuses); - FocusStatusProtectionPlacement GetProtPlacement(oCNpc* npc); - virtual void PrintValueOutside(zSTRING str, oCNpc* npc) override; + enum TargetProtectionMode { + Disabled = 0, + CurrentWeapon = 1, + All = 2 + }; + + enum class ProtectionRenderMode { + Disabled, + Immortal, + Normal + }; + struct ProtectionModel { + ProtectionRenderMode renderMode = ProtectionRenderMode::Disabled; + bool isInFightMode = false; + TargetProtectionMode targetProtectionMode = TargetProtectionMode::Disabled; + }; + + struct ProtectionLayout { + FocusStatusProtectionPlacement placement = FocusStatusProtectionPlacement::NONE; + int startX = 0; + int startY = 0; + int iconSize = 0; + int margin = 0; + int fontY = 0; + int totalContentSize = 0; + }; + + struct ProtectionIconRenderData { + zSTRING text; + zSTRING texture; + zCOLOR color; + }; + + class FocusStatusBar : public StatusBar { public: + static bool IsDistanceWeaponDamageTypeOverwritten; + FocusStatusBar(); bool NeedAdjustPosition(int x, int y, oCNpc* npc); - virtual void Loop() override; + virtual bool Init() override; virtual void Clear() override; + virtual void Loop() override; + + private: + static constexpr int ProtectionCount = 7; + static constexpr int ProtectionPlacementRightMargin = 250; + + static const oEIndexDamage ProtectionDamageOrderedIndexes[ProtectionCount]; + const zSTRING CrackedShieldTexture = zSTRING("ICON_PROTECTIONS"); // https://game-icons.net/1x1/lorc/cracked-shield.html + + zCView* protView = nullptr; + + oEIndexDamage activeDamageIndexes[oEDamageIndex_MAX]; // not `PROTECTION_COUNT` because of bound safety + int activeDamageIndexesCount = 0; + + NpcProtectionStatus activeStatuses[oEDamageIndex_MAX]; // not `PROTECTION_COUNT` because of bound safety + int activeStatusesCount = 0; + + ProtectionModel protectionModel = {}; + + int valueOffsetY = 0; + + zSTRING ImmuneAbbreviation = "IMM"; + zSTRING iconCache[oEDamageIndex_MAX + 1]; + + bool BuildProtectionModel(oCNpc* npc); + void FillDamageIndexesBuffer(); + void BuildFightModeDamage(DamageMask& mask); + void BuildNoFightModeDamage(DamageMask& mask); + + void BuildProtectionLayout(ProtectionLayout& layout); + FocusStatusProtectionPlacement GetProtectionPlacement() const; + int GetHorizontalProtMargin(int fontY) const; + int GetVerticalProtMargin(int fontY) const; + + void RenderProtectionWithLayout(const ProtectionLayout& layout); + bool CanRenderProtectionStatus(oCNpc* npc, oEIndexDamage damageIndex) const; + void RenderProtectionIconsClose(const ProtectionLayout& layout); + void RenderProtectionIconsRight(const ProtectionLayout& layout); + void RenderProtectionIconsTop(const ProtectionLayout& layout); + + ProtectionIconRenderData BuildIconRenderData(const NpcProtectionStatus& status, unsigned char alpha); + void MoveFocusBar(int x, int y, oCNpc* npc); + virtual void PrintValueOutside(zSTRING& str) override; + + const zSTRING& GetIconNameByDamageIndex(const oEIndexDamage& index); }; } \ No newline at end of file diff --git a/zUtilities/Headers.h b/zUtilities/Headers.h index e678235..bce6630 100644 --- a/zUtilities/Headers.h +++ b/zUtilities/Headers.h @@ -3,6 +3,7 @@ // Automatically generated block #include +#include #pragma region Includes #include "Const.h" #include "Colors.h" @@ -10,7 +11,7 @@ #include "Randomizer.h" #include "KeyCode.h" #include "IconInfo.h" -#include "NpcHelper.h" +#include "DamageMaskHelper.h" #include "PlayerHelper.h" #include "DebugHelper.h" #include "FocusColor.h" diff --git a/zUtilities/NpcHelper.cpp b/zUtilities/NpcHelper.cpp deleted file mode 100644 index 6d2d5f9..0000000 --- a/zUtilities/NpcHelper.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Supported with union (c) 2020 Union team -// Union SOURCE file - -namespace GOTHIC_ENGINE { - const std::vector PROTECTION_DAMAGE_INDEXES = std::vector({ - oEIndexDamage::oEDamageIndex_Edge, - oEIndexDamage::oEDamageIndex_Blunt, - oEIndexDamage::oEDamageIndex_Point, - oEIndexDamage::oEDamageIndex_Fire, - oEIndexDamage::oEDamageIndex_Magic, - oEIndexDamage::oEDamageIndex_Fly, - oEIndexDamage::oEDamageIndex_Fall - }); - - - bool NpcHelper::CanRenderProtectionStatus(oCNpc* npc, oEIndexDamage damageIndex) - { - if (Options::ShowProtAllDamageTypes || Options::ShowCurrWeapProtOnly) { - return true; - } - - return npc->GetProtectionByIndex(damageIndex) != 0; - } - - std::vector NpcHelper::GetDamageIndexes() { - if (player->IsInFightMode_S(0) && (Options::ShowProtOnlyInFight || Options::ShowCurrWeapProtOnly)) { - return std::vector(); - } - - if (!Options::ShowCurrWeapProtOnly) { - return PROTECTION_DAMAGE_INDEXES; - } - - int playerDmgIndex = player->GetActiveDamageIndex(); - if (!playerDmgIndex) - { - return std::vector(); - } - - auto vec = std::vector(); - vec.push_back((oEIndexDamage)playerDmgIndex); - return vec; - } - - std::vector NpcHelper::GetProtectionVisibleStatuses(oCNpc* npc) { - auto vec = std::vector(); - - if (!Options::ShowTargetProtection) { - return vec; - } - - if (npc->HasFlag(NPC_FLAG_IMMORTAL)) - { - NpcProtectionStatus status; - status.immune = true; - vec.push_back(status); - return vec; - } - - auto damageIndexes = GetDamageIndexes(); - for (auto damageIndex : damageIndexes) - { - if (!CanRenderProtectionStatus(npc, damageIndex)) { - continue; - } - - NpcProtectionStatus status; - status.value = npc->GetProtectionByIndex(damageIndex); - status.damageIndex = damageIndex; - status.immune = status.value < 0; - vec.push_back(status); - } - - return vec; - } - - int NpcHelper::GetProtectionStatusesVisibleCount(oCNpc* npc) { - if (!Options::ShowTargetProtection) { - return 0; - } - - if (npc->HasFlag(NPC_FLAG_IMMORTAL)) { - return 1; - } - - int count = 0; - auto damageIndexes = GetDamageIndexes(); - for (auto damageIndex : damageIndexes) - { - if (!CanRenderProtectionStatus(npc, damageIndex)) { - continue; - } - - count++; - } - - return count; - } -} \ No newline at end of file diff --git a/zUtilities/NpcHelper.h b/zUtilities/NpcHelper.h deleted file mode 100644 index cc78da6..0000000 --- a/zUtilities/NpcHelper.h +++ /dev/null @@ -1,23 +0,0 @@ -// Supported with union (c) 2020 Union team -// Union HEADER file - -namespace GOTHIC_ENGINE { - struct NpcProtectionStatus { - bool immune; - int value; - oEIndexDamage damageIndex; - }; - - class NpcHelper { - - private: - std::vector GetDamageIndexes(); - bool CanRenderProtectionStatus(oCNpc* npc, oEIndexDamage damageIndex); - - public: - std::vector GetProtectionVisibleStatuses(oCNpc* npc); - int GetProtectionStatusesVisibleCount(oCNpc* npc); - }; - - NpcHelper npcHelper; -} \ No newline at end of file diff --git a/zUtilities/Options.h b/zUtilities/Options.h index bde060a..d6b52f7 100644 --- a/zUtilities/Options.h +++ b/zUtilities/Options.h @@ -53,9 +53,14 @@ namespace GOTHIC_ENGINE { zoptions->AddTrivia( PLUGIN_NAME, "ShowSystemTime", "... on screen display of system time (24H format), (0) - 'Disabled', (1) - 'Hour:Minute format', (2) - 'Hour:Minute:Second format'" ); zoptions->AddTrivia( PLUGIN_NAME, "ShowGameTime", "... enables (1) or disables (0) on screen display of in game time" ); zoptions->AddTrivia( PLUGIN_NAME, "ShowMunitionAmount", "... enables (1) or disables (0) on screen display of currently used munition amount" ); - zoptions->AddTrivia( PLUGIN_NAME, "ShowTargetProtection", "... enables for currently equipped weapon (1) or shows all protection stats (2) or disables (0) protection icon and value next to the focused npc hp bar"); - zoptions->AddTrivia( PLUGIN_NAME, "ShowProtOnlyInFight", "... enables (1) or disables (0) showing protection stats only during combat"); - zoptions->AddTrivia( PLUGIN_NAME, "ShowProtAllDamageTypes", "... enables (1) or disables (0) showing all protection stats, even if they are 0" ); + zoptions->AddTrivia( PLUGIN_NAME, "ShowTargetProtectionNoFight", "... specifies mode for showing target protection in no fight mode by" + nline + "... (0) - 'Disabled', (1) - 'CurrentWeapon', (2) - 'All'"); + zoptions->AddTrivia( PLUGIN_NAME, "ShowTargetProtectionInFight", "... specifies mode for showing target protection in fight mode by" + nline + "... (0) - 'Disabled', (1) - 'CurrentWeapon', (2) - 'All'"); + zoptions->AddTrivia( PLUGIN_NAME, "TargetProtectionIconStyle", "... specifies protection icon style, (0) - 'DamagePopup', (1) - 'Shields'" ); + zoptions->AddTrivia( PLUGIN_NAME, "TargetProtectionIconPosition", "... specifies protection icon position, (0) - 'Top', (1) - 'Right'" ); + zoptions->AddTrivia( PLUGIN_NAME, "HideTargetProtectionZeroValues", "... hides protection icons with zero value, (0) - 'Disabled', (1) - 'Enabled'" ); + zoptions->AddTrivia( PLUGIN_NAME, "HideTargetProtectionFallDamage", "... hides protection icon for fall damage, (0) - 'Disabled', (1) - 'Enabled'" ); + zoptions->AddTrivia( PLUGIN_NAME, "HideTargetProtectionFlyDamage", "... hides protection icon for fly damage, (0) - 'Disabled', (1) - 'Enabled'" ); + zoptions->AddTrivia( PLUGIN_NAME, "HideTargetProtectionFireDamage", "... hides protection icon for fire damage, (0) - 'Disabled', (1) - 'Enabled'" ); #if ENGINE >= Engine_G2 zoptions->AddTrivia( PLUGIN_NAME, "ShowPickpocketIcon", "... enables (1) or disables (0) coin icon next to the focused npc name when it can be pickpocketed" ); #endif @@ -102,6 +107,8 @@ namespace GOTHIC_ENGINE { zoptions->AddTrivia( PLUGIN_NAME, "SelectedDialogueColor", "... defines color of selected line in dialogues" + nline + "... use 'R|G|B' or 'R|G|B|A' format" + nline + "... leave empty to use default color" ); zoptions->AddTrivia( PLUGIN_NAME, "SaveReminder", "... Time in minutes after which the reminder to save the game appears on the screen" + nline + "... set to -1 to disable"); + + zoptions->AddTrivia(PLUGIN_NAME, "DistanceWeaponDamageType", "... This value is used to override distance weapon protection icon type. It's maintained by plugin itself. Do not change it."); } } } \ No newline at end of file diff --git a/zUtilities/PlayerStatus.h b/zUtilities/PlayerStatus.h index a1f6a5c..d43bed5 100644 --- a/zUtilities/PlayerStatus.h +++ b/zUtilities/PlayerStatus.h @@ -3,11 +3,12 @@ namespace GOTHIC_ENGINE { namespace Options { - bool ShowGameTime, ShowMunitionAmount, ShowTargetProtection, ShowPickpocketIcon, UseTimeMultiplier, ShowCurrWeapProtOnly, ShowProtOnlyInFight,ShowProtAllDamageTypes; - bool ShowHumanNpcXpRewardIcon; - int ShowSystemTime, KeyTimeMultiplier; + bool ShowGameTime, ShowMunitionAmount, ShowPickpocketIcon, UseTimeMultiplier, ShowHumanNpcXpRewardIcon; + int ShowSystemTime, KeyTimeMultiplier, ShowTargetProtectionNoFight, ShowTargetProtectionInFight, TargetProtectionIconStyle; Array TimeMultipliers; - int SaveReminder; + int TargetProtectionIconPosition, DistanceWeaponDamageTypeFromIni, SaveReminder; + bool HideTargetProtectionZeroValues, HideTargetProtectionFallDamage, HideTargetProtectionFlyDamage, HideTargetProtectionFireDamage; + DamageMask DistanceWeaponDamageType = DamageMask{ oEDamageType::oEDamageType_Point }; // Default type fallback void PlayerStatus() { ShowSystemTime = zoptions->ReadInt( PLUGIN_NAME, "ShowSystemTime", 0 ); @@ -28,15 +29,21 @@ namespace GOTHIC_ENGINE { SaveReminder = zoptions->ReadInt(PLUGIN_NAME, "SaveReminder", 5); - auto showTargetProtectionValue = zoptions->ReadInt(PLUGIN_NAME, "ShowTargetProtection", true); - if (showTargetProtectionValue < 0 || showTargetProtectionValue > 2) { - return; - } + ShowTargetProtectionNoFight = zoptions->ReadInt(PLUGIN_NAME, "ShowTargetProtectionNoFight", TargetProtectionMode::Disabled); + ShowTargetProtectionInFight = zoptions->ReadInt(PLUGIN_NAME, "ShowTargetProtectionInFight", TargetProtectionMode::CurrentWeapon); + TargetProtectionIconStyle = zoptions->ReadInt(PLUGIN_NAME, "TargetProtectionIconStyle", 0); + TargetProtectionIconPosition = zoptions->ReadInt(PLUGIN_NAME, "TargetProtectionIconPosition", 0); + HideTargetProtectionZeroValues = zoptions->ReadBool(PLUGIN_NAME, "HideTargetProtectionZeroValues", false); + HideTargetProtectionFallDamage = zoptions->ReadBool(PLUGIN_NAME, "HideTargetProtectionFallDamage", false); + HideTargetProtectionFlyDamage = zoptions->ReadBool(PLUGIN_NAME, "HideTargetProtectionFlyDamage", false); + HideTargetProtectionFireDamage = zoptions->ReadBool(PLUGIN_NAME, "HideTargetProtectionFireDamage", false); - ShowTargetProtection = showTargetProtectionValue >= 1; - ShowCurrWeapProtOnly = showTargetProtectionValue == 1; - ShowProtOnlyInFight = zoptions->ReadBool(PLUGIN_NAME, "ShowProtOnlyInFight", true); - ShowProtAllDamageTypes = zoptions->ReadBool(PLUGIN_NAME, "ShowProtAllDamageTypes", false); + // when value form ini is 0, do not override default to avoid showing nothing for distance weapons + if (DistanceWeaponDamageTypeFromIni = zoptions->ReadInt(PLUGIN_NAME, "DistanceWeaponDamageType", 0)) { + for (const auto& entry : DAMAGE_MAP) { + DistanceWeaponDamageType[entry.index] = (DistanceWeaponDamageTypeFromIni & entry.type) != 0; + } + } } } diff --git a/zUtilities/Sources.h b/zUtilities/Sources.h index 937f160..3e6fb60 100644 --- a/zUtilities/Sources.h +++ b/zUtilities/Sources.h @@ -6,7 +6,7 @@ #include "Misc.cpp" #include "User.cpp" #include "Commands.cpp" -#include "NpcHelper.cpp" +#include "DamageMaskHelper.cpp" #include "IconInfo.cpp" #include "DebugHelper.cpp" #include "FocusColor.cpp" diff --git a/zUtilities/StatusBar.cpp b/zUtilities/StatusBar.cpp index 720fd5d..9d20b32 100644 --- a/zUtilities/StatusBar.cpp +++ b/zUtilities/StatusBar.cpp @@ -112,36 +112,34 @@ namespace GOTHIC_ENGINE { return Z(int)bar->currentValue + "/" + Z(int)bar->maxHigh; } - void StatusBar::PrintValue( oCNpc* npc ) { - if ( !Options::StatusBarValueMode ) + void StatusBar::PrintValue() { + if (!Options::StatusBarValueMode) return; - del( valueView ); + del(valueView); - if ( !IsBarActive() ) + if (!IsBarActive()) return; - valueView = new zCView( 0, 0, 8192, 8192 ); - - auto str = GetBarValue(); + valueView = new zCView(0, 0, 8192, 8192); + zSTRING str = GetBarValue(); - if (name && name.Length()) { + if (name && name.Length()) str = name + ": " + str; - } - zCView* ownerView = (Options::StatusBarValueMode == Inside) ? bar->range_bar : screen; - ownerView->InsertItem( valueView ); + const bool inside = (Options::StatusBarValueMode == Inside); + zCView* ownerView = inside ? bar->range_bar : screen; - if ( Options::StatusBarValueMode != Inside ) { - PrintValueOutside(str, npc); - return; - } + ownerView->InsertItem(valueView); - valueView->PrintCXY( str ); + if (inside) + valueView->PrintCXY(str); + else + PrintValueOutside(str); } - void StatusBar::PrintValueOutside(zSTRING str, oCNpc* npc) { + void StatusBar::PrintValueOutside(zSTRING& str) { int offsetY = bar->vsizey / 2 + valueView->FontY(); int x = bar->vposx + bar->vsizex / 2 - valueView->FontSize(str) / 2; int y = bar->vposy; @@ -207,7 +205,7 @@ namespace GOTHIC_ENGINE { ChangeBarPos(); PredictRestore(); - PrintValue( player ); + PrintValue(); } StatusBar::StatusBar( oCViewStatusBar* bar ) { diff --git a/zUtilities/StatusBar.h b/zUtilities/StatusBar.h index cd1a830..87a7bde 100644 --- a/zUtilities/StatusBar.h +++ b/zUtilities/StatusBar.h @@ -39,8 +39,8 @@ namespace GOTHIC_ENGINE { zCArray symbols; zSTRING name; Array userPos; - void PrintValue(oCNpc* npc); - virtual void PrintValueOutside(zSTRING str, oCNpc* npc); + void PrintValue(); + virtual void PrintValueOutside(zSTRING& str); bool IsBarActive(); StatusBar(oCViewStatusBar* bar); virtual zSTRING GetBarValue(); diff --git a/zUtilities/ZenGin/Gothic_UserAPI/oCNpc.inl b/zUtilities/ZenGin/Gothic_UserAPI/oCNpc.inl index 108f4f4..da00af5 100644 --- a/zUtilities/ZenGin/Gothic_UserAPI/oCNpc.inl +++ b/zUtilities/ZenGin/Gothic_UserAPI/oCNpc.inl @@ -5,6 +5,8 @@ void __thiscall OnChrzonszcz( zCVob* ); void OnDamage_Hit_Union( oSDamageDescriptor& ); +void OnDamage_Hit_DistanceWeapon( oSDamageDescriptor& ); +void EquipItem_Union( oCItem* ); int GetAivar( zSTRING aivar ); int EV_UseItemToState_Union( oCMsgManipulate* ); int GetFistDamageIndex(); diff --git a/zUtilities/zUtilities.vcxproj b/zUtilities/zUtilities.vcxproj index 95bdc09..372005f 100644 --- a/zUtilities/zUtilities.vcxproj +++ b/zUtilities/zUtilities.vcxproj @@ -869,6 +869,14 @@ + + + + + + + + @@ -986,14 +994,6 @@ - - - - - - - - diff --git a/zUtilities/zUtilities.vcxproj.filters b/zUtilities/zUtilities.vcxproj.filters index d8a4140..16bf70f 100644 --- a/zUtilities/zUtilities.vcxproj.filters +++ b/zUtilities/zUtilities.vcxproj.filters @@ -250,6 +250,9 @@ {ad1d68b4-22dc-4e58-914f-444545260f86} + + {9d696232-6486-45d7-a379-af59a6476b5c} + @@ -2916,20 +2919,24 @@ Plugin\Workspace\Misc - - - + Plugin\Workspace\Misc - + Plugin\Workspace\Misc + + Plugin\Workspace\Features\PlayerStatus\FocusStatusBar + + + Plugin\Workspace\Features\PlayerStatus\FocusStatusBar +