diff --git a/Infrastructure/include/graphics/math.h b/Infrastructure/include/graphics/math.h index b4a03112b..b2ad0caee 100644 --- a/Infrastructure/include/graphics/math.h +++ b/Infrastructure/include/graphics/math.h @@ -62,4 +62,5 @@ struct TigRect { void FitInto(const TigRect &boundingRect); bool Intersects(const TigRect &other); bool Intersects(const TigRect &other, TigRect &intersection); + bool ContainsPoint(int px, int py); }; diff --git a/TemplePlus/d20_class.cpp b/TemplePlus/d20_class.cpp index d3b8f15db..a580faa3a 100644 --- a/TemplePlus/d20_class.cpp +++ b/TemplePlus/d20_class.cpp @@ -453,6 +453,10 @@ bool D20ClassSystem::IsSelectingFeatsOnLevelup(objHndl handle, Stat classEnum){ return pythonClassIntegration.IsSelectingFeatsOnLevelup(handle, classEnum); } +void D20ClassSystem::LevelupGetBonusFeats(objHndl handle, Stat classEnum){ + pythonClassIntegration.LevelupGetBonusFeats(handle, classEnum); +} + bool D20ClassSystem::IsSelectingSpellsOnLevelup(objHndl handle, Stat classEnum){ return pythonClassIntegration.IsSelectingSpellsOnLevelup(handle, classEnum); diff --git a/TemplePlus/d20_class.h b/TemplePlus/d20_class.h index 8f539c39e..25d78b86c 100644 --- a/TemplePlus/d20_class.h +++ b/TemplePlus/d20_class.h @@ -150,6 +150,7 @@ struct D20ClassSystem : temple::AddressTable // Levelup bool IsSelectingFeatsOnLevelup(objHndl handle, Stat classEnum); + void LevelupGetBonusFeats( objHndl handle, Stat classEnum); bool IsSelectingSpellsOnLevelup(objHndl handle, Stat classEnum); void LevelupInitSpellSelection(objHndl handle, Stat classEnum); diff --git a/TemplePlus/python/python_integration_class_spec.cpp b/TemplePlus/python/python_integration_class_spec.cpp index a95e8f500..b15dad6b4 100644 --- a/TemplePlus/python/python_integration_class_spec.cpp +++ b/TemplePlus/python/python_integration_class_spec.cpp @@ -200,6 +200,19 @@ bool PythonClassSpecIntegration::IsSelectingFeatsOnLevelup(objHndl handle, Stat } +void PythonClassSpecIntegration::LevelupGetBonusFeats(objHndl handle, Stat classEnum){ + auto classSpecEntry = mScripts.find(classEnum); + if (classSpecEntry == mScripts.end()) + return; + + auto attachee = PyObjHndl_Create(handle); + auto args = Py_BuildValue("(O)", attachee); + Py_DECREF(attachee); + + RunScriptDefault0(classSpecEntry->second.id, (EventId)ClassSpecFunc::LevelupGetBonusFeats, args) ; + Py_DECREF(args); +} + bool PythonClassSpecIntegration::IsSelectingSpellsOnLevelup(objHndl handle, Stat classEnum){ auto classSpecEntry = mScripts.find(classEnum); if (classSpecEntry == mScripts.end()) @@ -265,6 +278,7 @@ static std::map classSpecFunctions = { { ClassSpecFunc::GetFeats,"GetClassFeats" }, { ClassSpecFunc::IsSelectingFeatsOnLevelup, "IsSelectingFeatsOnLevelup" }, + { ClassSpecFunc::LevelupGetBonusFeats, "LevelupGetBonusFeats" }, { ClassSpecFunc::LevelupCheckSpells, "LevelupCheckSpells" }, { ClassSpecFunc::IsSelectingSpellsOnLevelup, "IsSelectingSpellsOnLevelup" }, diff --git a/TemplePlus/python/python_integration_class_spec.h b/TemplePlus/python/python_integration_class_spec.h index 64ce9171c..2ca68adef 100644 --- a/TemplePlus/python/python_integration_class_spec.h +++ b/TemplePlus/python/python_integration_class_spec.h @@ -28,6 +28,7 @@ enum class ClassSpecFunc : int { // levelup callbacks IsSelectingFeatsOnLevelup, + LevelupGetBonusFeats, LevelupCheckSpells, IsSelectingSpellsOnLevelup, @@ -62,6 +63,7 @@ class PythonClassSpecIntegration : public PythonIntegration { // levelup bool IsSelectingFeatsOnLevelup(objHndl handle, Stat classEnum); + void LevelupGetBonusFeats(objHndl handle, Stat classEnum); bool IsSelectingSpellsOnLevelup(objHndl handle, Stat classEnum); void LevelupInitSpellSelection(objHndl handle, Stat classEnum); diff --git a/TemplePlus/temple_enums.h b/TemplePlus/temple_enums.h index 4f2a0af35..4087da399 100644 --- a/TemplePlus/temple_enums.h +++ b/TemplePlus/temple_enums.h @@ -681,6 +681,7 @@ enum feat_enums : int { FEAT_GREATER_RAGE = 675, FEAT_GREATER_WEAPON_SPECIALIZATION_GAUNTLET = 676, FEAT_GREATER_WEAPON_SPECIALIZATION_UNARMED_STRIKE_MEDIUM_SIZED_BEING = 677, + FEAT_GREATER_WEAPON_SPECIALIZATION_BASTARD_SWORD = 733, /// ... rest of Greater Weapon Specializations ... FEAT_GREATER_WEAPON_SPECIALIZATION_GRAPPLE = 746, FEAT_TIRELESS_RAGE = 747, diff --git a/TemplePlus/tig/tig.cpp b/TemplePlus/tig/tig.cpp index 7641103c5..c6519ef2f 100644 --- a/TemplePlus/tig/tig.cpp +++ b/TemplePlus/tig/tig.cpp @@ -1,3 +1,4 @@ +#include "..\..\Infrastructure\include\graphics\math.h" #include "stdafx.h" #include "tig.h" #include "tig_tabparser.h" @@ -273,6 +274,10 @@ bool TigRect::Intersects(const TigRect& other, TigRect& intersection) { return true; } +bool TigRect::ContainsPoint(int pX, int pY){ + return (pX >= x && pX <= x + width && pY >= y && pY <= y + height); +} + RECT TigRect::ToRect() { return{x, y, x + width, y + height}; } diff --git a/TemplePlus/ui/ui_char_editor.cpp b/TemplePlus/ui/ui_char_editor.cpp index 467a93823..8384f975c 100644 --- a/TemplePlus/ui/ui_char_editor.cpp +++ b/TemplePlus/ui/ui_char_editor.cpp @@ -125,6 +125,7 @@ class UiCharEditor { BOOL FeatsWndMsg(int widId, TigMsg* msg); void FeatsWndRender(int widId); + BOOL FeatsEntryBtnMsg(int widId, TigMsg* msg); void FeatsEntryBtnRender(int widId); BOOL FeatsExistingBtnMsg(int widId, TigMsg* msg); @@ -167,10 +168,15 @@ class UiCharEditor { bool SpellIsForbidden(int spEnum); bool SpellIsAlreadyKnown(int spEnum, int spellClass); std::string GetFeatName(feat_enums feat); // includes strings for Mutli-selection feat categories e.g. FEAT_WEAPON_FOCUS + TigTextStyle & GetFeatStyle(feat_enums feat, bool allowMultiple = true); bool FeatAlreadyPicked(feat_enums feat); bool FeatCanPick(feat_enums feat); bool IsSelectingRangerSpec(); bool IsClassBonusFeat(feat_enums feat); + void SetBonusFeats(std::vector & fti); + void FeatsSanitize(); + void FeatsMultiSelectActivate(feat_enums feat); + feat_enums FeatsMultiGetFirst(feat_enums feat); // first alphabetical // widget IDs int classWndId = 0; @@ -286,7 +292,7 @@ class UiCharEditor { std::unique_ptr mClass; std::vector mSpellInfo; std::vector mAvailableSpells; // spells available for learning - std::vector mExistingFeats, mSelectableFeats, mMultiSelectFeats, mMultiSelectMasterFeats; + std::vector mExistingFeats, mSelectableFeats, mMultiSelectFeats, mMultiSelectMasterFeats, mBonusFeats; //std::unique_ptr mStats; //std::unique_ptr mFeatures; //std::unique_ptr mSkills; @@ -392,6 +398,9 @@ PYBIND11_PLUGIN(tp_char_editor){ ; // methods mm + .def("set_bonus_feats", [](std::vector & fti){ + uiCharEditor.SetBonusFeats(fti); + }) .def("get_spell_enums", []()->std::vector& { return uiCharEditor.GetKnownSpellInfo(); }) @@ -549,13 +558,14 @@ void UiCharEditor::BtnStatesUpdate(int systemId){ ui.ButtonSetButtonState(stateBtnIds[4], UBS_NORMAL); + mIsSelectingBonusFeat = false; // feats and features if (classCode >= stat_level_barbarian){ auto classLvlNew = GetNewLvl(classCode); if (d20ClassSys.IsSelectingFeatsOnLevelup(handle, classCode) ) { - ui.ButtonSetButtonState(stateBtnIds[4], UBS_NORMAL); + ui.ButtonSetButtonState(stateBtnIds[4], UBS_NORMAL); // feats mIsSelectingBonusFeat = true; } @@ -814,11 +824,10 @@ BOOL UiCharEditor::FeatsCheckComplete(){ auto &selPkt = GetCharEditorSelPacket(); // is a 3rd level and no feat chosen - auto newLvl = objects.StatLevelGet(handle, stat_level) + 1; - if (!(newLvl % 3) && selPkt.feat0 == FEAT_NONE) + if (IsSelectingNormalFeat() && selPkt.feat0 == FEAT_NONE) return 0; - if (IsSelectingBonusFeat() && !mBonusFeatOk) // the logic will be handled in the msg callbacks & Python API now + if (IsSelectingBonusFeat() && selPkt.feat2 == FEAT_NONE) // the logic will be handled in the msg callbacks & Python API now return 0; return 1; @@ -830,6 +839,7 @@ void UiCharEditor::FeatsFinalize() void UiCharEditor::FeatsReset(CharEditorSelectionPacket & selPkt){ mFeatsActivated = false; + mIsSelectingBonusFeat = false; selPkt.feat0 = FEAT_NONE; selPkt.feat1 = FEAT_NONE; @@ -1131,6 +1141,9 @@ void UiCharEditor::FeatsActivate(){ auto &selPkt = GetCharEditorSelPacket(); mIsSelectingBonusFeat = d20ClassSys.IsSelectingFeatsOnLevelup(handle, selPkt.classCode); + mBonusFeats.clear(); + if (mIsSelectingBonusFeat) + d20ClassSys.LevelupGetBonusFeats(handle, selPkt.classCode); feat_enums existingFeats[122]; auto isRangerStyleChoosing = selPkt.classCode == stat_level_ranger && (objects.StatLevelGet(handle, stat_level_ranger) == 1); @@ -1617,34 +1630,12 @@ void UiCharEditor::FeatsWndRender(int widId){ RenderHooks::RenderRectInt(featsMainWnd.x + 3, featsMainWnd.y + 36, 185, 227, 0xFF5D5D5D); UiRenderer::DrawTextInWidget(widId, featsAvailTitleString, featsAvailTitleRect, whiteTextGenericStyle); - auto getFeatStyle = [](feat_enums feat, bool allowMultiple = true){ - auto newLvl = uiCharEditor.GetNewLvl(); - if ( (allowMultiple || !uiCharEditor.FeatAlreadyPicked(feat) ) - && uiCharEditor.FeatCanPick(feat) ) - { - if (uiCharEditor.featsMultiSelected == feat){ - return uiCharEditor.blueTextStyle; - } - if (feats.IsClassFeat(feat)){ // class Specific feat - return uiCharEditor.featsClassStyle; - } - else if (uiCharEditor.IsClassBonusFeat(feat)) // is choosing class bonus right now - { - return uiCharEditor.featsGoldenStyle; - } - else - return uiCharEditor.featsBonusTextStyle; - } - - return uiCharEditor.featsGreyedStyle; - }; - // Feat Slot if (IsSelectingNormalFeat()){ RenderHooks::RenderRectInt(featsSelectedBorderRect.x , featsSelectedBorderRect.y, featsSelectedBorderRect.width, featsSelectedBorderRect.height, 0xFFFFffff); UiRenderer::DrawTextInWidget(widId, featsTitleString, featsTitleRect, featsBonusTextStyle); if (selPkt.feat0 != FEAT_NONE){ - UiRenderer::DrawTextInWidget(widId, GetFeatName(selPkt.feat0), feat0TextRect ,getFeatStyle(selPkt.feat0)); + UiRenderer::DrawTextInWidget(widId, GetFeatName(selPkt.feat0), feat0TextRect ,GetFeatStyle(selPkt.feat0)); } } @@ -1655,7 +1646,7 @@ void UiCharEditor::FeatsWndRender(int widId){ UiRenderer::DrawTextInWidget(widId, featsClassBonusTitleString, featsClassBonusRect, featsGoldenStyle); if (selPkt.feat2 != FEAT_NONE){ - UiRenderer::DrawTextInWidget(widId, GetFeatName(selPkt.feat2), feat2TextRect, getFeatStyle(selPkt.feat2)); + UiRenderer::DrawTextInWidget(widId, GetFeatName(selPkt.feat2), feat2TextRect, GetFeatStyle(selPkt.feat2)); } } @@ -1670,48 +1661,181 @@ void UiCharEditor::FeatsWndRender(int widId){ BOOL UiCharEditor::FeatsWndMsg(int widId, TigMsg * msg) { - return 0; -} + if (msg->type == TigMsgType::WIDGET) { + auto msgW = (TigMsgWidget*)msg; + if (msgW->widgetEventType == TigMsgWidgetEvent::Scrolled) { + ui.ScrollbarGetY(featsScrollbarId, &featsScrollbarY); + ui.ScrollbarGetY(featsExistingScrollbarId, &featsExistingScrollbarY); + } + return FALSE; + } -BOOL UiCharEditor::FeatsEntryBtnMsg(int widId, TigMsg * msg) -{ - return 0; + if (msg->type != TigMsgType::MOUSE) + return FALSE; + + + auto msgM = (TigMsgMouse*)msg; + auto &selPkt = GetCharEditorSelPacket(); + + if (msgM->buttonStateFlags & MouseStateFlags::MSF_RMB_RELEASED && ui.IsWidgetHidden(featsMultiSelectWndId)) { + + auto putFeat = false; + feat_enums feat; + + // cycle thru widgets to find the one where the RMB happened + for (auto i=0; i < FEATS_AVAIL_BTN_COUNT; i++){ + if (!featsBtnRects[i].ContainsPoint(msgM->x - featsMainWnd.x, msgM->y - featsMainWnd.y)) + continue; + + auto featIdx = i + featsScrollbarY; + if (featIdx >= (int)mSelectableFeats.size()) + break; + + feat = (feat_enums)mSelectableFeats[featIdx].featEnum; + + + if (IsSelectingNormalFeat() && selPkt.feat0 == FEAT_NONE){ + selPkt.feat0 = feat; + putFeat = true; + break; + } + else if (IsSelectingBonusFeat() && selPkt.feat2 == FEAT_NONE) + { + selPkt.feat2 = feat; + putFeat = true; + break; + } + } + if (putFeat){ + + if (feats.IsFeatMultiSelectMaster(feat)){ + FeatsMultiSelectActivate(feat); + } + FeatsSanitize(); + } + + if (featsSelectedBorderRect.ContainsPoint(msgM->x, msgM->y)){ + selPkt.feat0 = FEAT_NONE; + } + else if (featsClassBonusRect.ContainsPoint(msgM->x, msgM->y) && IsSelectingBonusFeat()){ + selPkt.feat2 = FEAT_NONE; + } + + } + + if (!(msgM->buttonStateFlags & MouseStateFlags::MSF_SCROLLWHEEL_CHANGE)) + return TRUE; + + TigMsgMouse msgCopy = *msgM; + msgCopy.buttonStateFlags = MouseStateFlags::MSF_SCROLLWHEEL_CHANGE; + + if ((int)msgM->x >= featsMainWnd.x + 3 && (int)msgM->x <= featsMainWnd.x + 188 + && (int)msgM->y >= featsMainWnd.y +36 && (int)msgM->y <= featsMainWnd.y + 263) { + ui.WidgetCopy(featsScrollbarId, &featsScrollbar); + if (featsScrollbar.handleMessage) + return featsScrollbar.handleMessage(featsScrollbarId, (TigMsg*)&msgCopy); + } + + if ((int)msgM->x >= featsMainWnd.x + 207 && (int)msgM->x <= featsMainWnd.x + 392 + && (int)msgM->y >= featsMainWnd.y + 118 && (int)msgM->y <= featsMainWnd.y + 263) { + ui.WidgetCopy(featsExistingScrollbarId, &featsExistingScrollbar); + if (featsExistingScrollbar.handleMessage) + return featsExistingScrollbar.handleMessage(featsExistingScrollbarId, (TigMsg*)&msgCopy); + } + + return FALSE; } -void UiCharEditor::FeatsEntryBtnRender(int widId){ +BOOL UiCharEditor::FeatsEntryBtnMsg(int widId, TigMsg * msg){ + + if (msg->type != TigMsgType::WIDGET) + return 0; + auto msgW = (TigMsgWidget*)msg; auto widIdx = ui.WidgetlistIndexof(widId, &featsAvailBtnIds[0], FEATS_AVAIL_BTN_COUNT); auto featIdx = widIdx + featsScrollbarY; if (widIdx == -1 || featIdx >= (int)mSelectableFeats.size()) - return; + return FALSE; auto featInfo = mSelectableFeats[featIdx]; auto feat = (feat_enums)featInfo.featEnum; auto &selPkt = GetCharEditorSelPacket(); + auto btn = ui.GetButton(widId); + + switch (msgW->widgetEventType){ + case TigMsgWidgetEvent::Clicked: + if (!FeatAlreadyPicked(feat) && FeatCanPick(feat)){ + auto origX = msgW->x - btn->x, origY = msgW->y - btn->y; + auto style = uiCharEditor.GetFeatStyle(feat); + auto featCallback = [origX, origY, feat, style](int x, int y) { + std::string text(uiCharEditor.GetFeatName(feat)); + UiRenderer::PushFont(PredefinedFont::PRIORY_12); + TigRect rect(x - origX, y - origY, 180, uiCharEditor.FEATS_AVAIL_BTN_HEIGHT); + tigFont.Draw(text.c_str(), rect, style); + UiRenderer::PopFont(); + }; + mouseFuncs.SetCursorDrawCallback(featCallback, (uint32_t)&featCallback); - auto newLvl = GetNewLvl(selPkt.classCode); - auto style = featsBonusTextStyle; - if (FeatAlreadyPicked(feat) || !FeatCanPick(feat)) { - style = featsGreyedStyle; - } - else if (featsMultiSelected == feat) - { - style = blueTextStyle; - } - else - { - if (feats.IsClassFeat(feat)) - { - style = featsClassStyle; } - else if (IsClassBonusFeat(feat)) { - style = featsGoldenStyle; + return TRUE; + case TigMsgWidgetEvent::MouseReleased: + if (helpSys.IsClickForHelpActive()){ + mouseFuncs.SetCursorDrawCallback(nullptr, 0); + helpSys.PresentWikiHelp(109 + feat); + return TRUE; } - } + case TigMsgWidgetEvent::MouseReleasedAtDifferentButton: + if (FeatAlreadyPicked(feat) || !FeatCanPick(feat)) + return TRUE; + mouseFuncs.SetCursorDrawCallback(nullptr, 0); + + // check if inserted into the normal slot + if (featsSelectedBorderRect.ContainsPoint(msgW->x, msgW->y) && IsSelectingNormalFeat()){ + selPkt.feat0 = feat; + if (feats.IsFeatMultiSelectMaster(feat)) + FeatsMultiSelectActivate(feat); + } + // check if inserted into the bonus slot + else if (IsSelectingBonusFeat() + && featsClassBonusBorderRect.ContainsPoint(msgW->x, msgW->y) && IsClassBonusFeat(feat)){ + selPkt.feat2 = feat; + if (feats.IsFeatMultiSelectMaster(feat)) + FeatsMultiSelectActivate(feat); + } + FeatsSanitize(); + return TRUE; + case TigMsgWidgetEvent::Entered: + temple::GetRef(0x10162A10)(FeatsMultiGetFirst(feat), temple::GetRef(0x10C76B48), 1024u); // UiTooltipSetForFeat + temple::GetRef(0x10162C00)(temple::GetRef(0x10C76B48)); // UiCharTextboxSet + return TRUE; + case TigMsgWidgetEvent::Exited: + temple::GetRef(0x10162C00)(""); // UiCharTextboxSet + return TRUE; + default: + return FALSE; + + } + return TRUE; +} + + + + + +void UiCharEditor::FeatsEntryBtnRender(int widId){ + + auto widIdx = ui.WidgetlistIndexof(widId, &featsAvailBtnIds[0], FEATS_AVAIL_BTN_COUNT); + auto featIdx = widIdx + featsScrollbarY; + if (widIdx == -1 || featIdx >= (int)mSelectableFeats.size()) + return; + + auto featInfo = mSelectableFeats[featIdx]; + auto feat = (feat_enums)featInfo.featEnum; + UiRenderer::PushFont(PredefinedFont::PRIORY_12); - UiRenderer::DrawTextInWidget(featsMainWndId, GetFeatName(feat), featsBtnRects[widIdx], style); + UiRenderer::DrawTextInWidget(featsMainWndId, GetFeatName(feat), featsBtnRects[widIdx], GetFeatStyle(feat, false)); UiRenderer::PopFont(); } @@ -2205,7 +2329,7 @@ void UiCharEditor::ClassSetPermissibles(){ bool UiCharEditor::IsSelectingNormalFeat(){ auto handle = GetEditedChar(); - auto newLvl = objects.StatLevelGet(handle, stat_level)+1; + auto newLvl = GetNewLvl(); return (newLvl % 3) == 0; } @@ -2340,6 +2464,31 @@ std::string UiCharEditor::GetFeatName(feat_enums feat){ } +TigTextStyle & UiCharEditor::GetFeatStyle(feat_enums feat, bool allowMultiple){ + auto &selPkt = GetCharEditorSelPacket(); + auto newLvl = uiCharEditor.GetNewLvl(selPkt.classCode); + + if ( (allowMultiple || !uiCharEditor.FeatAlreadyPicked(feat)) + && uiCharEditor.FeatCanPick(feat)) + { + if (uiCharEditor.featsMultiSelected == feat) { + return uiCharEditor.blueTextStyle; + } + if (feats.IsClassFeat(feat)) { // class Specific feat + return uiCharEditor.featsClassStyle; + } + else if (uiCharEditor.IsClassBonusFeat(feat)) // is choosing class bonus right now + { + return uiCharEditor.featsGoldenStyle; + } + else + return uiCharEditor.featsBonusTextStyle; + } + + return uiCharEditor.featsGreyedStyle; + +} + bool UiCharEditor::FeatAlreadyPicked(feat_enums feat){ if (feats.IsFeatPropertySet(feat, 0x1) // can be gained multiple times || feats.IsFeatMultiSelectMaster(feat)) @@ -2371,7 +2520,7 @@ bool UiCharEditor::FeatCanPick(feat_enums feat){ // TODO extend the specials if (feat == FEAT_IMPROVED_TRIP || feat == FEAT_IMPROVED_DISARM){ - if (selPkt.classCode == stat_level_monk && objects.StatLevelGet(handle, stat_level_monk) == 6) + if (selPkt.classCode == stat_level_monk && GetNewLvl(stat_level_monk) == 6) return true; } if (!feats.IsFeatPartOfMultiselect(feat)){ @@ -2445,10 +2594,17 @@ bool UiCharEditor::IsSelectingRangerSpec() } bool UiCharEditor::IsClassBonusFeat(feat_enums feat){ - // TODO generalize + // mBonusFeats is delivered via the python class API + for (auto it : mBonusFeats) { + if (it.featEnum == feat) + return true; + } + + // the old stuff auto &selPkt = GetCharEditorSelPacket(); auto newLvl = GetNewLvl(selPkt.classCode); + switch(selPkt.classCode){ case stat_level_fighter: return feats.IsFighterFeat(feat); @@ -2471,6 +2627,67 @@ bool UiCharEditor::IsClassBonusFeat(feat_enums feat){ } } +void UiCharEditor::SetBonusFeats(std::vector& fti){ + mBonusFeats.clear(); + for (auto it : fti) { + uiCharEditor.mBonusFeats.push_back(it); + } +} + +void UiCharEditor::FeatsSanitize(){ + auto &selPkt = GetCharEditorSelPacket(); + + for (auto i=0; i < 3; i++){ // check if any of the feat now lack the prereq (due to user removal). loop three times to ensure up-to-date state. + if (selPkt.feat0 != FEAT_NONE && !FeatCanPick(selPkt.feat0)) + selPkt.feat0 = FEAT_NONE; + if (selPkt.feat1 != FEAT_NONE && !FeatCanPick(selPkt.feat1)) { + selPkt.feat1 = FEAT_NONE; + } + if (selPkt.feat2 != FEAT_NONE && !FeatCanPick(selPkt.feat2) && !IsSelectingRangerSpec()) + selPkt.feat2 = FEAT_NONE; + } + + +} + +void UiCharEditor::FeatsMultiSelectActivate(feat_enums feat){ + + auto &selPkt = GetCharEditorSelPacket(); + if (feat == FEAT_WEAPON_FINESSE){ + if (selPkt.feat0 == FEAT_WEAPON_FINESSE) + selPkt.feat0 = FEAT_WEAPON_FINESSE_DAGGER; + if (selPkt.feat1 == FEAT_WEAPON_FINESSE) + selPkt.feat1 = FEAT_WEAPON_FINESSE_DAGGER; + if (selPkt.feat2 == FEAT_WEAPON_FINESSE) + selPkt.feat2 = FEAT_WEAPON_FINESSE_DAGGER; + return; + } + + ui.WidgetBringToFront(featsMultiSelectWndId); +} + +feat_enums UiCharEditor::FeatsMultiGetFirst(feat_enums feat){ + switch(feat) + { + case FEAT_EXOTIC_WEAPON_PROFICIENCY: + return FEAT_EXOTIC_WEAPON_PROFICIENCY_BASTARD_SWORD; + case FEAT_IMPROVED_CRITICAL: + return FEAT_IMPROVED_CRITICAL_BASTARD_SWORD; + case FEAT_MARTIAL_WEAPON_PROFICIENCY: + return FEAT_MARTIAL_WEAPON_PROFICIENCY_BATTLEAXE; + case FEAT_SKILL_FOCUS: + return FEAT_SKILL_FOCUS_APPRAISE; + case FEAT_GREATER_WEAPON_FOCUS: + return FEAT_GREATER_WEAPON_FOCUS_BASTARD_SWORD; + case FEAT_WEAPON_SPECIALIZATION: + return FEAT_WEAPON_SPECIALIZATION_BASTARD_SWORD; + case FEAT_GREATER_WEAPON_SPECIALIZATION: + return FEAT_GREATER_WEAPON_SPECIALIZATION_BASTARD_SWORD; + default: + return feat; + } +} + class UiCharEditorHooks : public TempleFix { diff --git a/tpdata/templeplus/lib/templeplus/constants.py b/tpdata/templeplus/lib/templeplus/constants.py index f2c70845f..75f475414 100644 --- a/tpdata/templeplus/lib/templeplus/constants.py +++ b/tpdata/templeplus/lib/templeplus/constants.py @@ -1497,7 +1497,6 @@ feat_none = 649 feat_exotic_weapon_proficiency_head = 650 feat_improved_critical_head = 651 -feat_exotic_weapon_proficiency_head = 652 feat_martial_weapon_proficiency_head = 652 feat_skill_focus_head = 653 feat_weapon_finesse_head = 654 diff --git a/tpdata/tpgamefiles.dat b/tpdata/tpgamefiles.dat index 6d65c3e66..8e922073a 100644 Binary files a/tpdata/tpgamefiles.dat and b/tpdata/tpgamefiles.dat differ