From 9f9798d3a4377d8021ee686af48d1f442190644b Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Mon, 2 Jan 2023 14:11:02 +0200 Subject: [PATCH 01/26] Update android JNI bindings --- client/Client.cpp | 8 ++++++- client/widgets/TextControls.cpp | 37 --------------------------------- client/widgets/TextControls.h | 3 --- lib/CAndroidVMHelper.cpp | 5 ----- lib/CAndroidVMHelper.h | 2 -- 5 files changed, 7 insertions(+), 48 deletions(-) diff --git a/client/Client.cpp b/client/Client.cpp index 5ee3c6cb0..ded4b4d82 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -770,8 +770,14 @@ void CClient::reinitScripting() #endif } - #ifdef VCMI_ANDROID +extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_clientSetupJNI(JNIEnv * env, jobject cls) +{ + logNetwork->info("Received clientSetupJNI"); + + CAndroidVMHelper::cacheVM(env); +} + extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerClosed(JNIEnv * env, jobject cls) { logNetwork->info("Received server closed signal"); diff --git a/client/widgets/TextControls.cpp b/client/widgets/TextControls.cpp index 3fdf070e3..7dff297a8 100644 --- a/client/widgets/TextControls.cpp +++ b/client/widgets/TextControls.cpp @@ -488,9 +488,6 @@ CKeyboardFocusListener::CKeyboardFocusListener(CTextInput * textInput) void CKeyboardFocusListener::focusGot() { CSDL_Ext::startTextInput(&textInput->pos); -#ifdef VCMI_ANDROID - textInput->notifyAndroidTextInputChanged(textInput->text); -#endif usageIndex++; } @@ -552,9 +549,6 @@ void CTextInput::keyPressed(const SDL_KeyboardEvent & key) { redraw(); cb(text); -#ifdef VCMI_ANDROID - notifyAndroidTextInputChanged(text); -#endif } } @@ -563,10 +557,6 @@ void CTextInput::setText(const std::string & nText, bool callCb) CLabel::setText(nText); if(callCb) cb(text); - -#ifdef VCMI_ANDROID - notifyAndroidTextInputChanged(text); -#endif } bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key) @@ -592,10 +582,6 @@ void CTextInput::textInputed(const SDL_TextInputEvent & event) cb(text); } newText.clear(); - -#ifdef VCMI_ANDROID - notifyAndroidTextInputChanged(text); -#endif } void CTextInput::textEdited(const SDL_TextEditingEvent & event) @@ -606,11 +592,6 @@ void CTextInput::textEdited(const SDL_TextEditingEvent & event) newText = event.text; redraw(); cb(text + newText); - -#ifdef VCMI_ANDROID - auto editedText = text + newText; - notifyAndroidTextInputChanged(editedText); -#endif } void CTextInput::filenameFilter(std::string & text, const std::string &) @@ -657,24 +638,6 @@ void CTextInput::numberFilter(std::string & text, const std::string & oldText, i } } -#ifdef VCMI_ANDROID -void CTextInput::notifyAndroidTextInputChanged(std::string & text) -{ - if(!focus) - return; - - auto fun = [&text](JNIEnv * env, jclass cls, jmethodID method) - { - auto jtext = env->NewStringUTF(text.c_str()); - env->CallStaticVoidMethod(cls, method, jtext); - env->DeleteLocalRef(jtext); - }; - CAndroidVMHelper vmHelper; - vmHelper.callCustomMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "notifyTextInputChanged", - "(Ljava/lang/String;)V", fun, true); -} -#endif //VCMI_ANDROID - CFocusable::CFocusable() :CFocusable(std::make_shared()) { diff --git a/client/widgets/TextControls.h b/client/widgets/TextControls.h index 335d9c1c4..b4ba5c037 100644 --- a/client/widgets/TextControls.h +++ b/client/widgets/TextControls.h @@ -198,9 +198,6 @@ class CTextInput : public CLabel, public CFocusable protected: std::string visibleText() override; -#ifdef VCMI_ANDROID - void notifyAndroidTextInputChanged(std::string & text); -#endif public: CFunctionList cb; CFunctionList filters; diff --git a/lib/CAndroidVMHelper.cpp b/lib/CAndroidVMHelper.cpp index 075bf71dd..6ac49e164 100644 --- a/lib/CAndroidVMHelper.cpp +++ b/lib/CAndroidVMHelper.cpp @@ -21,11 +21,6 @@ void CAndroidVMHelper::cacheVM(JNIEnv * env) env->GetJavaVM(&vmCache); } -void CAndroidVMHelper::cacheVM(JavaVM * vm) -{ - vmCache = vm; -} - CAndroidVMHelper::CAndroidVMHelper() { auto res = vmCache->GetEnv((void **) &envPtr, JNI_VERSION_1_1); diff --git a/lib/CAndroidVMHelper.h b/lib/CAndroidVMHelper.h index 48e77d1ae..b46272d48 100644 --- a/lib/CAndroidVMHelper.h +++ b/lib/CAndroidVMHelper.h @@ -42,8 +42,6 @@ public: static void cacheVM(JNIEnv * env); - static void cacheVM(JavaVM * vm); - static constexpr const char * NATIVE_METHODS_DEFAULT_CLASS = "eu/vcmi/vcmi/NativeMethods"; }; From 6b7ce798d0182443e797c4c4a0a8f262c193b44c Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Wed, 28 Dec 2022 21:58:32 +0200 Subject: [PATCH 02/26] artifactTransitionPos created --- client/widgets/CArtifactHolder.cpp | 4 ++-- lib/CArtHandler.cpp | 15 ++++++++++++--- lib/CArtHandler.h | 1 + lib/GameConstants.h | 1 + server/CGameHandler.cpp | 12 ++++++------ 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 5b4690ed7..f3a7411fa 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -740,11 +740,11 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact } else if(commonInfo->dst == src) //the dest artifact was moved -> we are picking it { - assert(dst.slot >= GameConstants::BACKPACK_START); + assert(ArtifactUtils::isSlotBackpack(dst.slot)); commonInfo->reset(); CArtifactsOfHero::ArtPlacePtr ap; - for(CArtifactsOfHero *aoh : commonInfo->participants) + for(CArtifactsOfHero * aoh : commonInfo->participants) { if(dst.isHolder(aoh->curHero)) { diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 0e0851639..79bc0d618 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -824,7 +824,12 @@ bool CArtifactInstance::canBePutAt(const ArtifactLocation & al, bool assumeDestR bool CArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved) const { - if(slot >= GameConstants::BACKPACK_START) + if(slot == ArtifactPosition::TRANSITION_POS) + { + return true; + } + + if(ArtifactUtils::isSlotBackpack(slot)) { if(artType->isBig()) return false; @@ -851,7 +856,7 @@ void CArtifactInstance::putAt(ArtifactLocation al) assert(canBePutAt(al)); al.getHolderArtSet()->setNewArtSlot(al.slot, this, false); - if(!ArtifactUtils::isSlotBackpack(al.slot)) + if(!ArtifactUtils::isSlotBackpack(al.slot) && (al.slot != ArtifactPosition::TRANSITION_POS)) al.getHolderNode()->attachTo(*this); } @@ -1329,6 +1334,8 @@ const CCombinedArtifactInstance *CArtifactSet::getAssemblyByConstituent(Artifact const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const { + if(pos == ArtifactPosition::TRANSITION_POS) + return &artifactTransitionPos; if(vstd::contains(artifactsWorn, pos)) return &artifactsWorn.at(pos); if(pos >= ArtifactPosition::AFTER_LAST ) @@ -1355,7 +1362,9 @@ ArtSlotInfo & CArtifactSet::retrieveNewArtSlot(ArtifactPosition slot) { assert(!vstd::contains(artifactsWorn, slot)); - if (!ArtifactUtils::isSlotBackpack(slot)) + if(slot == ArtifactPosition::TRANSITION_POS) + return artifactTransitionPos; + if(!ArtifactUtils::isSlotBackpack(slot)) return artifactsWorn[slot]; ArtSlotInfo newSlot; diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 70a8bc5c7..cf9c99a06 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -317,6 +317,7 @@ class DLL_LINKAGE CArtifactSet public: std::vector artifactsInBackpack; //hero's artifacts from bag std::map artifactsWorn; //map; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 + ArtSlotInfo artifactTransitionPos; // Used as transition position for manual artifact exchange ArtSlotInfo & retrieveNewArtSlot(ArtifactPosition slot); void setNewArtSlot(ArtifactPosition slot, CArtifactInstance *art, bool locked); diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 068fbfd90..244162e67 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -983,6 +983,7 @@ class ArtifactPosition public: enum EArtifactPosition { + TRANSITION_POS = -3, FIRST_AVAILABLE = -2, PRE_FIRST = -1, //sometimes used as error, sometimes as first free in backpack HEAD, SHOULDERS, NECK, RIGHT_HAND, LEFT_HAND, TORSO, //5 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 81ecb186f..6d9c553a9 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3907,17 +3907,17 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat if (src.slot == ArtifactPosition::MACH4 || dst.slot == ArtifactPosition::MACH4) COMPLAIN_RET("Cannot move catapult!"); - if (dst.slot >= GameConstants::BACKPACK_START) + if(ArtifactUtils::isSlotBackpack(dst.slot)) vstd::amin(dst.slot, ArtifactPosition(GameConstants::BACKPACK_START + (si32)dst.getHolderArtSet()->artifactsInBackpack.size())); - if (src.slot == dst.slot && src.artHolder == dst.artHolder) + if(src.slot == dst.slot && src.artHolder == dst.artHolder) COMPLAIN_RET("Won't move artifact: Dest same as source!"); - if (dst.slot < GameConstants::BACKPACK_START && destArtifact) //moving art to another slot + // Check if dst slot is occupied + if(!ArtifactUtils::isSlotBackpack(dst.slot) && destArtifact) { - //old artifact must be removed first - moveArtifact(dst, ArtifactLocation(dst.artHolder, ArtifactPosition( - (si32)dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START))); + // Previous artifact must be removed first + moveArtifact(dst, ArtifactLocation(dst.artHolder, ArtifactPosition::TRANSITION_POS)); } auto hero = boost::get>(dst.artHolder); if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->id, dst.slot)) From c706b4d4190a55d90d4808f991600f483f8e4236 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Wed, 28 Dec 2022 23:56:21 +0200 Subject: [PATCH 03/26] CArtifactsOfHero::artifactMoved updated --- client/widgets/CArtifactHolder.cpp | 77 ++++++++++++------------------ 1 file changed, 31 insertions(+), 46 deletions(-) diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index f3a7411fa..2b9200820 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -716,31 +716,40 @@ void CArtifactsOfHero::realizeCurrentTransaction() ArtifactLocation(commonInfo->dst.AOH->curHero, commonInfo->dst.slotID)); } -void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) +void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) { bool isCurHeroSrc = src.isHolder(curHero), isCurHeroDst = dst.isHolder(curHero); - if(isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START) + if(isCurHeroSrc && ArtifactUtils::isSlotBackpack(src.slot)) updateSlot(src.slot); - if(isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) + if(isCurHeroDst && ArtifactUtils::isSlotBackpack(dst.slot)) updateSlot(dst.slot); - if(isCurHeroSrc || isCurHeroDst) //we need to update all slots, artifact might be combined and affect more slots + // We need to update all slots, artifact might be combined and affect more slots + if(isCurHeroSrc || isCurHeroDst) updateWornSlots(false); - if (!src.isHolder(curHero) && !isCurHeroDst) + if(!src.isHolder(curHero) && !isCurHeroDst) return; - if(commonInfo->src == src) //artifact was taken from us + // When moving one artifact onto another it leads to two art movements: dst->TRANSITION_POS; src->dst + // however after first movement we pick the art from TRANSITION_POS and the second movement coming when + // we have a different artifact may look surprising... but it's valid. + + // Artifact was taken from us + if(commonInfo->src == src) { - assert(commonInfo->dst == dst //expected movement from slot ot slot - || dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START //artifact moved back to backpack (eg. to make place for art we are moving) + // Expected movement from slot ot slot + assert(commonInfo->dst == dst + // Artifact moved back to backpack (eg. to make place for art we are moving) + || dst.slot == dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START || dst.getHolderArtSet()->bearerType() != ArtBearer::HERO); commonInfo->reset(); unmarkSlots(); } - else if(commonInfo->dst == src) //the dest artifact was moved -> we are picking it + // The dest artifact was moved -> we are picking it + else if(commonInfo->dst == src) { - assert(ArtifactUtils::isSlotBackpack(dst.slot)); + assert(dst.slot == ArtifactPosition::TRANSITION_POS); commonInfo->reset(); CArtifactsOfHero::ArtPlacePtr ap; @@ -749,52 +758,28 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact if(dst.isHolder(aoh->curHero)) { commonInfo->src.AOH = aoh; - if((ap = aoh->getArtPlace(dst.slot)))//getArtPlace may return null - break; + break; } } - if(ap) - { - ap->select(); - } - else - { - commonInfo->src.art = dst.getArt(); - commonInfo->src.slotID = dst.slot; - assert(commonInfo->src.AOH); - CCS->curh->dragAndDropCursor(std::make_unique("artifact", dst.getArt()->artType->getIconIndex())); - markPossibleSlots(dst.getArt()); - } - } - else if(src.slot >= GameConstants::BACKPACK_START && - src.slot < commonInfo->src.slotID && - src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one - { - //int fixedSlot = src.hero->getArtPos(commonInfo->src.art); - vstd::advance(commonInfo->src.slotID, -1); - assert(commonInfo->src.valid()); - } - else - { - //when moving one artifact onto another it leads to two art movements: dst->backapck; src->dst - // however after first movement we pick the art from backpack and the second movement coming when - // we have a different artifact may look surprising... but it's valid. + commonInfo->src.art = dst.getArt(); + commonInfo->src.slotID = dst.slot; + assert(commonInfo->src.AOH); + CCS->curh->dragAndDropCursor(make_unique("artifact", dst.getArt()->artType->getIconIndex())); + markPossibleSlots(dst.getArt()); } updateParentWindow(); int shift = 0; -// if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos) -// shift++; -// - if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos) + if(!ArtifactUtils::isSlotBackpack(src.slot) && dst.slot - GameConstants::BACKPACK_START < backpackPos) shift++; - if(dst.slot < GameConstants::BACKPACK_START && src.slot - GameConstants::BACKPACK_START < backpackPos) + if(!ArtifactUtils::isSlotBackpack(dst.slot) && src.slot - GameConstants::BACKPACK_START < backpackPos) shift--; - if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START) - || (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) ) - scrollBackpack(shift); //update backpack slots + // If backpack is changed, update it + if((isCurHeroSrc && ArtifactUtils::isSlotBackpack(src.slot)) + || (isCurHeroDst && ArtifactUtils::isSlotBackpack(dst.slot))) + scrollBackpack(shift); } void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al) From 4005b4836072ba3cf8ad51914dac1ba46f74efba Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Thu, 29 Dec 2022 20:39:01 +0200 Subject: [PATCH 04/26] Regressions fixed --- client/widgets/CArtifactHolder.cpp | 32 +++++++++++++++++++++++++++--- client/windows/GUIClasses.cpp | 2 -- lib/CArtHandler.cpp | 21 ++++++++++++++++---- lib/CArtHandler.h | 2 +- server/CGameHandler.cpp | 29 +++++++++++++++------------ 5 files changed, 63 insertions(+), 23 deletions(-) diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 2b9200820..46ec17742 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -670,6 +670,16 @@ CArtifactsOfHero::CArtifactsOfHero(const Point & position, bool createCommonPart CArtifactsOfHero::~CArtifactsOfHero() { dispose(); + // Artifact located in artifactsTransitionPos should be returned + if(!curHero->artifactsTransitionPos.empty()) + { + auto artPlace = getArtPlace( + ArtifactUtils::getArtifactDstPosition(curHero->artifactsTransitionPos.begin()->artifact, curHero, curHero->bearerType())); + assert(artPlace); + assert(artPlace->ourOwner); + artPlace->setMeAsDest(); + artPlace->ourOwner->realizeCurrentTransaction(); + } } void CArtifactsOfHero::updateParentWindow() @@ -735,8 +745,21 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const Artifac // however after first movement we pick the art from TRANSITION_POS and the second movement coming when // we have a different artifact may look surprising... but it's valid. + // Used when doing dragAndDrop and artifact swap multiple times + if(src.slot == ArtifactPosition::TRANSITION_POS && + commonInfo->src.slotID == ArtifactPosition::TRANSITION_POS && + commonInfo->dst.slotID == ArtifactPosition::PRE_FIRST) + { + auto art = curHero->getArt(ArtifactPosition::TRANSITION_POS); + assert(art); + CCS->curh->dragAndDropCursor(make_unique("artifact", art->artType->getIconIndex())); + markPossibleSlots(art); + + commonInfo->src.art = art; + commonInfo->src.slotID = src.slot; + } // Artifact was taken from us - if(commonInfo->src == src) + else if(commonInfo->src == src) { // Expected movement from slot ot slot assert(commonInfo->dst == dst @@ -746,13 +769,12 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const Artifac commonInfo->reset(); unmarkSlots(); } - // The dest artifact was moved -> we are picking it + // The dest artifact was moved after the swap -> we are picking it else if(commonInfo->dst == src) { assert(dst.slot == ArtifactPosition::TRANSITION_POS); commonInfo->reset(); - CArtifactsOfHero::ArtPlacePtr ap; for(CArtifactsOfHero * aoh : commonInfo->participants) { if(dst.isHolder(aoh->curHero)) @@ -795,6 +817,10 @@ void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al) CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(int slot) { + if(slot == ArtifactPosition::TRANSITION_POS) + { + return nullptr; + } if(slot < GameConstants::BACKPACK_START) { if(artWorn.find(ArtifactPosition(slot)) == artWorn.end()) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 8ceaed783..a84b12a1b 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1249,8 +1249,6 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, CExchangeWindow::~CExchangeWindow() { - artifs[0]->commonInfo = nullptr; - artifs[1]->commonInfo = nullptr; } const CGarrisonSlot * CExchangeWindow::getSelectedSlotID() const diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 79bc0d618..49fcce302 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -864,7 +864,7 @@ void CArtifactInstance::removeFrom(ArtifactLocation al) { assert(al.getHolderArtSet()->getArt(al.slot) == this); al.getHolderArtSet()->eraseArtSlot(al.slot); - if(!ArtifactUtils::isSlotBackpack(al.slot)) + if(!ArtifactUtils::isSlotBackpack(al.slot) && (al.slot != ArtifactPosition::TRANSITION_POS)) al.getHolderNode()->detachFrom(*this); } @@ -1335,7 +1335,11 @@ const CCombinedArtifactInstance *CArtifactSet::getAssemblyByConstituent(Artifact const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const { if(pos == ArtifactPosition::TRANSITION_POS) - return &artifactTransitionPos; + { + // Always add to the end. Always take from the beginning. + assert(!artifactsTransitionPos.empty()); + return &(*artifactsTransitionPos.begin()); + } if(vstd::contains(artifactsWorn, pos)) return &artifactsWorn.at(pos); if(pos >= ArtifactPosition::AFTER_LAST ) @@ -1363,7 +1367,11 @@ ArtSlotInfo & CArtifactSet::retrieveNewArtSlot(ArtifactPosition slot) assert(!vstd::contains(artifactsWorn, slot)); if(slot == ArtifactPosition::TRANSITION_POS) - return artifactTransitionPos; + { + // Always add to the end. Always take from the beginning. + artifactsTransitionPos.push_back(ArtSlotInfo()); + return artifactsTransitionPos.back(); + } if(!ArtifactUtils::isSlotBackpack(slot)) return artifactsWorn[slot]; @@ -1384,7 +1392,12 @@ void CArtifactSet::setNewArtSlot(ArtifactPosition slot, CArtifactInstance *art, void CArtifactSet::eraseArtSlot(ArtifactPosition slot) { - if(ArtifactUtils::isSlotBackpack(slot)) + if(slot == ArtifactPosition::TRANSITION_POS) + { + assert(!artifactsTransitionPos.empty()); + artifactsTransitionPos.erase(artifactsTransitionPos.begin()); + } + else if(ArtifactUtils::isSlotBackpack(slot)) { auto backpackSlot = ArtifactPosition(slot - GameConstants::BACKPACK_START); diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index cf9c99a06..cae8abef1 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -317,7 +317,7 @@ class DLL_LINKAGE CArtifactSet public: std::vector artifactsInBackpack; //hero's artifacts from bag std::map artifactsWorn; //map; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 - ArtSlotInfo artifactTransitionPos; // Used as transition position for manual artifact exchange + std::vector artifactsTransitionPos; // Used as transition position for dragAndDrop artifact exchange ArtSlotInfo & retrieveNewArtSlot(ArtifactPosition slot); void setNewArtSlot(ArtifactPosition slot, CArtifactInstance *art, bool locked); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 6d9c553a9..533c6f3db 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3910,21 +3910,24 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat if(ArtifactUtils::isSlotBackpack(dst.slot)) vstd::amin(dst.slot, ArtifactPosition(GameConstants::BACKPACK_START + (si32)dst.getHolderArtSet()->artifactsInBackpack.size())); - if(src.slot == dst.slot && src.artHolder == dst.artHolder) - COMPLAIN_RET("Won't move artifact: Dest same as source!"); - - // Check if dst slot is occupied - if(!ArtifactUtils::isSlotBackpack(dst.slot) && destArtifact) + if(!(src.slot == ArtifactPosition::TRANSITION_POS && dst.slot == ArtifactPosition::TRANSITION_POS)) { - // Previous artifact must be removed first - moveArtifact(dst, ArtifactLocation(dst.artHolder, ArtifactPosition::TRANSITION_POS)); - } - auto hero = boost::get>(dst.artHolder); - if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->id, dst.slot)) - giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK); + if(src.slot == dst.slot && src.artHolder == dst.artHolder) + COMPLAIN_RET("Won't move artifact: Dest same as source!"); - MoveArtifact ma(&src, &dst); - sendAndApply(&ma); + // Check if dst slot is occupied + if(!ArtifactUtils::isSlotBackpack(dst.slot) && destArtifact) + { + // Previous artifact must be removed first + moveArtifact(dst, ArtifactLocation(dst.artHolder, ArtifactPosition::TRANSITION_POS)); + } + auto hero = boost::get>(dst.artHolder); + if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->id, dst.slot)) + giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK); + + MoveArtifact ma(&src, &dst); + sendAndApply(&ma); + } return true; } From 7e8516f927945708ff0a0c664619c1f0f280d7e6 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 6 Jan 2023 23:29:33 +0200 Subject: [PATCH 05/26] Changed Sirens behavior to match H3 logic --- lib/mapObjects/MiscObjects.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 309971b9e..e0fddbb16 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1956,7 +1956,13 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const for (auto i = h->Slots().begin(); i != h->Slots().end(); i++) { - TQuantity drown = static_cast(i->second->count * 0.3); + // 1-sized stacks are not affected by sirens + if (i->second->count == 1) + continue; + + // tested H3 behavior: 30% (rounded up) of stack drowns + TQuantity drown = std::ceil(i->second->count * 0.3); + if(drown) { cb->changeStackCount(StackLocation(h, i->first), -drown); From 8526eba6fa82006409650f032aeb83e58276e86f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 31 Dec 2022 17:55:22 +0200 Subject: [PATCH 06/26] Added checks to music player to detect potential freezes --- client/CMusicHandler.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/client/CMusicHandler.cpp b/client/CMusicHandler.cpp index d52c81b83..563627beb 100644 --- a/client/CMusicHandler.cpp +++ b/client/CMusicHandler.cpp @@ -89,7 +89,7 @@ CSoundHandler::CSoundHandler(): soundBase::battle02, soundBase::battle03, soundBase::battle04, soundBase::battle05, soundBase::battle06, soundBase::battle07 }; - + //predefine terrain set //TODO: support custom sounds for new terrains and load from json horseSounds = @@ -542,6 +542,20 @@ MusicEntry::MusicEntry(CMusicHandler *owner, std::string setName, std::string mu } MusicEntry::~MusicEntry() { + if (playing) + { + assert(0); + logGlobal->error("Attempt to delete music while playing!"); + Mix_HaltMusic(); + } + + if (loop == 0 && Mix_FadingMusic() != MIX_NO_FADING) + { + assert(0); + logGlobal->error("Attempt to delete music while fading out!"); + Mix_HaltMusic(); + } + logGlobal->trace("Del-ing music file %s", currentName); if (music) Mix_FreeMusic(music); @@ -619,7 +633,7 @@ bool MusicEntry::play() bool MusicEntry::stop(int fade_ms) { - if (playing) + if (Mix_PlayingMusic()) { playing = false; loop = 0; From 9308319ac7a42105ee0a8ee4c794a9b139376540 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 8 Jan 2023 18:05:00 +0200 Subject: [PATCH 07/26] Added workaround for ~200 ms lag occuring after hero move --- client/CPlayerInterface.cpp | 3 +++ client/windows/CAdvmapInterface.cpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 9dbf79dfb..b55e6d2bd 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -2550,6 +2550,9 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path) // (i == 0) means hero went through all the path adventureInt->updateMoveHero(h, (i != 0)); adventureInt->updateNextHero(h); + + // ugly workaround to force instant update of adventure map + adventureInt->animValHitCount = 8; } setMovementStatus(false); diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 7706af146..99e676a36 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -1044,7 +1044,7 @@ void CAdvMapInt::show(SDL_Surface * to) { ++heroAnim; } - if(animValHitCount == 8) + if(animValHitCount >= 8) { CGI->mh->updateWater(); animValHitCount = 0; From 6379c5f6fae9a039089c4a7193f94372807ed7a1 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 8 Jan 2023 14:11:08 +0200 Subject: [PATCH 08/26] Install additional dll on Windows to get better style for Qt --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 95fe102a4..0a71610aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,6 +506,9 @@ if(WIN32) FILES ${integration_loc} DESTINATION ${BIN_DIR}/platforms ) + install( + FILES "$" + DESTINATION ${BIN_DIR}/styles) endif() endif() From a0568823a99b892ca8d97a513fb392d1923aa3f5 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Fri, 30 Dec 2022 21:43:32 +0200 Subject: [PATCH 09/26] swap contitutient --- client/widgets/CArtifactHolder.cpp | 33 ++++++++++++------------------ client/widgets/CArtifactHolder.h | 2 +- client/windows/GUIClasses.cpp | 4 ---- client/windows/GUIClasses.h | 1 - lib/CArtHandler.cpp | 19 +++++++++++++---- lib/CArtHandler.h | 1 + server/CGameHandler.cpp | 4 +++- 7 files changed, 33 insertions(+), 31 deletions(-) diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 46ec17742..4b12f2543 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -278,13 +278,12 @@ void CHeroArtPlace::select () if (locked) return; - selectSlot(true); pickSlot(true); if(ourArt->canBeDisassembled() && slotID < GameConstants::BACKPACK_START) //worn combined artifact -> locks have to disappear { - for(int i = 0; i < GameConstants::BACKPACK_START; i++) + for(auto slot : ArtifactUtils::constituentWornSlots()) { - auto ap = ourOwner->getArtPlace(i); + auto ap = ourOwner->getArtPlace(slot); if(ap)//getArtPlace may return null ap->pickSlot(ourArt->isPart(ap->ourArt)); } @@ -309,9 +308,9 @@ void CHeroArtPlace::deselect () pickSlot(false); if(ourArt && ourArt->canBeDisassembled()) //combined art returned to its slot -> restore locks { - for(int i = 0; i < GameConstants::BACKPACK_START; i++) + for(auto slot : ArtifactUtils::constituentWornSlots()) { - auto place = ourOwner->getArtPlace(i); + auto place = ourOwner->getArtPlace(slot); if(nullptr != place)//getArtPlace may return null place->pickSlot(false); @@ -735,10 +734,10 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const Artifac if(isCurHeroDst && ArtifactUtils::isSlotBackpack(dst.slot)) updateSlot(dst.slot); // We need to update all slots, artifact might be combined and affect more slots - if(isCurHeroSrc || isCurHeroDst) + if(isCurHeroSrc || isCurHeroDst) updateWornSlots(false); - if(!src.isHolder(curHero) && !isCurHeroDst) + if(!isCurHeroSrc && !isCurHeroDst) return; // When moving one artifact onto another it leads to two art movements: dst->TRANSITION_POS; src->dst @@ -748,11 +747,12 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const Artifac // Used when doing dragAndDrop and artifact swap multiple times if(src.slot == ArtifactPosition::TRANSITION_POS && commonInfo->src.slotID == ArtifactPosition::TRANSITION_POS && - commonInfo->dst.slotID == ArtifactPosition::PRE_FIRST) + commonInfo->dst.slotID == ArtifactPosition::PRE_FIRST && + isCurHeroDst) { auto art = curHero->getArt(ArtifactPosition::TRANSITION_POS); assert(art); - CCS->curh->dragAndDropCursor(make_unique("artifact", art->artType->getIconIndex())); + CCS->curh->dragAndDropCursor(std::make_unique("artifact", art->artType->getIconIndex())); markPossibleSlots(art); commonInfo->src.art = art; @@ -787,21 +787,14 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const Artifac commonInfo->src.art = dst.getArt(); commonInfo->src.slotID = dst.slot; assert(commonInfo->src.AOH); - CCS->curh->dragAndDropCursor(make_unique("artifact", dst.getArt()->artType->getIconIndex())); - markPossibleSlots(dst.getArt()); + CCS->curh->dragAndDropCursor(std::make_unique("artifact", dst.getArt()->artType->getIconIndex())); } updateParentWindow(); - int shift = 0; - if(!ArtifactUtils::isSlotBackpack(src.slot) && dst.slot - GameConstants::BACKPACK_START < backpackPos) - shift++; - if(!ArtifactUtils::isSlotBackpack(dst.slot) && src.slot - GameConstants::BACKPACK_START < backpackPos) - shift--; - // If backpack is changed, update it if((isCurHeroSrc && ArtifactUtils::isSlotBackpack(src.slot)) || (isCurHeroDst && ArtifactUtils::isSlotBackpack(dst.slot))) - scrollBackpack(shift); + scrollBackpack(0); } void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al) @@ -815,7 +808,7 @@ void CArtifactsOfHero::artifactRemoved(const ArtifactLocation &al) } } -CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(int slot) +CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(ArtifactPosition slot) { if(slot == ArtifactPosition::TRANSITION_POS) { @@ -823,7 +816,7 @@ CArtifactsOfHero::ArtPlacePtr CArtifactsOfHero::getArtPlace(int slot) } if(slot < GameConstants::BACKPACK_START) { - if(artWorn.find(ArtifactPosition(slot)) == artWorn.end()) + if(artWorn.find(slot) == artWorn.end()) { logGlobal->error("CArtifactsOfHero::getArtPlace: invalid slot %d", slot); return nullptr; diff --git a/client/widgets/CArtifactHolder.h b/client/widgets/CArtifactHolder.h index 36e16e547..0adfb0ec9 100644 --- a/client/widgets/CArtifactHolder.h +++ b/client/widgets/CArtifactHolder.h @@ -141,7 +141,7 @@ public: void artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst); void artifactRemoved(const ArtifactLocation &al); void artifactUpdateSlots(const ArtifactLocation &al); - ArtPlacePtr getArtPlace(int slot);//may return null + ArtPlacePtr getArtPlace(ArtifactPosition slot);//may return null void setHero(const CGHeroInstance * hero); const CGHeroInstance *getHero() const; diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index a84b12a1b..54d6a1531 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -1247,10 +1247,6 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, updateWidgets(); } -CExchangeWindow::~CExchangeWindow() -{ -} - const CGarrisonSlot * CExchangeWindow::getSelectedSlotID() const { return garr->getSelection(); diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index 56097d0b4..0ce7d8314 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -379,7 +379,6 @@ public: const CGarrisonSlot * getSelectedSlotID() const; CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID queryID); - ~CExchangeWindow(); }; /// Here you can buy ships diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 49fcce302..96632f488 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -856,7 +856,7 @@ void CArtifactInstance::putAt(ArtifactLocation al) assert(canBePutAt(al)); al.getHolderArtSet()->setNewArtSlot(al.slot, this, false); - if(!ArtifactUtils::isSlotBackpack(al.slot) && (al.slot != ArtifactPosition::TRANSITION_POS)) + if(ArtifactUtils::isSlotEquipment(al.slot)) al.getHolderNode()->attachTo(*this); } @@ -864,7 +864,7 @@ void CArtifactInstance::removeFrom(ArtifactLocation al) { assert(al.getHolderArtSet()->getArt(al.slot) == this); al.getHolderArtSet()->eraseArtSlot(al.slot); - if(!ArtifactUtils::isSlotBackpack(al.slot) && (al.slot != ArtifactPosition::TRANSITION_POS)) + if(ArtifactUtils::isSlotEquipment(al.slot)) al.getHolderNode()->detachFrom(*this); } @@ -1003,6 +1003,8 @@ bool CArtifactInstance::isPart(const CArtifactInstance *supposedPart) const bool CCombinedArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved) const { + if(slot == ArtifactPosition::TRANSITION_POS) + return true; bool canMainArtifactBePlaced = CArtifactInstance::canBePutAt(artSet, slot, assumeDestRemoved); if(!canMainArtifactBePlaced) return false; //no is no... @@ -1075,7 +1077,11 @@ void CCombinedArtifactInstance::addAsConstituent(CArtifactInstance *art, Artifac void CCombinedArtifactInstance::putAt(ArtifactLocation al) { - if(ArtifactUtils::isSlotBackpack(al.slot)) + if(al.slot == ArtifactPosition::TRANSITION_POS) + { + CArtifactInstance::putAt(al); + } + else if(ArtifactUtils::isSlotBackpack(al.slot)) { CArtifactInstance::putAt(al); for(ConstituentInfo &ci : constituentsInfo) @@ -1113,7 +1119,7 @@ void CCombinedArtifactInstance::putAt(ArtifactLocation al) void CCombinedArtifactInstance::removeFrom(ArtifactLocation al) { - if(ArtifactUtils::isSlotBackpack(al.slot)) + if(ArtifactUtils::isSlotBackpack(al.slot) || al.slot == ArtifactPosition::TRANSITION_POS) { CArtifactInstance::removeFrom(al); } @@ -1634,4 +1640,9 @@ DLL_LINKAGE bool ArtifactUtils::isSlotBackpack(ArtifactPosition slot) return slot >= GameConstants::BACKPACK_START; } +DLL_LINKAGE bool ArtifactUtils::isSlotEquipment(ArtifactPosition slot) +{ + return slot < GameConstants::BACKPACK_START && slot >= 0; +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index cae8abef1..504c362c7 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -393,6 +393,7 @@ namespace ArtifactUtils DLL_LINKAGE bool isArtRemovable(const std::pair & slot); DLL_LINKAGE bool checkSpellbookIsNeeded(const CGHeroInstance * heroPtr, ArtifactID artID, ArtifactPosition slot); DLL_LINKAGE bool isSlotBackpack(ArtifactPosition slot); + DLL_LINKAGE bool isSlotEquipment(ArtifactPosition slot); } VCMI_LIB_NAMESPACE_END diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 533c6f3db..ce0fb4bee 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3892,7 +3892,7 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat // Check if src/dest slots are appropriate for the artifacts exchanged. // Moving to the backpack is always allowed. - if ((!srcArtifact || dst.slot < GameConstants::BACKPACK_START) + if ((!srcArtifact || !ArtifactUtils::isSlotBackpack(dst.slot)) && srcArtifact && !srcArtifact->canBePutAt(dst, true)) COMPLAIN_RET("Cannot move artifact!"); @@ -3926,6 +3926,8 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK); MoveArtifact ma(&src, &dst); + if(dst.slot == ArtifactPosition::TRANSITION_POS) + ma.askAssemble = false; sendAndApply(&ma); } return true; From e1880604806e649fb189b21ce632d991375e6c05 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 9 Jan 2023 23:27:38 +0200 Subject: [PATCH 10/26] Fix server shutdown on transferring artifact to commander --- server/CGameHandler.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 40e684973..943f0e5bc 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3916,9 +3916,17 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat moveArtifact(dst, ArtifactLocation(dst.artHolder, ArtifactPosition( (si32)dst.getHolderArtSet()->artifactsInBackpack.size() + GameConstants::BACKPACK_START))); } - auto hero = boost::get>(dst.artHolder); - if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->id, dst.slot)) - giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK); + + try + { + auto hero = boost::get>(dst.artHolder); + if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->id, dst.slot)) + giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK); + } + catch (boost::bad_get const &) + { + // object other than hero received an art - ignore + } MoveArtifact ma(&src, &dst); sendAndApply(&ma); From 9658ffba99c825cc46eb92082cbeb8252d14f690 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 9 Jan 2023 23:38:04 +0200 Subject: [PATCH 11/26] Allow disabling & deleting local mods other than vcmi mod --- launcher/modManager/cmodlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index e08c99ce4..1ec1313d0 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -124,7 +124,7 @@ bool CModEntry::isCompatible() const bool CModEntry::isEssential() const { - return getValue("storedLocaly").toBool(); + return getName() == "vcmi"; } bool CModEntry::isInstalled() const From e855a9db7c4be58a630043e3a1b6294249141a15 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 31 Dec 2022 16:31:34 +0300 Subject: [PATCH 12/26] [Conan] use profile includes for common parts --- CI/conan/base/apple | 7 +++++++ CI/conan/base/ios | 5 +++++ CI/conan/base/macos | 4 ++++ CI/conan/ios-arm64 | 13 ++----------- CI/conan/ios-armv7 | 13 ++----------- CI/conan/macos-arm | 12 ++---------- CI/conan/macos-intel | 12 ++---------- 7 files changed, 24 insertions(+), 42 deletions(-) create mode 100644 CI/conan/base/apple create mode 100644 CI/conan/base/ios create mode 100644 CI/conan/base/macos diff --git a/CI/conan/base/apple b/CI/conan/base/apple new file mode 100644 index 000000000..477761aa9 --- /dev/null +++ b/CI/conan/base/apple @@ -0,0 +1,7 @@ +[settings] +compiler=apple-clang +compiler.version=13 +compiler.libcxx=libc++ +build_type=Release +[conf] +tools.cmake.cmaketoolchain:generator = Ninja diff --git a/CI/conan/base/ios b/CI/conan/base/ios new file mode 100644 index 000000000..0f48cadf2 --- /dev/null +++ b/CI/conan/base/ios @@ -0,0 +1,5 @@ +include(apple) + +[settings] +os=iOS +os.sdk=iphoneos diff --git a/CI/conan/base/macos b/CI/conan/base/macos new file mode 100644 index 000000000..db13d6a90 --- /dev/null +++ b/CI/conan/base/macos @@ -0,0 +1,4 @@ +include(apple) + +[settings] +os=Macos diff --git a/CI/conan/ios-arm64 b/CI/conan/ios-arm64 index f702a6302..238bbcf8f 100644 --- a/CI/conan/ios-arm64 +++ b/CI/conan/ios-arm64 @@ -1,14 +1,5 @@ +include(base/ios) + [settings] -os=iOS os.version=12.0 -os.sdk=iphoneos arch=armv8 -compiler=apple-clang -compiler.version=13 -compiler.libcxx=libc++ -build_type=Release -[options] -[build_requires] -[env] -[conf] -tools.cmake.cmaketoolchain:generator = Ninja diff --git a/CI/conan/ios-armv7 b/CI/conan/ios-armv7 index 35a87253d..6bec961e7 100644 --- a/CI/conan/ios-armv7 +++ b/CI/conan/ios-armv7 @@ -1,14 +1,5 @@ +include(base/ios) + [settings] -os=iOS os.version=10.0 -os.sdk=iphoneos arch=armv7 -compiler=apple-clang -compiler.version=13 -compiler.libcxx=libc++ -build_type=Release -[options] -[build_requires] -[env] -[conf] -tools.cmake.cmaketoolchain:generator = Ninja diff --git a/CI/conan/macos-arm b/CI/conan/macos-arm index 99858c9b3..d3f06e078 100644 --- a/CI/conan/macos-arm +++ b/CI/conan/macos-arm @@ -1,13 +1,5 @@ +include(base/macos) + [settings] -os=Macos os.version=11.0 arch=armv8 -compiler=apple-clang -compiler.version=13 -compiler.libcxx=libc++ -build_type=Release -[options] -[build_requires] -[env] -[conf] -tools.cmake.cmaketoolchain:generator = Ninja diff --git a/CI/conan/macos-intel b/CI/conan/macos-intel index 510caaee4..b527e056b 100644 --- a/CI/conan/macos-intel +++ b/CI/conan/macos-intel @@ -1,13 +1,5 @@ +include(base/macos) + [settings] -os=Macos os.version=10.13 arch=x86_64 -compiler=apple-clang -compiler.version=13 -compiler.libcxx=libc++ -build_type=Release -[options] -[build_requires] -[env] -[conf] -tools.cmake.cmaketoolchain:generator = Ninja From 43ef6c075e14d5e14b0024c6811608d14c2a320a Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 31 Dec 2022 16:32:11 +0300 Subject: [PATCH 13/26] [Conan] disable bitcode explicitly --- CI/conan/base/apple | 1 + 1 file changed, 1 insertion(+) diff --git a/CI/conan/base/apple b/CI/conan/base/apple index 477761aa9..18655d38b 100644 --- a/CI/conan/base/apple +++ b/CI/conan/base/apple @@ -4,4 +4,5 @@ compiler.version=13 compiler.libcxx=libc++ build_type=Release [conf] +tools.apple:enable_bitcode = False tools.cmake.cmaketoolchain:generator = Ninja From 427cae270462884dbe5c4ed03111dd47fc8f3271 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 31 Dec 2022 16:33:06 +0300 Subject: [PATCH 14/26] [Conan] fix building Boost.Locale with Apple-clang in versions >= 1.81 --- CI/conan/base/apple | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CI/conan/base/apple b/CI/conan/base/apple index 18655d38b..bdb33e3e6 100644 --- a/CI/conan/base/apple +++ b/CI/conan/base/apple @@ -3,6 +3,10 @@ compiler=apple-clang compiler.version=13 compiler.libcxx=libc++ build_type=Release + +# required for Boost.Locale in versions >= 1.81 +compiler.cppstd=11 + [conf] tools.apple:enable_bitcode = False tools.cmake.cmaketoolchain:generator = Ninja From 453217b4bb76951796f36153941fcd9e17fb44cf Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 14 Jan 2023 13:55:42 +0300 Subject: [PATCH 15/26] [Conan] ignore SDL versions with broken sound --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index fd6108234..a3030c689 100644 --- a/conanfile.py +++ b/conanfile.py @@ -13,7 +13,7 @@ class VCMI(ConanFile): "minizip/[~1.2.12]", "onetbb/[^2021.3]", # Nullkiller AI "qt/[~5.15.2]", # launcher - "sdl/[~2.24.0]", + "sdl/[~2.26.1 || >=2.0.20 <=2.22.0]", # versions in between have broken sound "sdl_image/[~2.0.5]", "sdl_mixer/[~2.0.4]", "sdl_ttf/[~2.0.18]", From 0eef2412db1c9ec5eed247a426bdae534453905b Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 14 Jan 2023 14:25:16 +0300 Subject: [PATCH 16/26] [Conan] switch to apple-clang 14 --- CI/conan/base/apple | 2 +- CI/conan/ios-armv7 | 3 +++ docs/conan.md | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CI/conan/base/apple b/CI/conan/base/apple index bdb33e3e6..ae85bc702 100644 --- a/CI/conan/base/apple +++ b/CI/conan/base/apple @@ -1,6 +1,6 @@ [settings] compiler=apple-clang -compiler.version=13 +compiler.version=14 compiler.libcxx=libc++ build_type=Release diff --git a/CI/conan/ios-armv7 b/CI/conan/ios-armv7 index 6bec961e7..54482ce16 100644 --- a/CI/conan/ios-armv7 +++ b/CI/conan/ios-armv7 @@ -3,3 +3,6 @@ include(base/ios) [settings] os.version=10.0 arch=armv7 + +# Xcode 13.x is the last version that can build for armv7 +compiler.version=13 diff --git a/docs/conan.md b/docs/conan.md index 3bd0308ef..f55ff5ba9 100644 --- a/docs/conan.md +++ b/docs/conan.md @@ -20,8 +20,8 @@ The following platforms are supported and known to work, others might require ch 1. Check if your build environment can use the prebuilt binaries: basically, that your compiler version (or Xcode major version) matches the information below. If you're unsure, simply advance to the next step. - - macOS: libraries are built with Apple clang 13 (Xcode 13.4.1), should be consumable by Xcode and Xcode CLT 13.x - - iOS: libraries are built with Apple clang 13 (Xcode 13.4.1), should be consumable by Xcode 13.x + - macOS: libraries are built with Apple clang 14 (Xcode 14.2), should be consumable by Xcode and Xcode CLT 14.x (older library versions are also available for Xcode 13, see Releases in the respective repo) + - iOS: libraries are built with Apple clang 14 (Xcode 14.2), should be consumable by Xcode 14.x (older library versions are also available for Xcode 13, see Releases in the respective repo) 2. Download the binaries archive and unpack it to `~/.conan` directory: From 9d3ad51de44ed3745bd7d67ccede8bb2e86e85b2 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 14 Jan 2023 14:37:08 +0300 Subject: [PATCH 17/26] [Conan][docs] fix examples of using our prebuilt binaries for macOS/iOS --- docs/conan.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/conan.md b/docs/conan.md index f55ff5ba9..6e0a2e834 100644 --- a/docs/conan.md +++ b/docs/conan.md @@ -85,7 +85,8 @@ conan install . \ --no-imports \ --build=never \ --profile:build=default \ - --profile:host=CI/conan/macos-intel + --profile:host=CI/conan/macos-intel \ + -o with_apple_system_libs=True cmake -S . -B build -G Xcode \ --toolchain conan-generated/conan_toolchain.cmake @@ -116,7 +117,8 @@ conan install . \ --no-imports \ --build=never \ --profile:build=default \ - --profile:host=CI/conan/ios-arm64 + --profile:host=CI/conan/ios-arm64 \ + -o with_apple_system_libs=True cmake --preset ios-conan ``` From b22c651b0f0c294691aa164d8ed7a7c8b3dbd1ea Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 14 Jan 2023 14:37:59 +0300 Subject: [PATCH 18/26] [CI] switch Apple platforms to Xcode 14 --- CI/ios/before_install.sh | 4 ++-- CI/mac/before_install.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CI/ios/before_install.sh b/CI/ios/before_install.sh index 69e459d86..5f47b58f0 100755 --- a/CI/ios/before_install.sh +++ b/CI/ios/before_install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -echo DEVELOPER_DIR=/Applications/Xcode_13.4.1.app >> $GITHUB_ENV +echo DEVELOPER_DIR=/Applications/Xcode_14.2.app >> $GITHUB_ENV mkdir ~/.conan ; cd ~/.conan -curl -L 'https://github.com/vcmi/vcmi-ios-deps/releases/download/1.1/ios-arm64.xz' \ +curl -L 'https://github.com/vcmi/vcmi-ios-deps/releases/download/1.2/ios-arm64.txz' \ | tar -xf - diff --git a/CI/mac/before_install.sh b/CI/mac/before_install.sh index 5bd64351c..b4c46a693 100755 --- a/CI/mac/before_install.sh +++ b/CI/mac/before_install.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash -echo DEVELOPER_DIR=/Applications/Xcode_13.4.1.app >> $GITHUB_ENV +echo DEVELOPER_DIR=/Applications/Xcode_14.2.app >> $GITHUB_ENV brew install ninja mkdir ~/.conan ; cd ~/.conan -curl -L "https://github.com/vcmi/vcmi-deps-macos/releases/download/1.1/$DEPS_FILENAME.txz" \ +curl -L "https://github.com/vcmi/vcmi-deps-macos/releases/download/1.2/$DEPS_FILENAME.txz" \ | tar -xf - From efbed6000bda890f6b8571b4ea26e769f1183e56 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 14 Jan 2023 14:38:03 +0300 Subject: [PATCH 19/26] fix typo --- AI/Nullkiller/Analyzers/ObjectClusterizer.cpp | 1 - config/schemas/terrain.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp index 2acdef5e9..8c0829a36 100644 --- a/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp +++ b/AI/Nullkiller/Analyzers/ObjectClusterizer.cpp @@ -202,7 +202,6 @@ void ObjectClusterizer::clusterize() Obj::WHIRLPOOL, Obj::BUOY, Obj::SIGN, - Obj::SIGN, Obj::GARRISON, Obj::MONSTER, Obj::GARRISON2, diff --git a/config/schemas/terrain.json b/config/schemas/terrain.json index b4be9e922..bcbabeaa8 100644 --- a/config/schemas/terrain.json +++ b/config/schemas/terrain.json @@ -54,7 +54,7 @@ "rockTerrain": { "type": "string", - "description": "The name of tock type terrain which will be used as borders in the underground" + "description": "The name of rock type terrain which will be used as borders in the underground" }, "river": { From 2f149141202eeedd2c3c604926af020dbfca718c Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 14 Jan 2023 16:27:11 +0300 Subject: [PATCH 20/26] fix Boost deprecation warnings warnings introduced in v1.81 # Conflicts: # mapeditor/resourceExtractor/ResourceConverter.cpp --- client/CPlayerInterface.cpp | 2 +- lib/filesystem/CFilesystemLoader.cpp | 19 ++++++++++++++++--- .../resourceExtractor/ResourceConverter.cpp | 16 ++++++---------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index b55e6d2bd..7192b1c88 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -1702,7 +1702,7 @@ int CPlayerInterface::getLastIndex( std::string namePrefix) else for (directory_iterator dir(gamesDir); dir != enddir; ++dir) { - if (is_regular(dir->status())) + if (is_regular_file(dir->status())) { std::string name = dir->path().filename().string(); if (starts_with(name, namePrefix) && ends_with(name, ".vcgm1")) diff --git a/lib/filesystem/CFilesystemLoader.cpp b/lib/filesystem/CFilesystemLoader.cpp index 122d22bf8..65d95ff6e 100644 --- a/lib/filesystem/CFilesystemLoader.cpp +++ b/lib/filesystem/CFilesystemLoader.cpp @@ -112,18 +112,31 @@ std::unordered_map CFilesystemLoader::listFiles(const std std::vector path; //vector holding relative path to our file bfs::recursive_directory_iterator enddir; +#if BOOST_VERSION >= 107200 // 1.72 + bfs::recursive_directory_iterator it(baseDirectory, bfs::directory_options::follow_directory_symlink); +#else bfs::recursive_directory_iterator it(baseDirectory, bfs::symlink_option::recurse); +#endif for(; it != enddir; ++it) { EResType::Type type; +#if BOOST_VERSION >= 107200 + const auto currentDepth = it.depth(); +#else + const auto currentDepth = it.level(); +#endif if (bfs::is_directory(it->status())) { - path.resize(it.level() + 1); + path.resize(currentDepth + 1); path.back() = it->path().filename(); // don't iterate into directory if depth limit reached - it.no_push(depth <= it.level()); +#if BOOST_VERSION >= 107200 + it.disable_recursion_pending(depth <= currentDepth); +#else + it.no_push(depth <= currentDepth); +#endif type = EResType::DIRECTORY; } @@ -134,7 +147,7 @@ std::unordered_map CFilesystemLoader::listFiles(const std { //reconstruct relative filename (not possible via boost AFAIK) bfs::path filename; - const size_t iterations = std::min((size_t)it.level(), path.size()); + const size_t iterations = std::min(static_cast(currentDepth), path.size()); if (iterations) { filename = path.front(); diff --git a/mapeditor/resourceExtractor/ResourceConverter.cpp b/mapeditor/resourceExtractor/ResourceConverter.cpp index d2be795b1..f08cd5e49 100644 --- a/mapeditor/resourceExtractor/ResourceConverter.cpp +++ b/mapeditor/resourceExtractor/ResourceConverter.cpp @@ -33,36 +33,32 @@ void ResourceConverter::convertExtractedResourceFiles(ConversionOptions conversi void ResourceConverter::doConvertPcxToPng(bool deleteOriginals) { - std::string filename; - bfs::path imagesPath = VCMIDirs::get().userExtractedPath() / "IMAGES"; bfs::directory_iterator end_iter; for(bfs::directory_iterator dir_itr(imagesPath); dir_itr != end_iter; ++dir_itr) { + const auto filename = dir_itr->path().filename(); try { if (!bfs::is_regular_file(dir_itr->status())) return; - std::string filePath = dir_itr->path().string(); - std::string fileStem = dir_itr->path().stem().string(); - filename = dir_itr->path().filename().string(); - std::string filenameLowerCase = boost::locale::to_lower(filename); + std::string filenameLowerCase = boost::algorithm::to_lower_copy(filename.string()); - if(bfs::extension(filenameLowerCase) == ".pcx") + if(boost::algorithm::to_lower_copy(filename.extension().string()) == ".pcx") { auto img = BitmapHandler::loadBitmap(filenameLowerCase); - bfs::path pngFilePath = imagesPath / (fileStem + ".png"); + bfs::path pngFilePath = imagesPath / (dir_itr->path().stem().string() + ".png"); img.save(pathToQString(pngFilePath), "PNG"); if(deleteOriginals) - bfs::remove(filePath); + bfs::remove(dir_itr->path()); } } catch(const std::exception & ex) { - logGlobal->info(filename + " " + ex.what() + "\n"); + logGlobal->info(filename.string() + " " + ex.what() + "\n"); } } } From 2de48624bcecebda677bca035bc50ad2b8c21ee6 Mon Sep 17 00:00:00 2001 From: Andrey Filipenkov Date: Sat, 14 Jan 2023 20:29:42 +0300 Subject: [PATCH 21/26] [iOS] fix linking QtCore --- launcher/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 5764c38a1..040bf52e1 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -128,6 +128,11 @@ enable_pch(vcmilauncher) if(APPLE_IOS) set(ICONS_DESTINATION ${DATA_DIR}) + # TODO: remove after fixing Conan's Qt recipe + if(XCODE_VERSION VERSION_GREATER_EQUAL 14.0) + target_link_libraries(vcmilauncher "-framework IOKit") + endif() + # workaround https://github.com/conan-io/conan-center-index/issues/13332 if(USING_CONAN) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/QIOSIntegrationPlugin.h From 9e7e649d377793fade626ff55bc05c8c6e1762ec Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sun, 8 Jan 2023 22:44:47 +0100 Subject: [PATCH 22/26] Gradual fade-in of built building --- client/gui/CAnimation.cpp | 16 +++++++++++----- client/gui/CAnimation.h | 4 ++-- client/widgets/Images.cpp | 4 ++-- client/widgets/Images.h | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/client/gui/CAnimation.cpp b/client/gui/CAnimation.cpp index ef1b92115..2f502856d 100644 --- a/client/gui/CAnimation.cpp +++ b/client/gui/CAnimation.cpp @@ -94,8 +94,8 @@ public: // Keep the original palette, in order to do color switching operation void savePalette(); - void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr, ui8 alpha=255) const override; - void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha=255) const override; + void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr, ui8 alpha=255) override; + void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha=255) override; std::shared_ptr scaleFast(float scale) const override; void exportBitmap(const boost::filesystem::path & path) const override; void playerColored(PlayerColor player) override; @@ -642,17 +642,17 @@ SDLImage::SDLImage(std::string filename) } } -void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src, ui8 alpha) const +void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src, ui8 alpha) { if(!surf) return; Rect destRect(posX, posY, surf->w, surf->h); - draw(where, &destRect, src); + draw(where, &destRect, src, alpha); } -void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* src, ui8 alpha) const +void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* src, ui8 alpha) { if (!surf) return; @@ -663,6 +663,12 @@ void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* sr if(src) { + if(alpha != UINT8_MAX) + { + const ColorShifterMultiplyAndAdd alphaShifter ({255, 255, 255, alpha}, {0, 0, 0, 0}); + adjustPalette(&alphaShifter); + } + if(src->x < margins.x) destShift.x += margins.x - src->x; diff --git a/client/gui/CAnimation.h b/client/gui/CAnimation.h index 7263f3ee2..7284f0625 100644 --- a/client/gui/CAnimation.h +++ b/client/gui/CAnimation.h @@ -40,8 +40,8 @@ public: using SpecialPalette = std::array; //draws image on surface "where" at position - virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr, ui8 alpha = 255) const=0; - virtual void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha = 255) const = 0; + virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr, ui8 alpha = 255) = 0; + virtual void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha = 255) = 0; virtual std::shared_ptr scaleFast(float scale) const = 0; diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index db4c7925e..6191f281c 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -339,7 +339,7 @@ void CAnimImage::playerColored(PlayerColor currPlayer) anim->getImage(0, group)->playerColored(player); } -CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group): +CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group, uint8_t alpha): anim(std::make_shared(name)), group(Group), frame(0), @@ -349,7 +349,7 @@ CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Del flags(Flags), xOffset(0), yOffset(0), - alpha(255) + alpha(alpha) { anim->loadGroup(group); last = anim->size(group); diff --git a/client/widgets/Images.h b/client/widgets/Images.h index cf0b64855..4cf125c13 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -142,7 +142,7 @@ public: //Set per-surface alpha, 0 = transparent, 255 = opaque void setAlpha(ui32 alphaValue); - CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0); + CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0, uint8_t alpha = 255); ~CShowableAnim(); //set animation to group or part of group From 3596d167a7b4c3af66cd2eb155c082fa4956b917 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Wed, 11 Jan 2023 19:57:42 +0100 Subject: [PATCH 23/26] Adjust code to develop + change place in code where alpha is applied --- client/gui/CAnimation.cpp | 17 +++++------------ client/gui/CAnimation.h | 4 ++-- client/gui/Canvas.cpp | 12 ++++++------ client/gui/Canvas.h | 3 ++- client/widgets/Images.cpp | 11 ++++++++++- client/widgets/Images.h | 2 +- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/client/gui/CAnimation.cpp b/client/gui/CAnimation.cpp index 2f502856d..5a76e2bdc 100644 --- a/client/gui/CAnimation.cpp +++ b/client/gui/CAnimation.cpp @@ -94,8 +94,8 @@ public: // Keep the original palette, in order to do color switching operation void savePalette(); - void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr, ui8 alpha=255) override; - void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha=255) override; + void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr) const override; + void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src) const override; std::shared_ptr scaleFast(float scale) const override; void exportBitmap(const boost::filesystem::path & path) const override; void playerColored(PlayerColor player) override; @@ -642,17 +642,16 @@ SDLImage::SDLImage(std::string filename) } } -void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src, ui8 alpha) +void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src) const { if(!surf) return; Rect destRect(posX, posY, surf->w, surf->h); - - draw(where, &destRect, src, alpha); + draw(where, &destRect, src); } -void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* src, ui8 alpha) +void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* src) const { if (!surf) return; @@ -663,12 +662,6 @@ void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* sr if(src) { - if(alpha != UINT8_MAX) - { - const ColorShifterMultiplyAndAdd alphaShifter ({255, 255, 255, alpha}, {0, 0, 0, 0}); - adjustPalette(&alphaShifter); - } - if(src->x < margins.x) destShift.x += margins.x - src->x; diff --git a/client/gui/CAnimation.h b/client/gui/CAnimation.h index 7284f0625..dcfb1cb64 100644 --- a/client/gui/CAnimation.h +++ b/client/gui/CAnimation.h @@ -40,8 +40,8 @@ public: using SpecialPalette = std::array; //draws image on surface "where" at position - virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr, ui8 alpha = 255) = 0; - virtual void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha = 255) = 0; + virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr) const = 0; + virtual void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src) const = 0; virtual std::shared_ptr scaleFast(float scale) const = 0; diff --git a/client/gui/Canvas.cpp b/client/gui/Canvas.cpp index de300973f..1ca09a147 100644 --- a/client/gui/Canvas.cpp +++ b/client/gui/Canvas.cpp @@ -70,12 +70,12 @@ void Canvas::draw(std::shared_ptr image, const Point & pos, const Rect & image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect); } -void Canvas::draw(std::shared_ptr image, const Point & pos, const Rect & sourceRect, uint8_t alpha) -{ - assert(image); - if (image) - image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect, alpha); -} +//void Canvas::draw(std::shared_ptr image, const Point & pos, const Rect & sourceRect, uint8_t alpha) +//{ +// assert(image); +// if (image) +// image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect, alpha); +//} void Canvas::draw(Canvas & image, const Point & pos) { diff --git a/client/gui/Canvas.h b/client/gui/Canvas.h index 2a156ea3a..de94d4eee 100644 --- a/client/gui/Canvas.h +++ b/client/gui/Canvas.h @@ -52,7 +52,8 @@ public: void draw(std::shared_ptr image, const Point & pos, const Rect & sourceRect); /// renders section of image bounded by sourceRect at specified position at specific transparency value - void draw(std::shared_ptr image, const Point & pos, const Rect & sourceRect, uint8_t alpha); + /// disabled for now as never used in code with alpha support +// void draw(std::shared_ptr image, const Point & pos, const Rect & sourceRect, uint8_t alpha); /// renders another canvas onto this canvas diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 6191f281c..26edda874 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -16,6 +16,7 @@ #include "../gui/SDL_Pixels.h" #include "../gui/CGuiHandler.h" #include "../gui/CCursorHandler.h" +#include "../gui/ColorFilter.h" #include "../battle/BattleInterface.h" #include "../battle/BattleInterfaceClasses.h" @@ -454,7 +455,15 @@ void CShowableAnim::blitImage(size_t frame, size_t group, SDL_Surface *to) Rect src( xOffset, yOffset, pos.w, pos.h); auto img = anim->getImage(frame, group); if(img) - img->draw(to, pos.x, pos.y, &src, alpha); + { + if(alpha != UINT8_MAX) + { + const ColorFilter alphaFilter = ColorFilter::genAlphaShifter(vstd::lerp(0.0f, 1.0f, alpha/255.0f)); + img->adjustPalette(alphaFilter); + } + + img->draw(to, pos.x, pos.y, &src); + } } void CShowableAnim::rotate(bool on, bool vertical) diff --git a/client/widgets/Images.h b/client/widgets/Images.h index 4cf125c13..6150bfc8b 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -142,7 +142,7 @@ public: //Set per-surface alpha, 0 = transparent, 255 = opaque void setAlpha(ui32 alphaValue); - CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0, uint8_t alpha = 255); + CShowableAnim(int x, int y, std::string name, ui8 flags=0, ui32 Delay=4, size_t Group=0, uint8_t alpha = UINT8_MAX); ~CShowableAnim(); //set animation to group or part of group From 8b2fc5198e85ccf74c7e55fbec1d59b3a875c549 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Wed, 11 Jan 2023 23:32:24 +0100 Subject: [PATCH 24/26] Remove condition that causes unintended behavior --- client/widgets/Images.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 26edda874..3ede9f5cb 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -456,11 +456,8 @@ void CShowableAnim::blitImage(size_t frame, size_t group, SDL_Surface *to) auto img = anim->getImage(frame, group); if(img) { - if(alpha != UINT8_MAX) - { - const ColorFilter alphaFilter = ColorFilter::genAlphaShifter(vstd::lerp(0.0f, 1.0f, alpha/255.0f)); - img->adjustPalette(alphaFilter); - } + const ColorFilter alphaFilter = ColorFilter::genAlphaShifter(vstd::lerp(0.0f, 1.0f, alpha/255.0f)); + img->adjustPalette(alphaFilter); img->draw(to, pos.x, pos.y, &src); } From 4fb554847529c3b7a3b4054863be657a8d1b007e Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sun, 15 Jan 2023 00:09:28 +0100 Subject: [PATCH 25/26] Remove unused stuff from code --- client/gui/Canvas.cpp | 7 ------- client/gui/Canvas.h | 5 ----- 2 files changed, 12 deletions(-) diff --git a/client/gui/Canvas.cpp b/client/gui/Canvas.cpp index 1ca09a147..0d7fc4fcf 100644 --- a/client/gui/Canvas.cpp +++ b/client/gui/Canvas.cpp @@ -70,13 +70,6 @@ void Canvas::draw(std::shared_ptr image, const Point & pos, const Rect & image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect); } -//void Canvas::draw(std::shared_ptr image, const Point & pos, const Rect & sourceRect, uint8_t alpha) -//{ -// assert(image); -// if (image) -// image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect, alpha); -//} - void Canvas::draw(Canvas & image, const Point & pos) { blitAt(image.surface, renderOffset.x + pos.x, renderOffset.y + pos.y, surface); diff --git a/client/gui/Canvas.h b/client/gui/Canvas.h index de94d4eee..80d16040e 100644 --- a/client/gui/Canvas.h +++ b/client/gui/Canvas.h @@ -51,11 +51,6 @@ public: /// renders section of image bounded by sourceRect at specified position void draw(std::shared_ptr image, const Point & pos, const Rect & sourceRect); - /// renders section of image bounded by sourceRect at specified position at specific transparency value - /// disabled for now as never used in code with alpha support -// void draw(std::shared_ptr image, const Point & pos, const Rect & sourceRect, uint8_t alpha); - - /// renders another canvas onto this canvas void draw(Canvas & image, const Point & pos); From bec894c3481907854cb57ed61e7b608e8390faf4 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 9 Jan 2023 00:24:11 +0200 Subject: [PATCH 26/26] Implemented automatic detection of H3 files language/encoding --- config/schemas/settings.json | 2 +- launcher/settingsView/csettingsview_moc.cpp | 2 + launcher/settingsView/csettingsview_moc.ui | 5 ++ lib/CGeneralTextHandler.cpp | 82 ++++++++++++++++++++- 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 0c78608b1..dd4a063f3 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -37,7 +37,7 @@ }, "encoding" : { "type" : "string", - "default" : "CP1252" + "default" : "auto" }, "swipe" : { "type" : "boolean", diff --git a/launcher/settingsView/csettingsview_moc.cpp b/launcher/settingsView/csettingsview_moc.cpp index 0d34a8e09..dc8d3a6cb 100644 --- a/launcher/settingsView/csettingsview_moc.cpp +++ b/launcher/settingsView/csettingsview_moc.cpp @@ -33,6 +33,8 @@ QString resolutionToString(const QSize & resolution) /// Note that it is possible to specify enconding manually in settings.json static const std::string knownEncodingsList[] = //TODO: remove hardcode { + // Asks vcmi to automatically detect encoding + "auto", // European Windows-125X encodings "CP1250", // West European, covers mostly Slavic languages that use latin script "CP1251", // Covers languages that use cyrillic scrypt diff --git a/launcher/settingsView/csettingsview_moc.ui b/launcher/settingsView/csettingsview_moc.ui index a9c12aadb..0d0894b72 100644 --- a/launcher/settingsView/csettingsview_moc.ui +++ b/launcher/settingsView/csettingsview_moc.ui @@ -526,6 +526,11 @@ + + + Automatic detection + + Central European (Windows 1250) diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index eed81242e..e365e5cf0 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -106,7 +106,84 @@ bool Unicode::isValidString(const char * data, size_t size) static std::string getSelectedEncoding() { - return settings["general"]["encoding"].String(); + auto explicitSetting = settings["general"]["encoding"].String(); + if (explicitSetting != "auto") + return explicitSetting; + return settings["session"]["encoding"].String(); +} + +/// Detects encoding of H3 text files based on matching against pregenerated footprints of H3 file +/// Can also detect language of H3 install, however right now this is not necessary +static void detectEncoding() +{ + static const size_t knownCount = 6; + + // "footprints" of data collected from known versions of H3 + static const std::array, knownCount> knownFootprints = + { { + { { 0.0559, 0.0000, 0.1983, 0.0051, 0.0222, 0.0183, 0.4596, 0.2405, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000 } }, + { { 0.0493, 0.0000, 0.1926, 0.0047, 0.0230, 0.0121, 0.4133, 0.2780, 0.0002, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0259, 0.0008 } }, + { { 0.0534, 0.0000, 0.1705, 0.0047, 0.0418, 0.0208, 0.4775, 0.2191, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0005, 0.0036, 0.0080 } }, + { { 0.0534, 0.0000, 0.1701, 0.0067, 0.0157, 0.0133, 0.4328, 0.2540, 0.0001, 0.0043, 0.0000, 0.0244, 0.0000, 0.0000, 0.0181, 0.0071 } }, + { { 0.0548, 0.0000, 0.1744, 0.0061, 0.0031, 0.0009, 0.0046, 0.0136, 0.0000, 0.0004, 0.0000, 0.0000, 0.0227, 0.0061, 0.4882, 0.2252 } }, + { { 0.0559, 0.0000, 0.1807, 0.0059, 0.0036, 0.0013, 0.0046, 0.0134, 0.0000, 0.0004, 0.0000, 0.0487, 0.0209, 0.0060, 0.4615, 0.1972 } } + } }; + + // languages of known footprints + static const std::array knownLanguages = + { { + "English", "French", "German", "Polish", "Russian", "Ukrainian" + } }; + + // encoding that should be used for known footprints + static const std::array knownEncodings = + { { + "CP1252", "CP1252", "CP1252", "CP1250", "CP1251", "CP1251" + } }; + + // load file that will be used for footprint generation + // this is one of the most text-heavy files in game and consists solely from translated texts + auto resource = CResourceHandler::get()->load(ResourceID("DATA/GENRLTXT.TXT", EResType::TEXT)); + + std::array charCount; + std::array footprint; + std::array deviations; + + boost::range::fill(charCount, 0); + boost::range::fill(footprint, 0.0); + boost::range::fill(deviations, 0.0); + + auto data = resource->readAll(); + + // compute how often each character occurs in input file + for (size_t i = 0; i < data.second; ++i) + charCount[data.first[i]] += 1; + + // and convert computed data into weights + // to reduce amount of data, group footprint data into 16-char blocks. + // While this will reduce precision, it should not affect output + // since we expect only tiny differences compared to reference footprints + for (size_t i = 0; i < 256; ++i) + footprint[i/16] += double(charCount[i]) / data.second; + + logGlobal->debug("Language footprint: %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", + footprint[0], footprint[1], footprint[2], footprint[3], footprint[4], footprint[5], footprint[6], footprint[7], + footprint[8], footprint[9], footprint[10], footprint[11], footprint[12], footprint[13], footprint[14], footprint[15] + ); + + for (size_t i = 0; i < deviations.size(); ++i) + { + for (size_t j = 0; j < footprint.size(); ++j) + deviations[i] += std::abs((footprint[j] - knownFootprints[i][j])); + } + + size_t bestIndex = boost::range::min_element(deviations) - deviations.begin(); + + for (size_t i = 0; i < deviations.size(); ++i) + logGlobal->debug("Comparing to %s: %f", knownLanguages[i], deviations[i]); + + Settings s = settings.write["session"]["encoding"]; + s->String() = knownEncodings[bestIndex]; } std::string Unicode::toUnicode(const std::string &text) @@ -364,6 +441,9 @@ CGeneralTextHandler::CGeneralTextHandler(): znpc00 (*this, "vcmi.znpc00" ), // technically - wog qeModCommands (*this, "vcmi.quickExchange" ) { + if (getSelectedEncoding().empty()) + detectEncoding(); + readToVector("core.vcdesc", "DATA/VCDESC.TXT" ); readToVector("core.lcdesc", "DATA/LCDESC.TXT" ); readToVector("core.tcommand", "DATA/TCOMMAND.TXT" );