From 954ea9d5a9a86b79caa15ba9a17ac0c9d01dd3ac Mon Sep 17 00:00:00 2001 From: nordsoft Date: Wed, 28 Dec 2022 01:14:10 +0400 Subject: [PATCH 01/54] Implement lasso brush --- mapeditor/icons/brush-3.png | Bin 0 -> 1485 bytes mapeditor/mainwindow.cpp | 16 ++++++++++ mapeditor/mainwindow.h | 2 ++ mapeditor/mainwindow.ui | 8 +++-- mapeditor/mapview.cpp | 58 ++++++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 mapeditor/icons/brush-3.png diff --git a/mapeditor/icons/brush-3.png b/mapeditor/icons/brush-3.png new file mode 100644 index 0000000000000000000000000000000000000000..19800c069ae5ce13875e18b5b7a04665709f4358 GIT binary patch literal 1485 zcmV;;1v2`HP)Hg1+lHrgWSWcKdPn90sKGrRqvPeo9CG3uKX#J{(IASm?@+di}}l?o-=)F3E6 zwD^Ni=!>T7nL9I?X}YoAW$t|Qo$sD|?zw001?ah|SeB6#0T!CBEf+H4bBB+JJu8re zhoBb*p;u8ID_yBf0ya+zcePvJL&AGs+11_tpRKn>9TgyPA7ZoSs0)aX0r00)%XR^J z`jH<$>RKN5V(7OqK*TS4xZz{h!*f1C3ECFkK$#7nA@pGN!$;%jYv zwjAKwmYb0gKL(K8-kPtb5${A?tlI~wzMrJ6wTdBr=Y%%%EaEMQ&o}4FQ^DA)s*}Z> z!FI&AHCpoWI|RUqx?7s@$8!5^Q=anY%X@i5{QA6kNcMelpE>R6eCYFpmMsVT zrI(b06~u#xf1yS}_UGdMvD``!0~u->P=lA4?YN`hilQ z|3tHka)7T{2CGqwjZfMwx$5irQN_*|e4l)UHmiYuz74Yp1t^#>hrJ3-SOXDcC_o0^ z7T9R1gAN8V6s;5)ieI5-7aQlmJn}lUna#nz!j%5V$X|o`xX!dHWQRV27P1=rj;t2b zW$~+pTw@bIek?ZvKPDL<64`^#UNTAck#RBsB6*5DP4<%UA_FqU$I>2EH_cM;u)Q~SI+rg`Rn{L z_AC5qq~L$#SMj%U$6Cz0vP{G5Y*=%5RT^yu;}-DInZ=349rJPVM6C3K^oO)8y(fJr{l>k`ead~!ea?NsT>_Ci%bnxC;Vy6= zb6>{xYV#Ue-+LB$7`JEXmTRm^AtP)R9u{)KHsMiWGV&)32xCG~*nyU<>-!d;FP=Re z4r3qYr~6#KE>;1F`>_J_P5xC?ROxV(DIHdCO*p$HRQI@7^PwV@Pvuf+ z5K}u-6REM(K@W$srgorh0{i?O)v0c>QtHxU-hBdD(>iYJ4b2sIOVX2K8m~4gmYVA5 zh^QEb$V`rCQ-|7ZS{nuL-t>?3n=-o(6I(7vocj#GzCZEo`!3>+v;dYIfPu#&ZWzzX z2i^rZ^Mu;6+rb@?NPG+6)c5T6zxpzGe*M(x+{AON=PiJ>H#?ob-|uwRK0yDg0B4PV z0id6JRRdfL?*IS*IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa4 z0RR91AfN*P1ONa40RR91AOHXW0FO>U$p8QWD@jB_R9Fe^R9g zAT3pYI%z6R9NRJ4N_jtT0&fBjCva@g|2iBY|Hm1&S|o^H!X35N_JlLN&p4`yPv{WP zxM^mND6Y)LTY$;vh(;RLy8@1{K)iGwB-wL!H|EoBqk+Y~FkmGhK#RnuV48s@-eYg1 z*{E0Wp4+>(7yNSqqUpIj*$epxU?s*RhPVg6wLl2427|gOj#qG}IQdimJ<)(zA{lQn z@Vt~NE(H4v4`{t?&5Rw1W@l&sdsydr>~oEujQ22%RiEs>J)L-2h)BeY%BmDShe%GN z78Y?9Mv;z7NX*-5Q;5_0Zq57^;MD&L#;b81qn|xQkYMjE6R{Z9F9{=2 nbS81lKs2|d|ETzu4I+{sT-1)tsZ|@Y00000NkvXXu0mjfTsXM{ literal 0 HcmV?d00001 diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index b8408b0f5..4bdbcc98e 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -839,6 +839,22 @@ void MainWindow::on_toolArea_clicked(bool checked) ui->tabWidget->setCurrentIndex(0); } +void MainWindow::on_toolLasso_clicked(bool checked) +{ + ui->toolBrush->setChecked(false); + ui->toolBrush2->setChecked(false); + ui->toolBrush4->setChecked(false); + ui->toolArea->setChecked(false); + //ui->toolLasso->setChecked(false); + + if(checked) + ui->mapView->selectionTool = MapView::SelectionTool::Lasso; + else + ui->mapView->selectionTool = MapView::SelectionTool::None; + + ui->tabWidget->setCurrentIndex(0); +} + void MainWindow::on_actionErase_triggered() { on_toolErase_clicked(); diff --git a/mapeditor/mainwindow.h b/mapeditor/mainwindow.h index ccd410aa8..d367525bd 100644 --- a/mapeditor/mainwindow.h +++ b/mapeditor/mainwindow.h @@ -87,6 +87,8 @@ private slots: void on_toolBrush2_clicked(bool checked); void on_toolBrush4_clicked(bool checked); + + void on_toolLasso_clicked(bool checked); void on_inspectorWidget_itemChanged(QTableWidgetItem *item); diff --git a/mapeditor/mainwindow.ui b/mapeditor/mainwindow.ui index 766114052..6e7522956 100644 --- a/mapeditor/mainwindow.ui +++ b/mapeditor/mainwindow.ui @@ -642,7 +642,7 @@ - false + true @@ -663,7 +663,11 @@ - O + + + + + icons:brush-3.pngicons:brush-3.png true diff --git a/mapeditor/mapview.cpp b/mapeditor/mapview.cpp index e24a990cb..6d91481ae 100644 --- a/mapeditor/mapview.cpp +++ b/mapeditor/mapview.cpp @@ -155,6 +155,14 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) } sc->selectionTerrainView.draw(); break; + + case MapView::SelectionTool::Lasso: + if(mouseEvent->buttons() == Qt::LeftButton) + { + sc->selectionTerrainView.select(tile); + sc->selectionTerrainView.draw(); + } + break; case MapView::SelectionTool::None: if(mouseEvent->buttons() & Qt::RightButton) @@ -247,6 +255,7 @@ void MapView::mousePressEvent(QMouseEvent *event) break; case MapView::SelectionTool::Area: + case MapView::SelectionTool::Lasso: if(event->button() == Qt::RightButton) break; @@ -329,6 +338,55 @@ void MapView::mouseReleaseEvent(QMouseEvent *event) switch(selectionTool) { + case MapView::SelectionTool::Lasso: { + if(event->button() == Qt::RightButton) + break; + + //key: y position of tile + //value.first: x position of left tile + //value.second: x postiion of right tile + std::map> selectionRangeMapX, selectionRangeMapY; + for(auto & t : sc->selectionTerrainView.selection()) + { + auto pairIter = selectionRangeMapX.find(t.y); + if(pairIter == selectionRangeMapX.end()) + selectionRangeMapX[t.y] = std::make_pair(t.x, t.x); + else + { + pairIter->second.first = std::min(pairIter->second.first, t.x); + pairIter->second.second = std::max(pairIter->second.second, t.x); + } + + pairIter = selectionRangeMapY.find(t.x); + if(pairIter == selectionRangeMapY.end()) + selectionRangeMapY[t.x] = std::make_pair(t.y, t.y); + else + { + pairIter->second.first = std::min(pairIter->second.first, t.y); + pairIter->second.second = std::max(pairIter->second.second, t.y); + } + } + + std::set selectionByX, selectionByY; + std::vector finalSelection; + for(auto & selectionRange : selectionRangeMapX) + { + for(int i = selectionRange.second.first; i < selectionRange.second.second; ++i) + selectionByX.insert(int3(i, selectionRange.first, sc->level)); + } + for(auto & selectionRange : selectionRangeMapY) + { + for(int i = selectionRange.second.first; i < selectionRange.second.second; ++i) + selectionByY.insert(int3(selectionRange.first, i, sc->level)); + } + std::set_intersection(selectionByX.begin(), selectionByX.end(), selectionByY.begin(), selectionByY.end(), std::back_inserter(finalSelection)); + for(auto & lassoTile : finalSelection) + sc->selectionTerrainView.select(lassoTile); + + sc->selectionTerrainView.draw(); + break; + } + case MapView::SelectionTool::None: if(event->button() == Qt::RightButton) break; From 9f9798d3a4377d8021ee686af48d1f442190644b Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Mon, 2 Jan 2023 14:11:02 +0200 Subject: [PATCH 02/54] 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 03/54] 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 04/54] 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 05/54] 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 06/54] 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 07/54] 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 08/54] 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 09/54] 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 3fd9a14651bfb32b01cd6237f420282d731494fe Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 10 Jan 2023 12:25:23 +0200 Subject: [PATCH 10/54] Fix #1401 - Restored accessing creature names via GeneralTextHandler --- lib/CGeneralTextHandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 08746da4f..eed81242e 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -374,6 +374,8 @@ CGeneralTextHandler::CGeneralTextHandler(): readToVector("core.restypes", "DATA/RESTYPES.TXT" ); readToVector("core.terrname", "DATA/TERRNAME.TXT" ); readToVector("core.randsign", "DATA/RANDSIGN.TXT" ); + readToVector("core.crgen1", "DATA/CRGEN1.TXT" ); + readToVector("core.crgen4", "DATA/CRGEN4.TXT" ); readToVector("core.overview", "DATA/OVERVIEW.TXT" ); readToVector("core.arraytxt", "DATA/ARRAYTXT.TXT" ); readToVector("core.priskill", "DATA/PRISKILL.TXT" ); From 9b77215b2324e88d13fbd06b32ec631bda8273b9 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 10 Jan 2023 18:44:36 +0200 Subject: [PATCH 11/54] Attempt to fix slowdown in daily buid - revert MXE --- CI/mxe/before_install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CI/mxe/before_install.sh b/CI/mxe/before_install.sh index d1a3b96d8..0a4855f2d 100644 --- a/CI/mxe/before_install.sh +++ b/CI/mxe/before_install.sh @@ -14,8 +14,8 @@ sudo add-apt-repository 'deb http://security.ubuntu.com/ubuntu bionic-security m sudo apt-get install -qq nsis ninja-build libssl1.0.0 # MXE repository was too slow for Travis far too often -wget -nv https://github.com/vcmi/vcmi-deps-mxe/releases/download/2022-12-26/mxe-i686-w64-mingw32.shared-2022-12-26.tar -tar -xvf mxe-i686-w64-mingw32.shared-2022-12-26.tar +wget -nv https://github.com/vcmi/vcmi-deps-mxe/releases/download/2021-02-20/mxe-i686-w64-mingw32.shared-2021-01-22.tar +tar -xvf mxe-i686-w64-mingw32.shared-2021-01-22.tar sudo dpkg -i mxe-*.deb sudo apt-get install -f --yes 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 12/54] 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 76540bd9605c41776d23a9c1bad891bb9a4323ec Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 11 Jan 2023 13:27:43 +0200 Subject: [PATCH 13/54] Fix tactics buttons positioning --- config/widgets/battleWindow.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/widgets/battleWindow.json b/config/widgets/battleWindow.json index b83b2e850..21261f32e 100644 --- a/config/widgets/battleWindow.json +++ b/config/widgets/battleWindow.json @@ -115,7 +115,7 @@ { "type": "button", "name": "tacticNext", - "position": {"x": 213, "y": 4}, + "position": {"x": 213, "y": 560}, "image": "icm011", "callback": "tacticNext", "hotkey": "space" @@ -124,7 +124,7 @@ { "type": "button", "name": "tacticEnd", - "position": {"x": 419, "y": 4}, + "position": {"x": 419, "y": 560}, "image": "icm012", "callback": "tacticEnd", "hotkey": "enter" From 5e72ef76cfd214d1e1d5c059bc5e183f83b5c76b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 10 Jan 2023 16:24:59 +0200 Subject: [PATCH 14/54] Fix quest texts accessing --- lib/mapObjects/CQuest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 7ee8d62a3..d945c5eb6 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -588,7 +588,7 @@ void CGSeerHut::initObj(CRandomGenerator & rand) quest->progress = CQuest::NOT_ACTIVE; if(quest->missionType) { - std::string questName = quest->missionName(CQuest::Emission(quest->missionType-1)); + std::string questName = quest->missionName(quest->missionType); if(!quest->isCustomFirst) quest->firstVisitText = VLC->generaltexth->translate("core.seerhut.quest." + questName + "." + quest->missionState(0), quest->textOption); From e1880604806e649fb189b21ce632d991375e6c05 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 9 Jan 2023 23:27:38 +0200 Subject: [PATCH 15/54] 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 16/54] 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 17/54] [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 18/54] [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 19/54] [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 20/54] [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 21/54] [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 22/54] [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 23/54] [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 24/54] 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 25/54] 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 26/54] [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 27/54] 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 28/54] 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 29/54] 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 30/54] 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 31/54] 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" ); From cbddf2d8fbae9ec7fef7c7016f9a12cb9c213ec4 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sun, 15 Jan 2023 17:22:53 +0100 Subject: [PATCH 32/54] Adjust newly built building appearance and outline blinking speed --- client/windows/CCastleInterface.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 31ba43873..f178c2ea5 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -47,7 +47,7 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town parent(Par), town(Town), str(Str), - stateCounter(80) + stateCounter(150) { addUsedEvents(LCLICK | RCLICK | HOVER); pos.x += str->pos.x; @@ -154,12 +154,12 @@ SDL_Color multiplyColors(const SDL_Color & b, const SDL_Color & a, double f) void CBuildingRect::show(SDL_Surface * to) { - const ui32 stageDelay = 16; + const ui32 stageDelay = 30; - const ui32 S1_TRANSP = 16; //0.5 sec building appear 0->100 transparency - const ui32 S2_WHITE_B = 32; //0.5 sec border glows from white to yellow - const ui32 S3_YELLOW_B= 48; //0.5 sec border glows from yellow to normal - const ui32 BUILDED = 80; // 1 sec delay, nothing happens + const ui32 S1_TRANSP = 30; //0.5 sec building appear 0->100 transparency + const ui32 S2_WHITE_B = 60; //0.5 sec border glows from white to yellow + const ui32 S3_YELLOW_B= 90; //0.5 sec border glows from yellow to normal + const ui32 BUILDED = 150; // 1 sec delay, nothing happens if(stateCounter < S1_TRANSP) { From 6572f5697ec7118def4fc2d14664362d2399d29e Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sun, 15 Jan 2023 19:10:40 +0100 Subject: [PATCH 33/54] Fix wrong rendering of castle shipyard --- client/windows/CCastleInterface.cpp | 14 ++++++++++++++ config/factions/castle.json | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 31ba43873..8252436db 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -53,6 +53,20 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town pos.x += str->pos.x; pos.y += str->pos.y; + // special animation frame manipulation for castle shipyard with and without ship + // done due to .def used in special way, not to animate building - first image is for shipyard without citadel moat, 2nd image is for including moat + if(Town->town->faction->getId() == FactionID::CASTLE && Str->building && + (Str->building->bid == BuildingID::SHIPYARD || Str->building->bid == BuildingID::SHIP)) + { + if(Town->hasBuilt(BuildingID::CITADEL)) + { + this->first = 1; + this->frame = 1; + } + else + this->last = 0; + } + if(!str->borderName.empty()) border = BitmapHandler::loadBitmap(str->borderName); else diff --git a/config/factions/castle.json b/config/factions/castle.json index 33a0006d8..7d48b39c3 100644 --- a/config/factions/castle.json +++ b/config/factions/castle.json @@ -84,7 +84,7 @@ "mageGuild3": { "animation" : "TBCSMAG3.def", "x" : 704, "y" : 107, "z" : 1, "border" : "TOCSM301.bmp", "area" : "TZCSM301.bmp" }, "mageGuild4": { "animation" : "TBCSMAG4.def", "x" : 704, "y" : 76, "z" : 1, "border" : "TOCSM401.bmp", "area" : "TZCSM401.bmp" }, "tavern": { "animation" : "TBCSTVRN.def", "x" : 0, "y" : 230, "z" : 1, "border" : "TOCSTAV1.bmp", "area" : "TZCSTAV1.bmp" }, - "shipyard": { "animation" : "TBCSDOCK.def", "x" : 478, "y" : 134, "border" : "TOCSDKMS.bmp", "area" : "TZCSDKMS.bmp" }, + "shipyard": { "animation" : "TBCSDOCK.def", "x" : 478, "y" : 134, "z" : 1, "border" : "TOCSDKMS.bmp", "area" : "TZCSDKMS.bmp" }, "fort": { "animation" : "TBCSCSTL.def", "x" : 595, "y" : 66, "border" : "TOCSCAS1.bmp", "area" : "TZCSCAS1.bmp" }, "citadel": { "animation" : "TBCSCAS2.def", "x" : 478, "y" : 66, "border" : "TOCSCAS2.bmp", "area" : "TZCSCAS2.bmp" }, "castle": { "animation" : "TBCSCAS3.def", "x" : 478, "y" : 37, "border" : "TOCSCAS3.bmp", "area" : "TZCSCAS3.bmp" }, @@ -98,7 +98,7 @@ "special1": { "animation" : "TBCSSPEC.def", "x" : 533, "y" : 71, "border" : "TOCSLT01.bmp", "area" : "TZCSLT01.bmp" }, "horde1": { "animation" : "TBCSHRD1.def", "x" : 76, "y" : 53, "z" : -1, "border" : "TOCSGR1H.bmp", "area" : "TZCSGR1H.bmp", "hidden" : true }, "horde1Upgr": { "animation" : "TBCSHRD2.def", "x" : 76, "y" : 35, "z" : -1, "border" : "TOCSGR2H.bmp", "area" : "TZCSGR2H.bmp", "hidden" : true, "builds" : "horde1" }, - "ship": { "animation" : "TBCSBOAT.def", "x" : 478, "y" : 134, "border" : "TOCSDKMN.bmp", "area" : "TZCSDKMN.bmp", "hidden" : true }, + "ship": { "animation" : "TBCSBOAT.def", "x" : 478, "y" : 134, "z" : 1, "border" : "TOCSDKMN.bmp", "area" : "TZCSDKMN.bmp", "hidden" : true }, "special2": { "animation" : "TBCSEXT0.def", "x" : 384, "y" : 193, "z" : -2, "border" : "TOCSCAVM.bmp", "area" : "TZCSCAVM.bmp" }, "special3": { "animation" : "TBCSEXT1.def", "x" : 0, "y" : 198, "z" : 1, "border" : "TOCSTAV2.bmp", "area" : "TZCSTAV2.bmp" }, "grail": { "animation" : "TBCSHOLY.def", "x" : 456, "y" : 109, "z" : -1, "border" : "TOCSHOLY.bmp", "area" : "TZCSHOLY.bmp" }, From 7dd7f4af548b7ae525d2dc3676f73f92441494d5 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Sun, 15 Jan 2023 19:56:55 +0100 Subject: [PATCH 34/54] Change to time-based frame counting, easier than I though --- client/windows/CCastleInterface.cpp | 44 ++++++++++++++--------------- client/windows/CCastleInterface.h | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index f178c2ea5..0d5ba23ef 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -44,10 +44,10 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town, const CStructure * Str) : CShowableAnim(0, 0, Str->defName, CShowableAnim::BASE), - parent(Par), - town(Town), - str(Str), - stateCounter(150) + parent(Par), + town(Town), + str(Str), + stateTimeCounter(2500) { addUsedEvents(LCLICK | RCLICK | HOVER); pos.x += str->pos.x; @@ -154,16 +154,16 @@ SDL_Color multiplyColors(const SDL_Color & b, const SDL_Color & a, double f) void CBuildingRect::show(SDL_Surface * to) { - const ui32 stageDelay = 30; + const ui32 stageDelay = 500; - const ui32 S1_TRANSP = 30; //0.5 sec building appear 0->100 transparency - const ui32 S2_WHITE_B = 60; //0.5 sec border glows from white to yellow - const ui32 S3_YELLOW_B= 90; //0.5 sec border glows from yellow to normal - const ui32 BUILDED = 150; // 1 sec delay, nothing happens + const ui32 S1_TRANSP = 500; //500 msec building appear 0->100 transparency + const ui32 S2_WHITE_B = 1000; //500 msec border glows from white to yellow + const ui32 S3_YELLOW_B= 1500; //500 msec border glows from yellow to normal + const ui32 BUILDED = 2500; //1000 msec delay, nothing happens - if(stateCounter < S1_TRANSP) + if(stateTimeCounter < S1_TRANSP) { - setAlpha(255*stateCounter/stageDelay); + setAlpha(255 * stateTimeCounter / stageDelay); CShowableAnim::show(to); } else @@ -172,9 +172,9 @@ void CBuildingRect::show(SDL_Surface * to) CShowableAnim::show(to); } - if(border && stateCounter > S1_TRANSP) + if(border && stateTimeCounter > S1_TRANSP) { - if(stateCounter == BUILDED) + if(stateTimeCounter >= BUILDED) { if(parent->selectedBuilding == this) blitAtLoc(border,0,0,to); @@ -191,11 +191,11 @@ void CBuildingRect::show(SDL_Surface * to) SDL_Color oldColor = border->format->palette->colors[colorID]; SDL_Color newColor; - if (stateCounter < S2_WHITE_B) - newColor = multiplyColors(c1, c2, static_cast(stateCounter % stageDelay) / stageDelay); + if (stateTimeCounter < S2_WHITE_B) + newColor = multiplyColors(c1, c2, static_cast(stateTimeCounter % stageDelay) / stageDelay); else - if (stateCounter < S3_YELLOW_B) - newColor = multiplyColors(c2, c3, static_cast(stateCounter % stageDelay) / stageDelay); + if (stateTimeCounter < S3_YELLOW_B) + newColor = multiplyColors(c2, c3, static_cast(stateTimeCounter % stageDelay) / stageDelay); else newColor = oldColor; @@ -204,13 +204,13 @@ void CBuildingRect::show(SDL_Surface * to) SDL_SetColors(border, &oldColor, colorID, 1); } } - if(stateCounter < BUILDED) - stateCounter++; + if(stateTimeCounter < BUILDED) + stateTimeCounter += GH.mainFPSmng->getElapsedMilliseconds(); } void CBuildingRect::showAll(SDL_Surface * to) { - if (stateCounter == 0) + if (stateTimeCounter == 0) return; CShowableAnim::showAll(to); @@ -632,9 +632,9 @@ void CCastleBuildings::addBuilding(BuildingID building) { //reset animation if(structures.size() == 1) - buildingRect->stateCounter = 0; // transparency -> fully visible stage + buildingRect->stateTimeCounter = 0; // transparency -> fully visible stage else - buildingRect->stateCounter = 16; // already in fully visible stage + buildingRect->stateTimeCounter = 500; // already in fully visible stage break; } } diff --git a/client/windows/CCastleInterface.h b/client/windows/CCastleInterface.h index 915e7579d..55bdc31d0 100644 --- a/client/windows/CCastleInterface.h +++ b/client/windows/CCastleInterface.h @@ -51,7 +51,7 @@ public: SDL_Surface* border; SDL_Surface* area; - ui32 stateCounter;//For building construction - current stage in animation + ui32 stateTimeCounter;//For building construction - current stage in animation CBuildingRect(CCastleBuildings * Par, const CGTownInstance *Town, const CStructure *Str); ~CBuildingRect(); From d44e80936961a96e491143401e5b27c0eeeb164c Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 5 Jan 2023 19:03:27 +0200 Subject: [PATCH 35/54] Refactoring of CursorHandler, in preparation for HW cursor routines --- client/gui/CCursorHandler.cpp | 284 +++++++++++++++++----------------- client/gui/CCursorHandler.h | 22 ++- 2 files changed, 161 insertions(+), 145 deletions(-) diff --git a/client/gui/CCursorHandler.cpp b/client/gui/CCursorHandler.cpp index bd9726a49..6d42b917c 100644 --- a/client/gui/CCursorHandler.cpp +++ b/client/gui/CCursorHandler.cpp @@ -44,11 +44,11 @@ CCursorHandler::CCursorHandler() , cursorLayer(nullptr) , frameTime(0.f) , showing(false) + , pos(0,0) { cursorLayer = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 40, 40); SDL_SetTextureBlendMode(cursorLayer, SDL_BLENDMODE_BLEND); - xpos = ypos = 0; type = Cursor::Type::DEFAULT; dndObject = nullptr; @@ -72,7 +72,7 @@ CCursorHandler::CCursorHandler() Point CCursorHandler::position() const { - return Point(xpos, ypos); + return pos; } void CCursorHandler::changeGraphic(Cursor::Type type, size_t index) @@ -127,171 +127,175 @@ void CCursorHandler::dragAndDropCursor(std::unique_ptr object) void CCursorHandler::cursorMove(const int & x, const int & y) { - xpos = x; - ypos = y; + pos.x = x; + pos.y = y; } -void CCursorHandler::shiftPos( int &x, int &y ) +Point CCursorHandler::getPivotOffsetDefault(size_t index) { - if(( type == Cursor::Type::COMBAT && frame != static_cast(Cursor::Combat::POINTER)) || type == Cursor::Type::SPELLBOOK) - { - x-=16; - y-=16; + return {0, 0}; +} - // Properly align the melee attack cursors. - if (type == Cursor::Type::COMBAT) - { - switch (static_cast(frame)) - { - case Cursor::Combat::HIT_NORTHEAST: - x -= 6; - y += 16; - break; - case Cursor::Combat::HIT_EAST: - x -= 16; - y += 10; - break; - case Cursor::Combat::HIT_SOUTHEAST: - x -= 6; - y -= 6; - break; - case Cursor::Combat::HIT_SOUTHWEST: - x += 16; - y -= 6; - break; - case Cursor::Combat::HIT_WEST: - x += 16; - y += 11; - break; - case Cursor::Combat::HIT_NORTHWEST: - x += 16; - y += 16; - break; - case Cursor::Combat::HIT_NORTH: - x += 9; - y += 16; - break; - case Cursor::Combat::HIT_SOUTH: - x += 9; - y -= 15; - break; - } - } - } - else if(type == Cursor::Type::ADVENTURE) - { - if (frame == 0) - { - //no-op - } - else if(frame == 2) - { - x -= 12; - y -= 10; - } - else if(frame == 3) - { - x -= 12; - y -= 12; - } - else if(frame < 27) - { - int hlpNum = (frame - 4)%6; - if(hlpNum == 0) - { - x -= 15; - y -= 13; - } - else if(hlpNum == 1) - { - x -= 13; - y -= 13; - } - else if(hlpNum == 2) - { - x -= 20; - y -= 20; - } - else if(hlpNum == 3) - { - x -= 13; - y -= 16; - } - else if(hlpNum == 4) - { - x -= 8; - y -= 9; - } - else if(hlpNum == 5) - { - x -= 14; - y -= 16; - } - } - else if(frame == 41) - { - x -= 14; - y -= 16; - } - else if(frame < 31 || frame == 42) - { - x -= 20; - y -= 20; - } - } +Point CCursorHandler::getPivotOffsetMap(size_t index) +{ + static const std::array offsets = {{ + { 0, 0}, // POINTER = 0, + { 0, 0}, // HOURGLASS = 1, + { 12, 10}, // HERO = 2, + { 12, 12}, // TOWN = 3, + + { 15, 13}, // T1_MOVE = 4, + { 13, 13}, // T1_ATTACK = 5, + { 20, 20}, // T1_SAIL = 6, + { 13, 16}, // T1_DISEMBARK = 7, + { 8, 9}, // T1_EXCHANGE = 8, + { 14, 16}, // T1_VISIT = 9, + + { 15, 13}, // T2_MOVE = 10, + { 13, 13}, // T2_ATTACK = 11, + { 20, 20}, // T2_SAIL = 12, + { 13, 16}, // T2_DISEMBARK = 13, + { 8, 9}, // T2_EXCHANGE = 14, + { 14, 16}, // T2_VISIT = 15, + + { 15, 13}, // T3_MOVE = 16, + { 13, 13}, // T3_ATTACK = 17, + { 20, 20}, // T3_SAIL = 18, + { 13, 16}, // T3_DISEMBARK = 19, + { 8, 9}, // T3_EXCHANGE = 20, + { 14, 16}, // T3_VISIT = 21, + + { 15, 13}, // T4_MOVE = 22, + { 13, 13}, // T4_ATTACK = 23, + { 20, 20}, // T4_SAIL = 24, + { 13, 16}, // T4_DISEMBARK = 25, + { 8, 9}, // T4_EXCHANGE = 26, + { 14, 16}, // T4_VISIT = 27, + + { 20, 20}, // T1_SAIL_VISIT = 28, + { 20, 20}, // T2_SAIL_VISIT = 29, + { 20, 20}, // T3_SAIL_VISIT = 30, + { 20, 20}, // T4_SAIL_VISIT = 31, + + { 6, 1}, // SCROLL_NORTH = 32, + { 16, 2}, // SCROLL_NORTHEAST = 33, + { 21, 6}, // SCROLL_EAST = 34, + { 16, 16}, // SCROLL_SOUTHEAST = 35, + { 6, 21}, // SCROLL_SOUTH = 36, + { 1, 16}, // SCROLL_SOUTHWEST = 37, + { 1, 5}, // SCROLL_WEST = 38, + { 2, 1}, // SCROLL_NORTHWEST = 39, + + { 0, 0}, // POINTER_COPY = 40, + { 14, 16}, // TELEPORT = 41, + { 20, 20}, // SCUTTLE_BOAT = 42 + }}; + + static_assert (offsets.size() == size_t(Cursor::Map::COUNT), "Invalid number of pivot offsets for cursor" ); + assert(index < offsets.size()); + return offsets[index]; +} + +Point CCursorHandler::getPivotOffsetCombat(size_t index) +{ + static const std::array offsets = {{ + { 12, 12 }, // BLOCKED = 0, + { 10, 14 }, // MOVE = 1, + { 14, 14 }, // FLY = 2, + { 12, 12 }, // SHOOT = 3, + { 12, 12 }, // HERO = 4, + { 8, 12 }, // QUERY = 5, + { 0, 0 }, // POINTER = 6, + { 21, 0 }, // HIT_NORTHEAST = 7, + { 31, 5 }, // HIT_EAST = 8, + { 21, 21 }, // HIT_SOUTHEAST = 9, + { 0, 21 }, // HIT_SOUTHWEST = 10, + { 0, 5 }, // HIT_WEST = 11, + { 0, 0 }, // HIT_NORTHWEST = 12, + { 6, 0 }, // HIT_NORTH = 13, + { 6, 31 }, // HIT_SOUTH = 14, + { 14, 0 }, // SHOOT_PENALTY = 15, + { 12, 12 }, // SHOOT_CATAPULT = 16, + { 12, 12 }, // HEAL = 17, + { 12, 12 }, // SACRIFICE = 18, + { 14, 20 }, // TELEPORT = 19 + }}; + + static_assert (offsets.size() == size_t(Cursor::Combat::COUNT), "Invalid number of pivot offsets for cursor" ); + assert(index < offsets.size()); + return offsets[index]; +} + +Point CCursorHandler::getPivotOffsetSpellcast() +{ + return { 18, 28}; +} + +Point CCursorHandler::getPivotOffset() +{ + switch (type) { + case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame); + case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame); + case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame); + case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast(); + }; + + assert(0); + return {0, 0}; } void CCursorHandler::centerCursor() { - this->xpos = static_cast((screen->w / 2.) - (currentCursor->pos.w / 2.)); - this->ypos = static_cast((screen->h / 2.) - (currentCursor->pos.h / 2.)); + pos.x = static_cast((screen->w / 2.) - (currentCursor->pos.w / 2.)); + pos.y = static_cast((screen->h / 2.) - (currentCursor->pos.h / 2.)); SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); - SDL_WarpMouse(this->xpos, this->ypos); + SDL_WarpMouse(pos.x, pos.y); SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE); } +void CCursorHandler::updateSpellcastCursor() +{ + static const float frameDisplayDuration = 0.1f; + + frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; + size_t newFrame = frame; + + while (frameTime > frameDisplayDuration) + { + frameTime -= frameDisplayDuration; + newFrame++; + } + + auto & animation = cursors.at(static_cast(type)); + + while (newFrame > animation->size()) + newFrame -= animation->size(); + + changeGraphic(Cursor::Type::SPELLBOOK, newFrame); +} + void CCursorHandler::render() { if(!showing) return; if (type == Cursor::Type::SPELLBOOK) - { - static const float frameDisplayDuration = 0.1f; + updateSpellcastCursor(); - frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; - size_t newFrame = frame; - - while (frameTime > frameDisplayDuration) - { - frameTime -= frameDisplayDuration; - newFrame++; - } - - auto & animation = cursors.at(static_cast(type)); - - while (newFrame > animation->size()) - newFrame -= animation->size(); - - changeGraphic(Cursor::Type::SPELLBOOK, newFrame); - } //the must update texture in the main (renderer) thread, but changes to cursor type may come from other threads updateTexture(); - int x = xpos; - int y = ypos; - shiftPos(x, y); + Point renderPos = pos; if(dndObject) - { - x -= dndObject->pos.w/2; - y -= dndObject->pos.h/2; - } + renderPos -= dndObject->pos.dimensions() / 2; + else + renderPos -= getPivotOffset(); SDL_Rect destRect; - destRect.x = x; - destRect.y = y; + destRect.x = renderPos.x; + destRect.y = renderPos.y; destRect.w = 40; destRect.h = 40; diff --git a/client/gui/CCursorHandler.h b/client/gui/CCursorHandler.h index 82082a93d..3c7aaafb4 100644 --- a/client/gui/CCursorHandler.h +++ b/client/gui/CCursorHandler.h @@ -8,11 +8,13 @@ * */ #pragma once + class CIntObject; class CAnimImage; struct SDL_Surface; struct SDL_Texture; -struct Point; + +#include "Geometries.h" namespace Cursor { @@ -51,7 +53,9 @@ namespace Cursor SHOOT_CATAPULT = 16, HEAL = 17, SACRIFICE = 18, - TELEPORT = 19 + TELEPORT = 19, + + COUNT }; enum class Map { @@ -97,7 +101,9 @@ namespace Cursor SCROLL_NORTHWEST = 39, //POINTER_COPY = 40, // probably unused TELEPORT = 41, - SCUTTLE_BOAT = 42 + SCUTTLE_BOAT = 42, + + COUNT }; enum class Spellcast { @@ -123,7 +129,6 @@ class CCursorHandler final void clearBuffer(); void updateBuffer(CIntObject * payload); void replaceBuffer(CIntObject * payload); - void shiftPos( int &x, int &y ); void updateTexture(); @@ -135,8 +140,15 @@ class CCursorHandler final void changeGraphic(Cursor::Type type, size_t index); /// position of cursor - int xpos, ypos; + Point pos; + Point getPivotOffsetDefault(size_t index); + Point getPivotOffsetMap(size_t index); + Point getPivotOffsetCombat(size_t index); + Point getPivotOffsetSpellcast(); + Point getPivotOffset(); + + void updateSpellcastCursor(); public: CCursorHandler(); ~CCursorHandler(); From 0e8ee929df51fdafd3ccb31623be24023f393915 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 5 Jan 2023 19:34:37 +0200 Subject: [PATCH 36/54] Renamed CCursorHandler -> CursorHandler --- client/CGameInfo.h | 4 +- client/CMT.cpp | 4 +- client/CMakeLists.txt | 4 +- client/CPlayerInterface.cpp | 2 +- client/battle/BattleActionsController.cpp | 2 +- client/battle/BattleAnimationClasses.cpp | 2 +- client/battle/BattleFieldController.cpp | 2 +- client/battle/BattleInterface.cpp | 2 +- client/battle/BattleInterfaceClasses.cpp | 2 +- client/battle/BattleWindow.cpp | 2 +- client/gui/CGuiHandler.cpp | 2 +- .../{CCursorHandler.cpp => CursorHandler.cpp} | 46 +++++++++---------- .../gui/{CCursorHandler.h => CursorHandler.h} | 6 +-- client/mainmenu/CMainMenu.cpp | 2 +- client/widgets/CArtifactHolder.cpp | 2 +- client/widgets/CComponent.cpp | 2 +- client/widgets/MiscWidgets.cpp | 2 +- client/windows/CAdvmapInterface.cpp | 2 +- client/windows/CTradeWindow.cpp | 2 +- client/windows/CWindowObject.cpp | 2 +- client/windows/GUIClasses.cpp | 2 +- client/windows/InfoWindows.cpp | 2 +- 22 files changed, 49 insertions(+), 49 deletions(-) rename client/gui/{CCursorHandler.cpp => CursorHandler.cpp} (83%) rename client/gui/{CCursorHandler.h => CursorHandler.h} (94%) diff --git a/client/CGameInfo.h b/client/CGameInfo.h index 42ff54acf..042538337 100644 --- a/client/CGameInfo.h +++ b/client/CGameInfo.h @@ -38,7 +38,7 @@ VCMI_LIB_NAMESPACE_END class CMapHandler; class CSoundHandler; class CMusicHandler; -class CCursorHandler; +class CursorHandler; class IMainVideoPlayer; class CServerHandler; @@ -49,7 +49,7 @@ public: CSoundHandler * soundh; CMusicHandler * musich; CConsoleHandler * consoleh; - CCursorHandler * curh; + CursorHandler * curh; IMainVideoPlayer * videoh; }; extern CClientState * CCS; diff --git a/client/CMT.cpp b/client/CMT.cpp index d5727bd97..d4bc18ce9 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -25,7 +25,7 @@ #include "lobby/CSelectionBase.h" #include "windows/CCastleInterface.h" #include "../lib/CConsoleHandler.h" -#include "gui/CCursorHandler.h" +#include "gui/CursorHandler.h" #include "../lib/CGameState.h" #include "../CCallback.h" #include "CPlayerInterface.h" @@ -470,7 +470,7 @@ int main(int argc, char * argv[]) pomtime.getDiff(); graphics = new Graphics(); // should be before curh - CCS->curh = new CCursorHandler(); + CCS->curh = new CursorHandler(); logGlobal->info("Screen handler: %d ms", pomtime.getDiff()); pomtime.getDiff(); diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6bcd92424..7dd73f81c 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -18,7 +18,7 @@ set(client_SRCS gui/CAnimation.cpp gui/Canvas.cpp - gui/CCursorHandler.cpp + gui/CursorHandler.cpp gui/CGuiHandler.cpp gui/CIntObject.cpp gui/ColorFilter.cpp @@ -104,7 +104,7 @@ set(client_HEADERS gui/CAnimation.h gui/Canvas.h - gui/CCursorHandler.h + gui/CursorHandler.h gui/CGuiHandler.h gui/ColorFilter.h gui/CIntObject.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index c7f295371..789e69231 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -19,7 +19,7 @@ #include "battle/BattleWindow.h" #include "../CCallback.h" #include "windows/CCastleInterface.h" -#include "gui/CCursorHandler.h" +#include "gui/CursorHandler.h" #include "windows/CKingdomInterface.h" #include "CGameInfo.h" #include "windows/CHeroWindow.h" diff --git a/client/battle/BattleActionsController.cpp b/client/battle/BattleActionsController.cpp index e0dc61229..65881f829 100644 --- a/client/battle/BattleActionsController.cpp +++ b/client/battle/BattleActionsController.cpp @@ -19,7 +19,7 @@ #include "../CGameInfo.h" #include "../CPlayerInterface.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" #include "../gui/CIntObject.h" #include "../windows/CCreatureWindow.h" diff --git a/client/battle/BattleAnimationClasses.cpp b/client/battle/BattleAnimationClasses.cpp index d96f48414..111a4e3a6 100644 --- a/client/battle/BattleAnimationClasses.cpp +++ b/client/battle/BattleAnimationClasses.cpp @@ -22,7 +22,7 @@ #include "../CGameInfo.h" #include "../CMusicHandler.h" #include "../CPlayerInterface.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" #include "../../CCallback.h" diff --git a/client/battle/BattleFieldController.cpp b/client/battle/BattleFieldController.cpp index 3155dc30d..675b39fb0 100644 --- a/client/battle/BattleFieldController.cpp +++ b/client/battle/BattleFieldController.cpp @@ -26,7 +26,7 @@ #include "../gui/CAnimation.h" #include "../gui/Canvas.h" #include "../gui/CGuiHandler.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../../CCallback.h" #include "../../lib/BattleFieldHandler.h" diff --git a/client/battle/BattleInterface.cpp b/client/battle/BattleInterface.cpp index 8a1e17bb9..6d3a0c4c5 100644 --- a/client/battle/BattleInterface.cpp +++ b/client/battle/BattleInterface.cpp @@ -28,7 +28,7 @@ #include "../CMusicHandler.h" #include "../CPlayerInterface.h" #include "../gui/Canvas.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" #include "../windows/CAdvmapInterface.h" diff --git a/client/battle/BattleInterfaceClasses.cpp b/client/battle/BattleInterfaceClasses.cpp index 14f67822a..cad232cde 100644 --- a/client/battle/BattleInterfaceClasses.cpp +++ b/client/battle/BattleInterfaceClasses.cpp @@ -26,7 +26,7 @@ #include "../Graphics.h" #include "../gui/CAnimation.h" #include "../gui/Canvas.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" #include "../widgets/AdventureMapClasses.h" #include "../widgets/Buttons.h" diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 76b57776b..e12501cc9 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -21,7 +21,7 @@ #include "../CPlayerInterface.h" #include "../CMusicHandler.h" #include "../gui/Canvas.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" #include "../gui/CAnimation.h" #include "../windows/CSpellWindow.h" diff --git a/client/gui/CGuiHandler.cpp b/client/gui/CGuiHandler.cpp index 060ce0570..f1f3ee424 100644 --- a/client/gui/CGuiHandler.cpp +++ b/client/gui/CGuiHandler.cpp @@ -14,7 +14,7 @@ #include #include "CIntObject.h" -#include "CCursorHandler.h" +#include "CursorHandler.h" #include "../CGameInfo.h" #include "../../lib/CThreadHelper.h" diff --git a/client/gui/CCursorHandler.cpp b/client/gui/CursorHandler.cpp similarity index 83% rename from client/gui/CCursorHandler.cpp rename to client/gui/CursorHandler.cpp index 6d42b917c..e8ab01cc4 100644 --- a/client/gui/CCursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -8,7 +8,7 @@ * */ #include "StdInc.h" -#include "CCursorHandler.h" +#include "CursorHandler.h" #include @@ -18,13 +18,13 @@ #include "../CMT.h" -void CCursorHandler::clearBuffer() +void CursorHandler::clearBuffer() { Uint32 fillColor = SDL_MapRGBA(buffer->format, 0, 0, 0, 0); CSDL_Ext::fillRect(buffer, nullptr, fillColor); } -void CCursorHandler::updateBuffer(CIntObject * payload) +void CursorHandler::updateBuffer(CIntObject * payload) { payload->moveTo(Point(0,0)); payload->showAll(buffer); @@ -32,13 +32,13 @@ void CCursorHandler::updateBuffer(CIntObject * payload) needUpdate = true; } -void CCursorHandler::replaceBuffer(CIntObject * payload) +void CursorHandler::replaceBuffer(CIntObject * payload) { clearBuffer(); updateBuffer(payload); } -CCursorHandler::CCursorHandler() +CursorHandler::CursorHandler() : needUpdate(true) , buffer(nullptr) , cursorLayer(nullptr) @@ -70,12 +70,12 @@ CCursorHandler::CCursorHandler() set(Cursor::Map::POINTER); } -Point CCursorHandler::position() const +Point CursorHandler::position() const { return pos; } -void CCursorHandler::changeGraphic(Cursor::Type type, size_t index) +void CursorHandler::changeGraphic(Cursor::Type type, size_t index) { assert(dndObject == nullptr); @@ -95,28 +95,28 @@ void CCursorHandler::changeGraphic(Cursor::Type type, size_t index) replaceBuffer(currentCursor); } -void CCursorHandler::set(Cursor::Default index) +void CursorHandler::set(Cursor::Default index) { changeGraphic(Cursor::Type::DEFAULT, static_cast(index)); } -void CCursorHandler::set(Cursor::Map index) +void CursorHandler::set(Cursor::Map index) { changeGraphic(Cursor::Type::ADVENTURE, static_cast(index)); } -void CCursorHandler::set(Cursor::Combat index) +void CursorHandler::set(Cursor::Combat index) { changeGraphic(Cursor::Type::COMBAT, static_cast(index)); } -void CCursorHandler::set(Cursor::Spellcast index) +void CursorHandler::set(Cursor::Spellcast index) { //Note: this is animated cursor, ignore specified frame and only change type changeGraphic(Cursor::Type::SPELLBOOK, frame); } -void CCursorHandler::dragAndDropCursor(std::unique_ptr object) +void CursorHandler::dragAndDropCursor(std::unique_ptr object) { dndObject = std::move(object); if(dndObject) @@ -125,18 +125,18 @@ void CCursorHandler::dragAndDropCursor(std::unique_ptr object) replaceBuffer(currentCursor); } -void CCursorHandler::cursorMove(const int & x, const int & y) +void CursorHandler::cursorMove(const int & x, const int & y) { pos.x = x; pos.y = y; } -Point CCursorHandler::getPivotOffsetDefault(size_t index) +Point CursorHandler::getPivotOffsetDefault(size_t index) { return {0, 0}; } -Point CCursorHandler::getPivotOffsetMap(size_t index) +Point CursorHandler::getPivotOffsetMap(size_t index) { static const std::array offsets = {{ { 0, 0}, // POINTER = 0, @@ -196,7 +196,7 @@ Point CCursorHandler::getPivotOffsetMap(size_t index) return offsets[index]; } -Point CCursorHandler::getPivotOffsetCombat(size_t index) +Point CursorHandler::getPivotOffsetCombat(size_t index) { static const std::array offsets = {{ { 12, 12 }, // BLOCKED = 0, @@ -226,12 +226,12 @@ Point CCursorHandler::getPivotOffsetCombat(size_t index) return offsets[index]; } -Point CCursorHandler::getPivotOffsetSpellcast() +Point CursorHandler::getPivotOffsetSpellcast() { return { 18, 28}; } -Point CCursorHandler::getPivotOffset() +Point CursorHandler::getPivotOffset() { switch (type) { case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame); @@ -244,7 +244,7 @@ Point CCursorHandler::getPivotOffset() return {0, 0}; } -void CCursorHandler::centerCursor() +void CursorHandler::centerCursor() { pos.x = static_cast((screen->w / 2.) - (currentCursor->pos.w / 2.)); pos.y = static_cast((screen->h / 2.) - (currentCursor->pos.h / 2.)); @@ -253,7 +253,7 @@ void CCursorHandler::centerCursor() SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE); } -void CCursorHandler::updateSpellcastCursor() +void CursorHandler::updateSpellcastCursor() { static const float frameDisplayDuration = 0.1f; @@ -274,7 +274,7 @@ void CCursorHandler::updateSpellcastCursor() changeGraphic(Cursor::Type::SPELLBOOK, newFrame); } -void CCursorHandler::render() +void CursorHandler::render() { if(!showing) return; @@ -302,7 +302,7 @@ void CCursorHandler::render() SDL_RenderCopy(mainRenderer, cursorLayer, nullptr, &destRect); } -void CCursorHandler::updateTexture() +void CursorHandler::updateTexture() { if(needUpdate) { @@ -311,7 +311,7 @@ void CCursorHandler::updateTexture() } } -CCursorHandler::~CCursorHandler() +CursorHandler::~CursorHandler() { if(buffer) SDL_FreeSurface(buffer); diff --git a/client/gui/CCursorHandler.h b/client/gui/CursorHandler.h similarity index 94% rename from client/gui/CCursorHandler.h rename to client/gui/CursorHandler.h index 3c7aaafb4..6a613336a 100644 --- a/client/gui/CCursorHandler.h +++ b/client/gui/CursorHandler.h @@ -112,7 +112,7 @@ namespace Cursor } /// handles mouse cursor -class CCursorHandler final +class CursorHandler final { bool needUpdate; SDL_Texture * cursorLayer; @@ -150,8 +150,8 @@ class CCursorHandler final void updateSpellcastCursor(); public: - CCursorHandler(); - ~CCursorHandler(); + CursorHandler(); + ~CursorHandler(); /** * Replaces the cursor with a custom image. diff --git a/client/mainmenu/CMainMenu.cpp b/client/mainmenu/CMainMenu.cpp index 3b265d0cd..27f053aa2 100644 --- a/client/mainmenu/CMainMenu.cpp +++ b/client/mainmenu/CMainMenu.cpp @@ -21,7 +21,7 @@ #include "../../lib/filesystem/CCompressedStream.h" #include "../gui/SDL_Extensions.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../CGameInfo.h" #include "../../lib/CGeneralTextHandler.h" diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 29ecc698b..1a82090a5 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -11,7 +11,7 @@ #include "CArtifactHolder.h" #include "../gui/CGuiHandler.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "Buttons.h" #include "CComponent.h" diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 14e166963..dd89c96f2 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -17,7 +17,7 @@ #include #include "../gui/CGuiHandler.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../CMessage.h" #include "../CGameInfo.h" diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index e11f9ab95..532513038 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -13,7 +13,7 @@ #include "CComponent.h" #include "../gui/CGuiHandler.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../CPlayerInterface.h" #include "../CMessage.h" diff --git a/client/windows/CAdvmapInterface.cpp b/client/windows/CAdvmapInterface.cpp index 1cd0f81c0..704d8391d 100644 --- a/client/windows/CAdvmapInterface.cpp +++ b/client/windows/CAdvmapInterface.cpp @@ -32,7 +32,7 @@ #include "../mapHandler.h" #include "../gui/CAnimation.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../gui/CGuiHandler.h" #include "../gui/SDL_Extensions.h" #include "../widgets/MiscWidgets.h" diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index ade100707..24a94adfc 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -13,7 +13,7 @@ #include "CAdvmapInterface.h" #include "../gui/CGuiHandler.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../widgets/Images.h" #include "../CGameInfo.h" diff --git a/client/windows/CWindowObject.cpp b/client/windows/CWindowObject.cpp index b55271543..6527f555a 100644 --- a/client/windows/CWindowObject.cpp +++ b/client/windows/CWindowObject.cpp @@ -18,7 +18,7 @@ #include "../gui/SDL_Pixels.h" #include "../gui/SDL_Extensions.h" #include "../gui/CGuiHandler.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../battle/BattleInterface.h" #include "../battle/BattleInterfaceClasses.h" diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 129c3235c..d139776d9 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -32,7 +32,7 @@ #include "../gui/CAnimation.h" #include "../gui/CGuiHandler.h" #include "../gui/SDL_Extensions.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../widgets/CComponent.h" #include "../widgets/MiscWidgets.h" diff --git a/client/windows/InfoWindows.cpp b/client/windows/InfoWindows.cpp index 5fa9ffb45..a793d66c2 100644 --- a/client/windows/InfoWindows.cpp +++ b/client/windows/InfoWindows.cpp @@ -24,7 +24,7 @@ #include "../gui/SDL_Pixels.h" #include "../gui/SDL_Extensions.h" #include "../gui/CGuiHandler.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../battle/BattleInterface.h" #include "../battle/BattleInterfaceClasses.h" From 9971bdca1b7ec5f5edd2b3678670435a76d21bec Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 5 Jan 2023 21:30:54 +0200 Subject: [PATCH 37/54] Existing software cursor logic is now in a separate class --- client/gui/CursorHandler.cpp | 185 +++++++++++++++++------------ client/gui/CursorHandler.h | 69 +++++++---- client/widgets/CArtifactHolder.cpp | 8 +- client/widgets/Images.cpp | 2 +- client/windows/CTradeWindow.cpp | 2 +- 5 files changed, 155 insertions(+), 111 deletions(-) diff --git a/client/gui/CursorHandler.cpp b/client/gui/CursorHandler.cpp index e8ab01cc4..94f07002a 100644 --- a/client/gui/CursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -12,60 +12,33 @@ #include + #include "SDL_Extensions.h" #include "CGuiHandler.h" -#include "../widgets/Images.h" +#include "CAnimation.h" #include "../CMT.h" -void CursorHandler::clearBuffer() -{ - Uint32 fillColor = SDL_MapRGBA(buffer->format, 0, 0, 0, 0); - CSDL_Ext::fillRect(buffer, nullptr, fillColor); -} - -void CursorHandler::updateBuffer(CIntObject * payload) -{ - payload->moveTo(Point(0,0)); - payload->showAll(buffer); - - needUpdate = true; -} - -void CursorHandler::replaceBuffer(CIntObject * payload) -{ - clearBuffer(); - updateBuffer(payload); -} - CursorHandler::CursorHandler() - : needUpdate(true) - , buffer(nullptr) - , cursorLayer(nullptr) + : cursorSW(new CursorSoftware()) , frameTime(0.f) , showing(false) , pos(0,0) { - cursorLayer = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 40, 40); - SDL_SetTextureBlendMode(cursorLayer, SDL_BLENDMODE_BLEND); type = Cursor::Type::DEFAULT; dndObject = nullptr; cursors = { - std::make_unique("CRADVNTR", 0), - std::make_unique("CRCOMBAT", 0), - std::make_unique("CRDEFLT", 0), - std::make_unique("CRSPELL", 0) + std::make_unique("CRADVNTR"), + std::make_unique("CRCOMBAT"), + std::make_unique("CRDEFLT"), + std::make_unique("CRSPELL") }; - currentCursor = cursors.at(static_cast(Cursor::Type::DEFAULT)).get(); - - buffer = CSDL_Ext::newSurface(40,40); - - SDL_SetSurfaceBlendMode(buffer, SDL_BLENDMODE_NONE); - SDL_ShowCursor(SDL_DISABLE); + for (auto & cursor : cursors) + cursor->preload(); set(Cursor::Map::POINTER); } @@ -79,20 +52,10 @@ void CursorHandler::changeGraphic(Cursor::Type type, size_t index) { assert(dndObject == nullptr); - if(type != this->type) - { - this->type = type; - this->frame = index; - currentCursor = cursors.at(static_cast(type)).get(); - currentCursor->setFrame(index); - } - else if(index != this->frame) - { - this->frame = index; - currentCursor->setFrame(index); - } + this->type = type; + this->frame = index; - replaceBuffer(currentCursor); + cursorSW->setImage(getCurrentImage(), getPivotOffset()); } void CursorHandler::set(Cursor::Default index) @@ -116,19 +79,25 @@ void CursorHandler::set(Cursor::Spellcast index) changeGraphic(Cursor::Type::SPELLBOOK, frame); } -void CursorHandler::dragAndDropCursor(std::unique_ptr object) +void CursorHandler::dragAndDropCursor(std::shared_ptr image) { - dndObject = std::move(object); - if(dndObject) - replaceBuffer(dndObject.get()); - else - replaceBuffer(currentCursor); + dndObject = image; + cursorSW->setImage(getCurrentImage(), getPivotOffset()); +} + +void CursorHandler::dragAndDropCursor (std::string path, size_t index) +{ + CAnimation anim(path); + anim.load(index); + dragAndDropCursor(anim.getImage(index)); } void CursorHandler::cursorMove(const int & x, const int & y) { pos.x = x; pos.y = y; + + cursorSW->setCursorPosition(pos); } Point CursorHandler::getPivotOffsetDefault(size_t index) @@ -233,6 +202,9 @@ Point CursorHandler::getPivotOffsetSpellcast() Point CursorHandler::getPivotOffset() { + if (dndObject) + return dndObject->dimensions(); + switch (type) { case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame); case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame); @@ -244,13 +216,24 @@ Point CursorHandler::getPivotOffset() return {0, 0}; } +std::shared_ptr CursorHandler::getCurrentImage() +{ + if (dndObject) + return dndObject; + + return cursors[static_cast(type)]->getImage(frame); +} + void CursorHandler::centerCursor() { - pos.x = static_cast((screen->w / 2.) - (currentCursor->pos.w / 2.)); - pos.y = static_cast((screen->h / 2.) - (currentCursor->pos.h / 2.)); + Point screenSize {screen->w, screen->h}; + pos = screenSize / 2 - getPivotOffset(); + SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); SDL_WarpMouse(pos.x, pos.y); SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE); + + cursorSW->setCursorPosition(pos); } void CursorHandler::updateSpellcastCursor() @@ -260,7 +243,7 @@ void CursorHandler::updateSpellcastCursor() frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f; size_t newFrame = frame; - while (frameTime > frameDisplayDuration) + while (frameTime >= frameDisplayDuration) { frameTime -= frameDisplayDuration; newFrame++; @@ -268,7 +251,7 @@ void CursorHandler::updateSpellcastCursor() auto & animation = cursors.at(static_cast(type)); - while (newFrame > animation->size()) + while (newFrame >= animation->size()) newFrame -= animation->size(); changeGraphic(Cursor::Type::SPELLBOOK, newFrame); @@ -282,16 +265,16 @@ void CursorHandler::render() if (type == Cursor::Type::SPELLBOOK) updateSpellcastCursor(); + cursorSW->render(); +} - //the must update texture in the main (renderer) thread, but changes to cursor type may come from other threads - updateTexture(); +void CursorSoftware::render() +{ + //texture must be updated in the main (renderer) thread, but changes to cursor type may come from other threads + if (needUpdate) + updateTexture(); - Point renderPos = pos; - - if(dndObject) - renderPos -= dndObject->pos.dimensions() / 2; - else - renderPos -= getPivotOffset(); + Point renderPos = pos - pivot; SDL_Rect destRect; destRect.x = renderPos.x; @@ -299,23 +282,67 @@ void CursorHandler::render() destRect.w = 40; destRect.h = 40; - SDL_RenderCopy(mainRenderer, cursorLayer, nullptr, &destRect); + SDL_RenderCopy(mainRenderer, cursorTexture, nullptr, &destRect); } -void CursorHandler::updateTexture() +void CursorSoftware::createTexture(const Point & dimensions) { - if(needUpdate) - { - SDL_UpdateTexture(cursorLayer, nullptr, buffer->pixels, buffer->pitch); - needUpdate = false; - } + if(cursorTexture) + SDL_DestroyTexture(cursorTexture); + + if (cursorSurface) + SDL_FreeSurface(cursorSurface); + + cursorSurface = CSDL_Ext::newSurface(dimensions.x, dimensions.y); + cursorTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, dimensions.x, dimensions.y); + + SDL_SetSurfaceBlendMode(cursorSurface, SDL_BLENDMODE_NONE); + SDL_SetTextureBlendMode(cursorTexture, SDL_BLENDMODE_BLEND); } -CursorHandler::~CursorHandler() +void CursorSoftware::updateTexture() { - if(buffer) - SDL_FreeSurface(buffer); + Point dimensions(-1, -1); + + if (!cursorSurface || Point(cursorSurface->w, cursorSurface->h) != cursorImage->dimensions()) + createTexture(cursorImage->dimensions()); + + Uint32 fillColor = SDL_MapRGBA(cursorSurface->format, 0, 0, 0, 0); + CSDL_Ext::fillRect(cursorSurface, nullptr, fillColor); + + cursorImage->draw(cursorSurface); + SDL_UpdateTexture(cursorTexture, NULL, cursorSurface->pixels, cursorSurface->pitch); + needUpdate = false; +} + +void CursorSoftware::setImage(std::shared_ptr image, const Point & pivotOffset) +{ + assert(image != nullptr); + cursorImage = image; + pivot = pivotOffset; + needUpdate = true; +} + +void CursorSoftware::setCursorPosition( const Point & newPos ) +{ + pos = newPos; +} + +CursorSoftware::CursorSoftware(): + cursorTexture(nullptr), + cursorSurface(nullptr), + needUpdate(false), + pivot(0,0) +{ + SDL_ShowCursor(SDL_DISABLE); +} + +CursorSoftware::~CursorSoftware() +{ + if(cursorTexture) + SDL_DestroyTexture(cursorTexture); + + if (cursorSurface) + SDL_FreeSurface(cursorSurface); - if(cursorLayer) - SDL_DestroyTexture(cursorLayer); } diff --git a/client/gui/CursorHandler.h b/client/gui/CursorHandler.h index 6a613336a..08e526a5a 100644 --- a/client/gui/CursorHandler.h +++ b/client/gui/CursorHandler.h @@ -9,8 +9,8 @@ */ #pragma once -class CIntObject; -class CAnimImage; +class CAnimation; +class IImage; struct SDL_Surface; struct SDL_Texture; @@ -111,37 +111,52 @@ namespace Cursor }; } +class CursorHardware +{ + //TODO +}; + +class CursorSoftware +{ + std::shared_ptr cursorImage; + + SDL_Texture * cursorTexture; + SDL_Surface * cursorSurface; + + Point pos; + Point pivot; + bool needUpdate; + + void createTexture(const Point & dimensions); + void updateTexture(); +public: + CursorSoftware(); + ~CursorSoftware(); + + void setImage(std::shared_ptr image, const Point & pivotOffset); + void setCursorPosition( const Point & newPos ); + + void render(); + void setVisible(bool on); +}; + /// handles mouse cursor class CursorHandler final { - bool needUpdate; - SDL_Texture * cursorLayer; + std::shared_ptr dndObject; //if set, overrides currentCursor - SDL_Surface * buffer; - CAnimImage * currentCursor; - - std::unique_ptr dndObject; //if set, overrides currentCursor - - std::array, 4> cursors; + std::array, 4> cursors; bool showing; - void clearBuffer(); - void updateBuffer(CIntObject * payload); - void replaceBuffer(CIntObject * payload); - - void updateTexture(); - /// Current cursor Cursor::Type type; size_t frame; float frameTime; + Point pos; void changeGraphic(Cursor::Type type, size_t index); - /// position of cursor - Point pos; - Point getPivotOffsetDefault(size_t index); Point getPivotOffsetMap(size_t index); Point getPivotOffsetCombat(size_t index); @@ -149,17 +164,19 @@ class CursorHandler final Point getPivotOffset(); void updateSpellcastCursor(); + + std::shared_ptr getCurrentImage(); + + std::unique_ptr cursorSW; public: CursorHandler(); ~CursorHandler(); - /** - * Replaces the cursor with a custom image. - * - * @param image Image to replace cursor with or nullptr to use the normal - * cursor. CursorHandler takes ownership of object - */ - void dragAndDropCursor (std::unique_ptr image); + /// Replaces the cursor with a custom image. + /// @param image Image to replace cursor with or nullptr to use the normal cursor. + void dragAndDropCursor(std::shared_ptr image); + + void dragAndDropCursor(std::string path, size_t index); /// Returns current position of the cursor Point position() const; diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index 1a82090a5..177d22eab 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -257,7 +257,7 @@ void CHeroArtPlace::clickRight(tribool down, bool previousState) void CArtifactsOfHero::activate() { if (commonInfo->src.AOH == this && commonInfo->src.art) - CCS->curh->dragAndDropCursor(std::make_unique("artifact", commonInfo->src.art->artType->getIconIndex())); + CCS->curh->dragAndDropCursor("artifact", commonInfo->src.art->artType->getIconIndex()); CIntObject::activate(); } @@ -289,7 +289,7 @@ void CHeroArtPlace::select () } } - CCS->curh->dragAndDropCursor(std::make_unique("artifact", ourArt->artType->getIconIndex())); + CCS->curh->dragAndDropCursor("artifact", ourArt->artType->getIconIndex()); ourOwner->commonInfo->src.setTo(this, false); ourOwner->markPossibleSlots(ourArt); @@ -752,7 +752,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation & src, const Artifac { auto art = curHero->getArt(ArtifactPosition::TRANSITION_POS); assert(art); - CCS->curh->dragAndDropCursor(std::make_unique("artifact", art->artType->getIconIndex())); + CCS->curh->dragAndDropCursor("artifact", art->artType->getIconIndex()); markPossibleSlots(art); commonInfo->src.art = art; @@ -787,7 +787,7 @@ 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(std::make_unique("artifact", dst.getArt()->artType->getIconIndex())); + CCS->curh->dragAndDropCursor("artifact", dst.getArt()->artType->getIconIndex()); } updateParentWindow(); diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 3ede9f5cb..285602f37 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -15,7 +15,7 @@ #include "../gui/CAnimation.h" #include "../gui/SDL_Pixels.h" #include "../gui/CGuiHandler.h" -#include "../gui/CCursorHandler.h" +#include "../gui/CursorHandler.h" #include "../gui/ColorFilter.h" #include "../battle/BattleInterface.h" diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 24a94adfc..0edb22e96 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -188,7 +188,7 @@ void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState) aw->arts->markPossibleSlots(art); //aw->arts->commonInfo->dst.AOH = aw->arts; - CCS->curh->dragAndDropCursor(std::make_unique("artifact", art->artType->iconIndex)); + CCS->curh->dragAndDropCursor("artifact", art->artType->iconIndex); aw->arts->artifactsOnAltar.erase(art); setID(-1); From 26a1c5801bce7195c918398fe2cf13285d3a11d0 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 5 Jan 2023 22:13:24 +0200 Subject: [PATCH 38/54] Implemented hardware cursor support --- client/gui/CursorHandler.cpp | 41 +++++++++++++++++++++++++++++++++++- client/gui/CursorHandler.h | 17 ++++++++++++--- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/client/gui/CursorHandler.cpp b/client/gui/CursorHandler.cpp index 94f07002a..6f52ed652 100644 --- a/client/gui/CursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -20,7 +20,7 @@ #include "../CMT.h" CursorHandler::CursorHandler() - : cursorSW(new CursorSoftware()) + : cursorSW(new CursorHardware()) , frameTime(0.f) , showing(false) , pos(0,0) @@ -52,6 +52,9 @@ void CursorHandler::changeGraphic(Cursor::Type type, size_t index) { assert(dndObject == nullptr); + if (type == this->type && index == this->frame) + return; + this->type = type; this->frame = index; @@ -346,3 +349,39 @@ CursorSoftware::~CursorSoftware() SDL_FreeSurface(cursorSurface); } + +CursorHardware::CursorHardware(): + cursor(nullptr) +{ +} + +CursorHardware::~CursorHardware() +{ + if(cursor) + SDL_FreeCursor(cursor); +} + +void CursorHardware::setImage(std::shared_ptr image, const Point & pivotOffset) +{ + auto cursorSurface = CSDL_Ext::newSurface(image->dimensions().x, image->dimensions().y); + + image->draw(cursorSurface); + + cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y); + + if (!cursor) + logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError()); + + SDL_FreeSurface(cursorSurface); + SDL_SetCursor(cursor); +} + +void CursorHardware::setCursorPosition( const Point & newPos ) +{ + //no-op +} + +void CursorHardware::render() +{ + //no-op +} diff --git a/client/gui/CursorHandler.h b/client/gui/CursorHandler.h index 08e526a5a..cb47d606a 100644 --- a/client/gui/CursorHandler.h +++ b/client/gui/CursorHandler.h @@ -13,6 +13,7 @@ class CAnimation; class IImage; struct SDL_Surface; struct SDL_Texture; +struct SDL_Cursor; #include "Geometries.h" @@ -113,7 +114,18 @@ namespace Cursor class CursorHardware { - //TODO + std::shared_ptr cursorImage; + + SDL_Cursor * cursor; + +public: + CursorHardware(); + ~CursorHardware(); + + void setImage(std::shared_ptr image, const Point & pivotOffset); + void setCursorPosition( const Point & newPos ); + + void render(); }; class CursorSoftware @@ -137,7 +149,6 @@ public: void setCursorPosition( const Point & newPos ); void render(); - void setVisible(bool on); }; /// handles mouse cursor @@ -167,7 +178,7 @@ class CursorHandler final std::shared_ptr getCurrentImage(); - std::unique_ptr cursorSW; + std::unique_ptr cursorSW; public: CursorHandler(); ~CursorHandler(); From 6985f2e050c63c5ad4d0ebe3e5d7bfe3ff566f67 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 5 Jan 2023 22:38:52 +0200 Subject: [PATCH 39/54] Cursor type can now be set in settings.json --- client/gui/CursorHandler.cpp | 30 +++++++++++++++++++++--------- client/gui/CursorHandler.h | 32 +++++++++++++++++++++----------- config/schemas/settings.json | 6 +++++- 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/client/gui/CursorHandler.cpp b/client/gui/CursorHandler.cpp index 6f52ed652..8a1c4516b 100644 --- a/client/gui/CursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -12,15 +12,23 @@ #include - #include "SDL_Extensions.h" #include "CGuiHandler.h" #include "CAnimation.h" +#include "../../lib/CConfigHandler.h" -#include "../CMT.h" +//#include "../CMT.h" + +std::unique_ptr CursorHandler::createCursor() +{ + if (settings["video"]["softwareCursor"].Bool()) + return std::make_unique(); + else + return std::make_unique(); +} CursorHandler::CursorHandler() - : cursorSW(new CursorHardware()) + : cursor(createCursor()) , frameTime(0.f) , showing(false) , pos(0,0) @@ -58,7 +66,7 @@ void CursorHandler::changeGraphic(Cursor::Type type, size_t index) this->type = type; this->frame = index; - cursorSW->setImage(getCurrentImage(), getPivotOffset()); + cursor->setImage(getCurrentImage(), getPivotOffset()); } void CursorHandler::set(Cursor::Default index) @@ -85,7 +93,7 @@ void CursorHandler::set(Cursor::Spellcast index) void CursorHandler::dragAndDropCursor(std::shared_ptr image) { dndObject = image; - cursorSW->setImage(getCurrentImage(), getPivotOffset()); + cursor->setImage(getCurrentImage(), getPivotOffset()); } void CursorHandler::dragAndDropCursor (std::string path, size_t index) @@ -100,7 +108,7 @@ void CursorHandler::cursorMove(const int & x, const int & y) pos.x = x; pos.y = y; - cursorSW->setCursorPosition(pos); + cursor->setCursorPosition(pos); } Point CursorHandler::getPivotOffsetDefault(size_t index) @@ -206,7 +214,7 @@ Point CursorHandler::getPivotOffsetSpellcast() Point CursorHandler::getPivotOffset() { if (dndObject) - return dndObject->dimensions(); + return dndObject->dimensions() / 2; switch (type) { case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame); @@ -236,7 +244,7 @@ void CursorHandler::centerCursor() SDL_WarpMouse(pos.x, pos.y); SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE); - cursorSW->setCursorPosition(pos); + cursor->setCursorPosition(pos); } void CursorHandler::updateSpellcastCursor() @@ -268,7 +276,7 @@ void CursorHandler::render() if (type == Cursor::Type::SPELLBOOK) updateSpellcastCursor(); - cursorSW->render(); + cursor->render(); } void CursorSoftware::render() @@ -367,6 +375,7 @@ void CursorHardware::setImage(std::shared_ptr image, const Point & pivot image->draw(cursorSurface); + auto oldCursor = cursor; cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y); if (!cursor) @@ -374,6 +383,9 @@ void CursorHardware::setImage(std::shared_ptr image, const Point & pivot SDL_FreeSurface(cursorSurface); SDL_SetCursor(cursor); + + if (oldCursor) + SDL_FreeCursor(oldCursor); } void CursorHardware::setCursorPosition( const Point & newPos ) diff --git a/client/gui/CursorHandler.h b/client/gui/CursorHandler.h index cb47d606a..ad4d4ee2f 100644 --- a/client/gui/CursorHandler.h +++ b/client/gui/CursorHandler.h @@ -112,7 +112,17 @@ namespace Cursor }; } -class CursorHardware +class ICursor +{ +public: + virtual ~ICursor() = default; + + virtual void setImage(std::shared_ptr image, const Point & pivotOffset) = 0; + virtual void setCursorPosition( const Point & newPos ) = 0; + virtual void render() = 0; +}; + +class CursorHardware : public ICursor { std::shared_ptr cursorImage; @@ -122,13 +132,12 @@ public: CursorHardware(); ~CursorHardware(); - void setImage(std::shared_ptr image, const Point & pivotOffset); - void setCursorPosition( const Point & newPos ); - - void render(); + void setImage(std::shared_ptr image, const Point & pivotOffset) override; + void setCursorPosition( const Point & newPos ) override; + void render() override; }; -class CursorSoftware +class CursorSoftware : public ICursor { std::shared_ptr cursorImage; @@ -145,10 +154,9 @@ public: CursorSoftware(); ~CursorSoftware(); - void setImage(std::shared_ptr image, const Point & pivotOffset); - void setCursorPosition( const Point & newPos ); - - void render(); + void setImage(std::shared_ptr image, const Point & pivotOffset) override; + void setCursorPosition( const Point & newPos ) override; + void render() override; }; /// handles mouse cursor @@ -178,7 +186,9 @@ class CursorHandler final std::shared_ptr getCurrentImage(); - std::unique_ptr cursorSW; + std::unique_ptr cursor; + + static std::unique_ptr createCursor(); public: CursorHandler(); ~CursorHandler(); diff --git a/config/schemas/settings.json b/config/schemas/settings.json index dd4a063f3..8ff443d5d 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -81,7 +81,7 @@ "type" : "object", "additionalProperties" : false, "default": {}, - "required" : [ "screenRes", "bitsPerPixel", "fullscreen", "realFullscreen", "spellbookAnimation","driver", "showIntro", "displayIndex" ], + "required" : [ "screenRes", "bitsPerPixel", "fullscreen", "realFullscreen", "softwareCursor", "spellbookAnimation", "driver", "showIntro", "displayIndex" ], "properties" : { "screenRes" : { "type" : "object", @@ -105,6 +105,10 @@ "type" : "boolean", "default" : false }, + "softwareCursor" : { + "type" : "boolean", + "default" : false + }, "showIntro" : { "type" : "boolean", "default" : true From c6d39da53e5e4d80d8fcab956a68d38befa74a22 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 5 Jan 2023 23:14:52 +0200 Subject: [PATCH 40/54] MXE compile fix --- client/gui/CursorHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/gui/CursorHandler.cpp b/client/gui/CursorHandler.cpp index 8a1c4516b..958f6bfd2 100644 --- a/client/gui/CursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -171,7 +171,7 @@ Point CursorHandler::getPivotOffsetMap(size_t index) { 20, 20}, // SCUTTLE_BOAT = 42 }}; - static_assert (offsets.size() == size_t(Cursor::Map::COUNT), "Invalid number of pivot offsets for cursor" ); + assert(offsets.size() == size_t(Cursor::Map::COUNT)); //Invalid number of pivot offsets for cursor assert(index < offsets.size()); return offsets[index]; } @@ -201,7 +201,7 @@ Point CursorHandler::getPivotOffsetCombat(size_t index) { 14, 20 }, // TELEPORT = 19 }}; - static_assert (offsets.size() == size_t(Cursor::Combat::COUNT), "Invalid number of pivot offsets for cursor" ); + assert(offsets.size() == size_t(Cursor::Combat::COUNT)); //Invalid number of pivot offsets for cursor assert(index < offsets.size()); return offsets[index]; } From 385dda1c5be406e588d4b0e2ef8d33b2e817f4a0 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 5 Jan 2023 23:24:49 +0200 Subject: [PATCH 41/54] Clean surface before using it --- client/gui/CursorHandler.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/gui/CursorHandler.cpp b/client/gui/CursorHandler.cpp index 958f6bfd2..688d2f49b 100644 --- a/client/gui/CursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -373,6 +373,9 @@ void CursorHardware::setImage(std::shared_ptr image, const Point & pivot { auto cursorSurface = CSDL_Ext::newSurface(image->dimensions().x, image->dimensions().y); + Uint32 fillColor = SDL_MapRGBA(cursorSurface->format, 0, 0, 0, 0); + CSDL_Ext::fillRect(cursorSurface, nullptr, fillColor); + image->draw(cursorSurface); auto oldCursor = cursor; From fc4121f5ec6d64898ee98f52e1bd687c4052e186 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 11 Jan 2023 13:41:44 +0200 Subject: [PATCH 42/54] Rechecked & updated offsets of adventure map cursors --- client/gui/CursorHandler.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/client/gui/CursorHandler.cpp b/client/gui/CursorHandler.cpp index 688d2f49b..b093637a4 100644 --- a/client/gui/CursorHandler.cpp +++ b/client/gui/CursorHandler.cpp @@ -126,36 +126,36 @@ Point CursorHandler::getPivotOffsetMap(size_t index) { 15, 13}, // T1_MOVE = 4, { 13, 13}, // T1_ATTACK = 5, - { 20, 20}, // T1_SAIL = 6, - { 13, 16}, // T1_DISEMBARK = 7, + { 16, 32}, // T1_SAIL = 6, + { 13, 20}, // T1_DISEMBARK = 7, { 8, 9}, // T1_EXCHANGE = 8, { 14, 16}, // T1_VISIT = 9, { 15, 13}, // T2_MOVE = 10, { 13, 13}, // T2_ATTACK = 11, - { 20, 20}, // T2_SAIL = 12, - { 13, 16}, // T2_DISEMBARK = 13, + { 16, 32}, // T2_SAIL = 12, + { 13, 20}, // T2_DISEMBARK = 13, { 8, 9}, // T2_EXCHANGE = 14, { 14, 16}, // T2_VISIT = 15, { 15, 13}, // T3_MOVE = 16, { 13, 13}, // T3_ATTACK = 17, - { 20, 20}, // T3_SAIL = 18, - { 13, 16}, // T3_DISEMBARK = 19, + { 16, 32}, // T3_SAIL = 18, + { 13, 20}, // T3_DISEMBARK = 19, { 8, 9}, // T3_EXCHANGE = 20, { 14, 16}, // T3_VISIT = 21, { 15, 13}, // T4_MOVE = 22, { 13, 13}, // T4_ATTACK = 23, - { 20, 20}, // T4_SAIL = 24, - { 13, 16}, // T4_DISEMBARK = 25, + { 16, 32}, // T4_SAIL = 24, + { 13, 20}, // T4_DISEMBARK = 25, { 8, 9}, // T4_EXCHANGE = 26, { 14, 16}, // T4_VISIT = 27, - { 20, 20}, // T1_SAIL_VISIT = 28, - { 20, 20}, // T2_SAIL_VISIT = 29, - { 20, 20}, // T3_SAIL_VISIT = 30, - { 20, 20}, // T4_SAIL_VISIT = 31, + { 16, 32}, // T1_SAIL_VISIT = 28, + { 16, 32}, // T2_SAIL_VISIT = 29, + { 16, 32}, // T3_SAIL_VISIT = 30, + { 16, 32}, // T4_SAIL_VISIT = 31, { 6, 1}, // SCROLL_NORTH = 32, { 16, 2}, // SCROLL_NORTHEAST = 33, From 0a858d9a7c0f740501eaa88cb45ea1a66e2ae998 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 15 Jan 2023 23:55:24 +0200 Subject: [PATCH 43/54] Upgrade msvc to include enet and qt translator (#1436) - Updated MSVC prebuilt package - Fixed build warnings --- CI/msvc/before_install.sh | 14 +++++++------- CMakeLists.txt | 2 +- client/CMakeLists.txt | 2 +- client/battle/BattleStacksController.cpp | 8 ++++---- lib/CGeneralTextHandler.cpp | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CI/msvc/before_install.sh b/CI/msvc/before_install.sh index c80aaf4d7..851cbd4a9 100644 --- a/CI/msvc/before_install.sh +++ b/CI/msvc/before_install.sh @@ -1,10 +1,10 @@ -curl -LfsS -o "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z" \ - "https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.5/vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z" -7z x "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z" +curl -LfsS -o "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z" \ + "https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.6/vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z" +7z x "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z" -rm -r -f vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug -mkdir -p vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin -cp vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/bin/* vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin +#rm -r -f vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug +#mkdir -p vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin +#cp vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/bin/* vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin DUMPBIN_DIR=$(vswhere -latest -find **/dumpbin.exe | head -n 1) -dirname "$DUMPBIN_DIR" > $GITHUB_PATH \ No newline at end of file +dirname "$DUMPBIN_DIR" > $GITHUB_PATH diff --git a/CMakeLists.txt b/CMakeLists.txt index fd51722d9..daf8454b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,7 +232,7 @@ if(MINGW OR MSVC) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4800") # 4800: implicit conversion from 'xxx' to bool. Possible information loss if(ENABLE_STRICT_COMPILATION) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wx") # Treats all compiler warnings as errors + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX") # Treats all compiler warnings as errors endif() if(ENABLE_MULTI_PROCESS_BUILDS) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 7dd73f81c..4679e75b7 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -232,7 +232,7 @@ if(WIN32) add_custom_command(TARGET vcmiclient POST_BUILD WORKING_DIRECTORY "$" COMMAND ${CMAKE_COMMAND} -E copy AI/fuzzylite.dll fuzzylite.dll - COMMAND ${CMAKE_COMMAND} -E copy AI/tbb.dll tbb.dll + COMMAND ${CMAKE_COMMAND} -E copy AI/tbb12.dll tbb12.dll ) endif() elseif(APPLE_IOS) diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index 811726c5f..37c73e7a6 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -83,10 +83,10 @@ BattleStacksController::BattleStacksController(BattleInterface & owner): amountNegative = IImage::createFromFile("CMNUMWIN.BMP"); amountEffNeutral = IImage::createFromFile("CMNUMWIN.BMP"); - static const auto shifterNormal = ColorFilter::genRangeShifter( 0,0,0, 0.6, 0.2, 1.0 ); - static const auto shifterPositive = ColorFilter::genRangeShifter( 0,0,0, 0.2, 1.0, 0.2 ); - static const auto shifterNegative = ColorFilter::genRangeShifter( 0,0,0, 1.0, 0.2, 0.2 ); - static const auto shifterNeutral = ColorFilter::genRangeShifter( 0,0,0, 1.0, 1.0, 0.2 ); + static const auto shifterNormal = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.6f, 0.2f, 1.0f ); + static const auto shifterPositive = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.2f, 1.0f, 0.2f ); + static const auto shifterNegative = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 0.2f, 0.2f ); + static const auto shifterNeutral = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 1.0f, 0.2f ); amountNormal->adjustPalette(shifterNormal); amountPositive->adjustPalette(shifterPositive); diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index e365e5cf0..908ef3281 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -156,7 +156,7 @@ static void detectEncoding() auto data = resource->readAll(); // compute how often each character occurs in input file - for (size_t i = 0; i < data.second; ++i) + for (si64 i = 0; i < data.second; ++i) charCount[data.first[i]] += 1; // and convert computed data into weights From 54e499f5e750df8973b951f13a278946c26fcd36 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 16 Jan 2023 00:33:53 +0100 Subject: [PATCH 44/54] Minor refactoring from code review --- client/windows/CCastleInterface.cpp | 21 ++++++++------------- client/windows/CCastleInterface.h | 8 ++++++++ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 0d5ba23ef..8c8852be9 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -154,14 +154,9 @@ SDL_Color multiplyColors(const SDL_Color & b, const SDL_Color & a, double f) void CBuildingRect::show(SDL_Surface * to) { - const ui32 stageDelay = 500; + uint32_t stageDelay = BUILDING_APPEAR_TIMEPOINT; - const ui32 S1_TRANSP = 500; //500 msec building appear 0->100 transparency - const ui32 S2_WHITE_B = 1000; //500 msec border glows from white to yellow - const ui32 S3_YELLOW_B= 1500; //500 msec border glows from yellow to normal - const ui32 BUILDED = 2500; //1000 msec delay, nothing happens - - if(stateTimeCounter < S1_TRANSP) + if(stateTimeCounter < BUILDING_APPEAR_TIMEPOINT) { setAlpha(255 * stateTimeCounter / stageDelay); CShowableAnim::show(to); @@ -172,9 +167,9 @@ void CBuildingRect::show(SDL_Surface * to) CShowableAnim::show(to); } - if(border && stateTimeCounter > S1_TRANSP) + if(border && stateTimeCounter > BUILDING_APPEAR_TIMEPOINT) { - if(stateTimeCounter >= BUILDED) + if(stateTimeCounter >= BUILD_ANIMATION_FINISHED_TIMEPOINT) { if(parent->selectedBuilding == this) blitAtLoc(border,0,0,to); @@ -191,10 +186,10 @@ void CBuildingRect::show(SDL_Surface * to) SDL_Color oldColor = border->format->palette->colors[colorID]; SDL_Color newColor; - if (stateTimeCounter < S2_WHITE_B) + if (stateTimeCounter < BUILDING_WHITE_BORDER_TIMEPOINT) newColor = multiplyColors(c1, c2, static_cast(stateTimeCounter % stageDelay) / stageDelay); else - if (stateTimeCounter < S3_YELLOW_B) + if (stateTimeCounter < BUILDING_YELLOW_BORDER_TIMEPOINT) newColor = multiplyColors(c2, c3, static_cast(stateTimeCounter % stageDelay) / stageDelay); else newColor = oldColor; @@ -204,7 +199,7 @@ void CBuildingRect::show(SDL_Surface * to) SDL_SetColors(border, &oldColor, colorID, 1); } } - if(stateTimeCounter < BUILDED) + if(stateTimeCounter < BUILD_ANIMATION_FINISHED_TIMEPOINT) stateTimeCounter += GH.mainFPSmng->getElapsedMilliseconds(); } @@ -634,7 +629,7 @@ void CCastleBuildings::addBuilding(BuildingID building) if(structures.size() == 1) buildingRect->stateTimeCounter = 0; // transparency -> fully visible stage else - buildingRect->stateTimeCounter = 500; // already in fully visible stage + buildingRect->stateTimeCounter = CBuildingRect::BUILDING_APPEAR_TIMEPOINT; // already in fully visible stage break; } } diff --git a/client/windows/CCastleInterface.h b/client/windows/CCastleInterface.h index 55bdc31d0..6b9f7f7e8 100644 --- a/client/windows/CCastleInterface.h +++ b/client/windows/CCastleInterface.h @@ -42,6 +42,14 @@ class CBuildingRect : public CShowableAnim { std::string getSubtitle(); public: + enum EBuildingCreationAnimationPhases : uint32_t + { + BUILDING_APPEAR_TIMEPOINT = 500, //500 msec building appears: 0->100% transparency + BUILDING_WHITE_BORDER_TIMEPOINT = 1000, //500 msec border glows from white to yellow + BUILDING_YELLOW_BORDER_TIMEPOINT = 1500, //500 msec border glows from yellow to normal + BUILD_ANIMATION_FINISHED_TIMEPOINT = 2500 //1000 msec delay, nothing happens + }; + /// returns building associated with this structure const CBuilding * getBuilding(); From 8e1d8f835a3c2fa39ff770e8437be1dfe401528d Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 26 Dec 2022 02:37:35 +0400 Subject: [PATCH 45/54] Implemented support of protocol 4 --- client/CMT.cpp | 64 ++++++++++------ launcher/lobby/lobby.h | 31 +++++++- launcher/lobby/lobby_moc.cpp | 65 ++++++++++++++++- launcher/lobby/lobby_moc.h | 7 ++ launcher/lobby/lobby_moc.ui | 138 +++++++++++++++++++++++------------ 5 files changed, 231 insertions(+), 74 deletions(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index d4bc18ce9..55224b771 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -208,6 +208,8 @@ int main(int argc, char * argv[]) ("lobby-host", "if this client hosts session") ("lobby-uuid", po::value(), "uuid to the server") ("lobby-connections", po::value(), "connections of server") + ("lobby-username", po::value(), "player name") + ("lobby-gamemode", po::value(), "use 0 for new game and 1 for load game") ("uuid", po::value(), "uuid for the client"); if(argc > 1) @@ -489,29 +491,8 @@ int main(int argc, char * argv[]) session["autoSkip"].Bool() = vm.count("autoSkip"); session["oneGoodAI"].Bool() = vm.count("oneGoodAI"); session["aiSolo"].Bool() = false; + std::shared_ptr mmenu; - session["lobby"].Bool() = false; - if(vm.count("lobby")) - { - session["lobby"].Bool() = true; - session["host"].Bool() = false; - session["address"].String() = vm["lobby-address"].as(); - CSH->uuid = vm["uuid"].as(); - session["port"].Integer() = vm["lobby-port"].as(); - logGlobal->info("Remote lobby mode at %s:%d, uuid is %s", session["address"].String(), session["port"].Integer(), CSH->uuid); - if(vm.count("lobby-host")) - { - session["host"].Bool() = true; - session["hostConnections"].String() = std::to_string(vm["lobby-connections"].as()); - session["hostUuid"].String() = vm["lobby-uuid"].as(); - logGlobal->info("This client will host session, server uuid is %s", session["hostUuid"].String()); - } - - //we should not reconnect to previous game in online mode - Settings saveSession = settings.write["server"]["reconnect"]; - saveSession->Bool() = false; - } - if(vm.count("testmap")) { session["testmap"].String() = vm["testmap"].as(); @@ -526,7 +507,44 @@ int main(int argc, char * argv[]) } else { - GH.curInt = CMainMenu::create().get(); + mmenu = CMainMenu::create(); + GH.curInt = mmenu.get(); + } + + std::vector names; + session["lobby"].Bool() = false; + if(vm.count("lobby")) + { + session["lobby"].Bool() = true; + session["host"].Bool() = false; + session["address"].String() = vm["lobby-address"].as(); + if(vm.count("lobby-username")) + session["username"].String() = vm["lobby-username"].as(); + else + session["username"].String() = settings["launcher"]["lobbyUsername"].String(); + if(vm.count("lobby-gamemode")) + session["gamemode"].Integer() = vm["lobby-gamemode"].as(); + else + session["gamemode"].Integer() = 0; + CSH->uuid = vm["uuid"].as(); + session["port"].Integer() = vm["lobby-port"].as(); + logGlobal->info("Remote lobby mode at %s:%d, uuid is %s", session["address"].String(), session["port"].Integer(), CSH->uuid); + if(vm.count("lobby-host")) + { + session["host"].Bool() = true; + session["hostConnections"].String() = std::to_string(vm["lobby-connections"].as()); + session["hostUuid"].String() = vm["lobby-uuid"].as(); + logGlobal->info("This client will host session, server uuid is %s", session["hostUuid"].String()); + } + + //we should not reconnect to previous game in online mode + Settings saveSession = settings.write["server"]["reconnect"]; + saveSession->Bool() = false; + + //start lobby immediately + names.push_back(session["username"].String()); + ESelectionScreen sscreen = session["gamemode"].Integer() == 0 ? ESelectionScreen::newGame : ESelectionScreen::loadGame; + mmenu->openLobby(sscreen, session["host"].Bool(), &names, ELoadMode::MULTI); } // Restore remote session - start game immediately diff --git a/launcher/lobby/lobby.h b/launcher/lobby/lobby.h index 3e11e3bbc..192f8624a 100644 --- a/launcher/lobby/lobby.h +++ b/launcher/lobby/lobby.h @@ -12,7 +12,7 @@ #include #include -const unsigned int ProtocolVersion = 3; +const unsigned int ProtocolVersion = 4; const std::string ProtocolEncoding = "utf8"; class ProtocolError: public std::runtime_error @@ -24,10 +24,10 @@ public: enum ProtocolConsts { //client consts - GREETING, USERNAME, MESSAGE, VERSION, CREATE, JOIN, LEAVE, KICK, READY, FORCESTART, + GREETING, USERNAME, MESSAGE, VERSION, CREATE, JOIN, LEAVE, KICK, READY, FORCESTART, HERE, ALIVE, HOSTMODE, //server consts - SESSIONS, CREATED, JOINED, KICKED, SRVERROR, CHAT, START, STATUS, HOST, MODS, CLIENTMODS + SESSIONS, CREATED, JOINED, KICKED, SRVERROR, CHAT, START, STATUS, HOST, MODS, CLIENTMODS, USERS, HEALTH, GAMEMODE }; const QMap ProtocolStrings @@ -78,6 +78,16 @@ const QMap ProtocolStrings //[unsupported] start session immediately //%1: room name {FORCESTART, "%1"}, + + //request user list + {HERE, ""}, + + //used as reponse to healcheck + {ALIVE, ""}, + + //host sets game mode (new game or load game) + //%1: game mode - 0 for new game, 1 for load game + {HOSTMODE, "%1"}, //=== server commands === //server commands are started from :>>, arguments are enumerated by : symbol @@ -140,7 +150,20 @@ const QMap ProtocolStrings //received chat message //arg[0]: sender username //arg[1]: message text - {CHAT, "MSG"} + {CHAT, "MSG"}, + + //list of users currently in lobby + //arg[0]: amount of players, following arguments depend on it + //arg[x]: username + //arg[x+1]: room (empty if not in the room) + {USERS, "USERS"}, + + //healthcheck from server + {HEALTH, "HEALTH"}, + + //game mode (new game or load game) set by host + //arg[0]: game mode + {GAMEMODE, "GAMEMODE"} }; class ServerCommand diff --git a/launcher/lobby/lobby_moc.cpp b/launcher/lobby/lobby_moc.cpp index c52f88f29..7b73b2a86 100644 --- a/launcher/lobby/lobby_moc.cpp +++ b/launcher/lobby/lobby_moc.cpp @@ -135,9 +135,12 @@ void Lobby::serverCommand(const ServerCommand & command) try if(args[1] == username) { ui->buttonReady->setText("Ready"); - ui->chat->clear(); //cleanup the chat + ui->optNewGame->setChecked(true); sysMessage(joinStr.arg("you", args[0])); session = args[0]; + bool isHost = command.command == JOINED && hostSession == session; + ui->optNewGame->setEnabled(isHost); + ui->optLoadGame->setEnabled(isHost); ui->stackedWidget->setCurrentWidget(command.command == JOINED ? ui->roomPage : ui->sessionsPage); } else @@ -217,6 +220,8 @@ void Lobby::serverCommand(const ServerCommand & command) try gameArgs << "--lobby"; gameArgs << "--lobby-address" << serverUrl; gameArgs << "--lobby-port" << QString::number(serverPort); + gameArgs << "--lobby-username" << username; + gameArgs << "--lobby-gamemode" << QString::number(isLoadGameMode); gameArgs << "--uuid" << args[0]; startGame(gameArgs); break; @@ -238,6 +243,35 @@ void Lobby::serverCommand(const ServerCommand & command) try chatMessage(args[0], msg); break; } + + case HEALTH: { + protocolAssert(args.size() == 0); + socketLobby.send(ProtocolStrings[ALIVE]); + break; + } + + case USERS: { + protocolAssert(args.size() > 0); + amount = args[0].toInt(); + + protocolAssert(amount == (args.size() - 1)); + ui->listUsers->clear(); + for(int i = 0; i < amount; ++i) + { + ui->listUsers->addItem(new QListWidgetItem(args[i + 1])); + } + break; + } + + case GAMEMODE: { + protocolAssert(args.size() == 1); + isLoadGameMode = args[0].toInt(); + if(isLoadGameMode == 1) + ui->optLoadGame->setChecked(true); + else + ui->optNewGame->setChecked(true); + break; + } default: sysMessage("Unknown server command"); @@ -291,7 +325,7 @@ void Lobby::onDisconnected() ui->userEdit->setEnabled(true); ui->newButton->setEnabled(false); ui->joinButton->setEnabled(false); - ui->sessionsTable->clear(); + ui->sessionsTable->setRowCount(0); } void Lobby::chatMessage(QString title, QString body, bool isSystem) @@ -329,6 +363,7 @@ void Lobby::on_connectButton_toggled(bool checked) { if(checked) { + ui->connectButton->setText("Disconnect"); authentificationStatus = AuthStatus::AUTH_NONE; username = ui->userEdit->text(); const int connectionTimeout = settings["launcher"]["connectionTimeout"].Integer(); @@ -360,8 +395,10 @@ void Lobby::on_connectButton_toggled(bool checked) } else { + ui->connectButton->setText("Connection"); ui->serverEdit->setEnabled(true); ui->userEdit->setEnabled(true); + ui->listUsers->clear(); socketLobby.disconnectServer(); } } @@ -417,3 +454,27 @@ void Lobby::on_kickButton_clicked() socketLobby.send(ProtocolStrings[KICK].arg(ui->playersList->currentItem()->text())); } + +void Lobby::on_buttonResolve_clicked() +{ + //TODO: auto-resolve mods conflicts +} + +void Lobby::on_optNewGame_toggled(bool checked) +{ + if(checked) + { + if(isLoadGameMode) + socketLobby.send(ProtocolStrings[HOSTMODE].arg(0)); + } +} + +void Lobby::on_optLoadGame_toggled(bool checked) +{ + if(checked) + { + if(!isLoadGameMode) + socketLobby.send(ProtocolStrings[HOSTMODE].arg(1)); + } +} + diff --git a/launcher/lobby/lobby_moc.h b/launcher/lobby/lobby_moc.h index 364500223..368d00b3c 100644 --- a/launcher/lobby/lobby_moc.h +++ b/launcher/lobby/lobby_moc.h @@ -49,9 +49,16 @@ private slots: void on_kickButton_clicked(); + void on_buttonResolve_clicked(); + + void on_optNewGame_toggled(bool checked); + + void on_optLoadGame_toggled(bool checked); + private: QString serverUrl; int serverPort; + bool isLoadGameMode = false; Ui::Lobby *ui; SocketLobby socketLobby; diff --git a/launcher/lobby/lobby_moc.ui b/launcher/lobby/lobby_moc.ui index 44d410903..0ceda38a8 100644 --- a/launcher/lobby/lobby_moc.ui +++ b/launcher/lobby/lobby_moc.ui @@ -7,7 +7,7 @@ 0 0 652 - 329 + 331 @@ -30,9 +30,6 @@ - - - @@ -56,13 +53,6 @@ - - - - true - - - @@ -147,34 +137,10 @@ - - + + - Ready - - - - - - - Mods mismatch - - - - - - - Leave - - - - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::NoSelection + Kick player @@ -191,13 +157,6 @@ - - - - Kick player - - - @@ -205,10 +164,99 @@ + + + + Leave + + + + + + + Mods mismatch + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoSelection + + + + + + + Ready + + + + + + + Resolve conflicts + + + + + + + 0 + + + + + New game + + + + + + + Load game + + + + + + + + + 0 + + + + + + 0 + 120 + + + + QAbstractItemView::NoEditTriggers + + + + + + + true + + + + + + + + From 14fc1c3f2641f5efe4bc5f4d9921f6b202b9d3d2 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 26 Dec 2022 02:52:24 +0400 Subject: [PATCH 46/54] Cosmetic changes --- launcher/lobby/lobby_moc.ui | 399 ++++++++++++++++++++---------------- 1 file changed, 222 insertions(+), 177 deletions(-) diff --git a/launcher/lobby/lobby_moc.ui b/launcher/lobby/lobby_moc.ui index 0ceda38a8..5b016f7c6 100644 --- a/launcher/lobby/lobby_moc.ui +++ b/launcher/lobby/lobby_moc.ui @@ -7,13 +7,26 @@ 0 0 652 - 331 + 383 Form + + + + + 0 + 0 + + + + Username + + + @@ -30,22 +43,6 @@ - - - - - 0 - 0 - - - - Username - - - - - - @@ -60,201 +57,249 @@ - - - + + + + + + 0 - - - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - 80 - - - false - - - true - - - 20 - - - 20 - - - - Session - - - - - Players - - - - - - - - - - - - - false - + + + + 0 + + + - New room + People in lobby - - - - false + + + + + 0 + 0 + - - Join room + + + 16777215 + 96 + - - - - - - - - - - Kick player + + 0 - - - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - - - - - Players in the room - - - - - - - Leave - - - - - - - Mods mismatch - - - - - QAbstractItemView::NoEditTriggers QAbstractItemView::NoSelection + + true + + + QListView::SinglePass + - - + + - Ready + Lobby chat - - - - Resolve conflicts + + + + true - - - - 0 - - - - - New game - - - - - - - Load game - - - - + + - - - - - - - 0 - + - + 0 - 120 + 0 - - QAbstractItemView::NoEditTriggers + + 0 + + + + + + false + + + New room + + + + + + + false + + + Join room + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + 80 + + + false + + + true + + + 20 + + + 20 + + + + Session + + + + + Players + + + + + + + + + + + + + + + + + Kick player + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + + + + + Players in the room + + + + + + + Leave + + + + + + + Mods mismatch + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::MultiSelection + + + + + + + Ready + + + + + + + Resolve + + + + + + + 0 + + + + + New game + + + + + + + Load game + + + + + + + - - - - true - - - - - - From a2e358876c6057da4b12c969a0d3626dd0474611 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 26 Dec 2022 03:13:34 +0400 Subject: [PATCH 47/54] Health check fix --- launcher/lobby/lobby_moc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/lobby/lobby_moc.cpp b/launcher/lobby/lobby_moc.cpp index 7b73b2a86..f177cc2b9 100644 --- a/launcher/lobby/lobby_moc.cpp +++ b/launcher/lobby/lobby_moc.cpp @@ -245,7 +245,6 @@ void Lobby::serverCommand(const ServerCommand & command) try } case HEALTH: { - protocolAssert(args.size() == 0); socketLobby.send(ProtocolStrings[ALIVE]); break; } From 2cce15efbe3f64173f24f30d73af234ff0db85d1 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Mon, 26 Dec 2022 04:34:10 +0400 Subject: [PATCH 48/54] Automatic mod conflict resolution --- launcher/lobby/lobby_moc.cpp | 130 +++++++++++++++++++---- launcher/lobby/lobby_moc.h | 9 ++ launcher/mainwindow_moc.cpp | 4 + launcher/modManager/cmodlistview_moc.cpp | 22 +++- launcher/modManager/cmodlistview_moc.h | 6 ++ 5 files changed, 146 insertions(+), 25 deletions(-) diff --git a/launcher/lobby/lobby_moc.cpp b/launcher/lobby/lobby_moc.cpp index f177cc2b9..7aed18e60 100644 --- a/launcher/lobby/lobby_moc.cpp +++ b/launcher/lobby/lobby_moc.cpp @@ -134,6 +134,7 @@ void Lobby::serverCommand(const ServerCommand & command) try if(args[1] == username) { + hostModsMap.clear(); ui->buttonReady->setText("Ready"); ui->optNewGame->setChecked(true); sysMessage(joinStr.arg("you", args[0])); @@ -155,33 +156,15 @@ void Lobby::serverCommand(const ServerCommand & command) try protocolAssert(amount * 2 == (args.size() - 1)); tagPoint = 1; - ui->modsList->clear(); - auto enabledMods = buildModsMap(); for(int i = 0; i < amount; ++i, tagPoint += 2) - { - if(enabledMods.contains(args[tagPoint])) - { - if(enabledMods[args[tagPoint]] == args[tagPoint + 1]) - enabledMods.remove(args[tagPoint]); - else - ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-update.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1]))); - } - else if(isModAvailable(args[tagPoint], args[tagPoint + 1])) - ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-enabled.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1]))); - else - ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-delete.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1]))); - } - for(auto & remainMod : enabledMods.keys()) - { - ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-disabled.png"), QString("%1 (v%2)").arg(remainMod, enabledMods[remainMod]))); - } - if(!ui->modsList->count()) - ui->modsList->addItem("No issues detected"); + hostModsMap[args[tagPoint]] = args[tagPoint + 1]; + + updateMods(); break; } case CLIENTMODS: { - protocolAssert(args.size() > 1); + protocolAssert(args.size() >= 1); amount = args[1].toInt(); protocolAssert(amount * 2 == (args.size() - 2)); @@ -398,10 +381,87 @@ void Lobby::on_connectButton_toggled(bool checked) ui->serverEdit->setEnabled(true); ui->userEdit->setEnabled(true); ui->listUsers->clear(); + hostModsMap.clear(); + updateMods(); socketLobby.disconnectServer(); } } +void Lobby::updateMods() +{ + ui->modsList->clear(); + if(hostModsMap.empty()) + return; + + auto enabledMods = buildModsMap(); + for(auto & mod : hostModsMap.keys()) + { + auto & modValue = hostModsMap[mod]; + auto modName = QString("%1 (v%2)").arg(mod, modValue); + //first - mod name + //second.first - should mod be enabled + //second.second - is possible to resolve + QMap modData; + QList modDataVal; + if(enabledMods.contains(mod)) + { + if(enabledMods[mod] == modValue) + enabledMods.remove(mod); //mod fully matches, remove from list + else + { + modDataVal.append(true); + modDataVal.append(false); + modData[mod] = modDataVal; + auto * lw = new QListWidgetItem(QIcon("icons:mod-update.png"), modName); //mod version mismatch + lw->setData(Qt::UserRole, modData); + ui->modsList->addItem(lw); + } + } + else if(isModAvailable(mod, modValue)) + { + modDataVal.append(true); + modDataVal.append(true); + modData[mod] = modDataVal; + auto * lw = new QListWidgetItem(QIcon("icons:mod-enabled.png"), modName); //mod available and needs to be enabled + lw->setData(Qt::UserRole, modData); + ui->modsList->addItem(lw); + } + else + { + modDataVal.append(true); + modDataVal.append(false); + modData[mod] = modDataVal; + auto * lw = new QListWidgetItem(QIcon("icons:mod-delete.png"), modName); //mod is not available and needs to be installed + lw->setData(Qt::UserRole, modData); + ui->modsList->addItem(lw); + } + } + for(auto & remainMod : enabledMods.keys()) + { + //first - mod name + //second.first - should mod be enabled + //second.second - is possible to resolve + QMap modData; + QList modDataVal; + modDataVal.append(false); + modDataVal.append(true); + modData[remainMod] = modDataVal; + auto modName = QString("%1 (v%2)").arg(remainMod, enabledMods[remainMod]); + auto * lw = new QListWidgetItem(QIcon("icons:mod-disabled.png"), modName); //mod needs to be disabled + lw->setData(Qt::UserRole, modData); + ui->modsList->addItem(lw); + } + if(!ui->modsList->count()) + { + ui->buttonResolve->setEnabled(false); + ui->modsList->addItem("No issues detected"); + } + else + { + ui->buttonResolve->setEnabled(true); + } +} + void Lobby::on_newButton_clicked() { new LobbyRoomRequest(socketLobby, "", buildModsMap(), this); @@ -456,7 +516,31 @@ void Lobby::on_kickButton_clicked() void Lobby::on_buttonResolve_clicked() { - //TODO: auto-resolve mods conflicts + QStringList toEnableList, toDisableList; + for(auto * item : ui->modsList->selectedItems()) + { + auto data = item->data(Qt::UserRole); + if(data.isNull()) + continue; + + auto modData = data.toMap(); + assert(modData.size() == 1); + auto modDataVal = modData.begin()->toList(); + assert(modDataVal.size() == 2); + if(!modDataVal[1].toBool()) + continue; + + if(modDataVal[0].toBool()) + toEnableList << modData.begin().key(); + else + toDisableList << modData.begin().key(); + + } + + for(auto & mod : toDisableList) + emit disableMod(mod); + for(auto & mod : toEnableList) + emit enableMod(mod); } void Lobby::on_optNewGame_toggled(bool checked) diff --git a/launcher/lobby/lobby_moc.h b/launcher/lobby/lobby_moc.h index 368d00b3c..1edb32cdc 100644 --- a/launcher/lobby/lobby_moc.h +++ b/launcher/lobby/lobby_moc.h @@ -22,6 +22,14 @@ class Lobby : public QWidget public: explicit Lobby(QWidget *parent = nullptr); ~Lobby(); + +signals: + + void enableMod(QString mod); + void disableMod(QString mod); + +public slots: + void updateMods(); private slots: void on_messageEdit_returnPressed(); @@ -66,6 +74,7 @@ private: QString session; QString username; QStringList gameArgs; + QMap hostModsMap; enum AuthStatus { diff --git a/launcher/mainwindow_moc.cpp b/launcher/mainwindow_moc.cpp index 5b71b9043..ea9e6bb38 100644 --- a/launcher/mainwindow_moc.cpp +++ b/launcher/mainwindow_moc.cpp @@ -53,6 +53,10 @@ MainWindow::MainWindow(QWidget * parent) load(); // load FS before UI ui->setupUi(this); + + connect(ui->lobbyView, &Lobby::enableMod, ui->modlistView, &CModListView::enableModByName); + connect(ui->lobbyView, &Lobby::disableMod, ui->modlistView, &CModListView::disableModByName); + connect(ui->modlistView, &CModListView::modsChanged, ui->lobbyView, &Lobby::updateMods); //load window settings QSettings s(Ui::teamName, Ui::appName); diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index e4c731127..155d89579 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -496,7 +496,14 @@ QStringList CModListView::findDependentMods(QString mod, bool excludeDisabled) void CModListView::on_enableButton_clicked() { QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); + + enableModByName(modName); + + checkManagerErrors(); +} +void CModListView::enableModByName(QString modName) +{ assert(findBlockingMods(modName).empty()); assert(findInvalidDependencies(modName).empty()); @@ -505,17 +512,24 @@ void CModListView::on_enableButton_clicked() if(modModel->getMod(name).isDisabled()) manager->enableMod(name); } - checkManagerErrors(); + emit modsChanged(); } void CModListView::on_disableButton_clicked() { QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); + disableModByName(modName); + + checkManagerErrors(); +} + +void CModListView::disableModByName(QString modName) +{ if(modModel->hasMod(modName) && modModel->getMod(modName).isEnabled()) manager->disableMod(modName); - checkManagerErrors(); + emit modsChanged(); } void CModListView::on_updateButton_clicked() @@ -544,6 +558,8 @@ void CModListView::on_uninstallButton_clicked() manager->disableMod(modName); manager->uninstallMod(modName); } + + emit modsChanged(); checkManagerErrors(); } @@ -631,6 +647,8 @@ void CModListView::downloadFinished(QStringList savedFiles, QStringList failedFi if(doInstallFiles) installFiles(savedFiles); + + emit modsChanged(); } void CModListView::hideProgressBar() diff --git a/launcher/modManager/cmodlistview_moc.h b/launcher/modManager/cmodlistview_moc.h index 688a79d05..a23f0e713 100644 --- a/launcher/modManager/cmodlistview_moc.h +++ b/launcher/modManager/cmodlistview_moc.h @@ -66,6 +66,8 @@ class CModListView : public QWidget signals: void extraResolutionsEnabledChanged(bool enabled); + + void modsChanged(); public: explicit CModListView(QWidget * parent = 0); @@ -82,6 +84,10 @@ public: bool isExtraResolutionsModEnabled() const; const CModList & getModList() const; + +public slots: + void enableModByName(QString modName); + void disableModByName(QString modName); private slots: void dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight); From da085b005851c77a0c65a89ade772d7f2450aee6 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Tue, 3 Jan 2023 13:57:51 +0400 Subject: [PATCH 49/54] Code review tweaks --- launcher/lobby/lobby.h | 2 +- launcher/lobby/lobby_moc.cpp | 100 ++++++++++++++++------------------- 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/launcher/lobby/lobby.h b/launcher/lobby/lobby.h index 192f8624a..2ff03f721 100644 --- a/launcher/lobby/lobby.h +++ b/launcher/lobby/lobby.h @@ -163,7 +163,7 @@ const QMap ProtocolStrings //game mode (new game or load game) set by host //arg[0]: game mode - {GAMEMODE, "GAMEMODE"} + {GAMEMODE, "GAMEMODE"}, }; class ServerCommand diff --git a/launcher/lobby/lobby_moc.cpp b/launcher/lobby/lobby_moc.cpp index 7aed18e60..1137dca1a 100644 --- a/launcher/lobby/lobby_moc.cpp +++ b/launcher/lobby/lobby_moc.cpp @@ -16,6 +16,18 @@ #include "../modManager/cmodlist.h" #include "../../lib/CConfigHandler.h" +enum GameMode +{ + NEW_GAME = 0, LOAD_GAME = 1 +}; + +enum ModResolutionRoles +{ + ModNameRole = Qt::UserRole + 1, + ModEnableRole, + ModResolvableRole +}; + Lobby::Lobby(QWidget *parent) : QWidget(parent), ui(new Ui::Lobby) @@ -248,7 +260,7 @@ void Lobby::serverCommand(const ServerCommand & command) try case GAMEMODE: { protocolAssert(args.size() == 1); isLoadGameMode = args[0].toInt(); - if(isLoadGameMode == 1) + if(isLoadGameMode) ui->optLoadGame->setChecked(true); else ui->optNewGame->setChecked(true); @@ -345,7 +357,7 @@ void Lobby::on_connectButton_toggled(bool checked) { if(checked) { - ui->connectButton->setText("Disconnect"); + ui->connectButton->setText(tr("Disconnect")); authentificationStatus = AuthStatus::AUTH_NONE; username = ui->userEdit->text(); const int connectionTimeout = settings["launcher"]["connectionTimeout"].Integer(); @@ -377,7 +389,7 @@ void Lobby::on_connectButton_toggled(bool checked) } else { - ui->connectButton->setText("Connection"); + ui->connectButton->setText(tr("Connect")); ui->serverEdit->setEnabled(true); ui->userEdit->setEnabled(true); ui->listUsers->clear(); @@ -393,68 +405,51 @@ void Lobby::updateMods() if(hostModsMap.empty()) return; + auto createModListWidget = [](const QIcon & icon, const QString & label, const QString & name, bool enableFlag, bool resolveFlag) + { + auto * lw = new QListWidgetItem(icon, label); //mod version mismatch + lw->setData(ModResolutionRoles::ModNameRole, name); + lw->setData(ModResolutionRoles::ModEnableRole, enableFlag); + lw->setData(ModResolutionRoles::ModResolvableRole, resolveFlag); + return lw; + }; + auto enabledMods = buildModsMap(); - for(auto & mod : hostModsMap.keys()) + for(const auto & mod : hostModsMap.keys()) { auto & modValue = hostModsMap[mod]; auto modName = QString("%1 (v%2)").arg(mod, modValue); - //first - mod name - //second.first - should mod be enabled - //second.second - is possible to resolve - QMap modData; - QList modDataVal; if(enabledMods.contains(mod)) { if(enabledMods[mod] == modValue) enabledMods.remove(mod); //mod fully matches, remove from list else { - modDataVal.append(true); - modDataVal.append(false); - modData[mod] = modDataVal; - auto * lw = new QListWidgetItem(QIcon("icons:mod-update.png"), modName); //mod version mismatch - lw->setData(Qt::UserRole, modData); - ui->modsList->addItem(lw); + //mod version mismatch + ui->modsList->addItem(createModListWidget(QIcon("icons:mod-update.png"), modName, mod, true, false)); } } else if(isModAvailable(mod, modValue)) { - modDataVal.append(true); - modDataVal.append(true); - modData[mod] = modDataVal; - auto * lw = new QListWidgetItem(QIcon("icons:mod-enabled.png"), modName); //mod available and needs to be enabled - lw->setData(Qt::UserRole, modData); - ui->modsList->addItem(lw); + //mod is available and needs to be enabled + ui->modsList->addItem(createModListWidget(QIcon("icons:mod-enabled.png"), modName, mod, true, true)); } else { - modDataVal.append(true); - modDataVal.append(false); - modData[mod] = modDataVal; - auto * lw = new QListWidgetItem(QIcon("icons:mod-delete.png"), modName); //mod is not available and needs to be installed - lw->setData(Qt::UserRole, modData); - ui->modsList->addItem(lw); + //mod is not available and needs to be installed + ui->modsList->addItem(createModListWidget(QIcon("icons:mod-delete.png"), modName, mod, true, false)); } } - for(auto & remainMod : enabledMods.keys()) + for(const auto & remainMod : enabledMods.keys()) { - //first - mod name - //second.first - should mod be enabled - //second.second - is possible to resolve - QMap modData; - QList modDataVal; - modDataVal.append(false); - modDataVal.append(true); - modData[remainMod] = modDataVal; auto modName = QString("%1 (v%2)").arg(remainMod, enabledMods[remainMod]); - auto * lw = new QListWidgetItem(QIcon("icons:mod-disabled.png"), modName); //mod needs to be disabled - lw->setData(Qt::UserRole, modData); - ui->modsList->addItem(lw); + //mod needs to be disabled + ui->modsList->addItem(createModListWidget(QIcon("icons:mod-disabled.png"), modName, remainMod, false, true)); } if(!ui->modsList->count()) { ui->buttonResolve->setEnabled(false); - ui->modsList->addItem("No issues detected"); + ui->modsList->addItem(tr("No issues detected")); } else { @@ -519,24 +514,23 @@ void Lobby::on_buttonResolve_clicked() QStringList toEnableList, toDisableList; for(auto * item : ui->modsList->selectedItems()) { - auto data = item->data(Qt::UserRole); - if(data.isNull()) + auto modName = item->data(ModResolutionRoles::ModNameRole); + if(modName.isNull()) continue; - auto modData = data.toMap(); - assert(modData.size() == 1); - auto modDataVal = modData.begin()->toList(); - assert(modDataVal.size() == 2); - if(!modDataVal[1].toBool()) + bool modToEnable = item->data(ModResolutionRoles::ModEnableRole).toBool(); + bool modToResolve = item->data(ModResolutionRoles::ModResolvableRole).toBool(); + + if(!modToResolve) continue; - if(modDataVal[0].toBool()) - toEnableList << modData.begin().key(); + if(modToEnable) + toEnableList << modName.toString(); else - toDisableList << modData.begin().key(); - + toDisableList << modName.toString(); } + //disabling first, then enabling for(auto & mod : toDisableList) emit disableMod(mod); for(auto & mod : toEnableList) @@ -548,7 +542,7 @@ void Lobby::on_optNewGame_toggled(bool checked) if(checked) { if(isLoadGameMode) - socketLobby.send(ProtocolStrings[HOSTMODE].arg(0)); + socketLobby.send(ProtocolStrings[HOSTMODE].arg(GameMode::NEW_GAME)); } } @@ -557,7 +551,7 @@ void Lobby::on_optLoadGame_toggled(bool checked) if(checked) { if(!isLoadGameMode) - socketLobby.send(ProtocolStrings[HOSTMODE].arg(1)); + socketLobby.send(ProtocolStrings[HOSTMODE].arg(GameMode::LOAD_GAME)); } } From 1ef9610e30a06ef43c387991492690a6b6089473 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Tue, 3 Jan 2023 15:36:26 +0400 Subject: [PATCH 50/54] Remove irrelevant comment --- launcher/lobby/lobby_moc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/lobby/lobby_moc.cpp b/launcher/lobby/lobby_moc.cpp index 1137dca1a..e6689bc9c 100644 --- a/launcher/lobby/lobby_moc.cpp +++ b/launcher/lobby/lobby_moc.cpp @@ -407,7 +407,7 @@ void Lobby::updateMods() auto createModListWidget = [](const QIcon & icon, const QString & label, const QString & name, bool enableFlag, bool resolveFlag) { - auto * lw = new QListWidgetItem(icon, label); //mod version mismatch + auto * lw = new QListWidgetItem(icon, label); lw->setData(ModResolutionRoles::ModNameRole, name); lw->setData(ModResolutionRoles::ModEnableRole, enableFlag); lw->setData(ModResolutionRoles::ModResolvableRole, resolveFlag); From d0b5a023ee2ac226e2144b84e905bff9c4bed799 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Tue, 3 Jan 2023 16:11:12 +0400 Subject: [PATCH 51/54] Fix bug --- client/CMT.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index 55224b771..a0eb08468 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -209,7 +209,7 @@ int main(int argc, char * argv[]) ("lobby-uuid", po::value(), "uuid to the server") ("lobby-connections", po::value(), "connections of server") ("lobby-username", po::value(), "player name") - ("lobby-gamemode", po::value(), "use 0 for new game and 1 for load game") + ("lobby-gamemode", po::value(), "use 0 for new game and 1 for load game") ("uuid", po::value(), "uuid for the client"); if(argc > 1) From a2581fb942740efb3644b635badae04dd8f7437b Mon Sep 17 00:00:00 2001 From: nordsoft Date: Tue, 3 Jan 2023 23:55:47 +0400 Subject: [PATCH 52/54] Fix crash at start --- client/NetPacksLobbyClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/NetPacksLobbyClient.cpp b/client/NetPacksLobbyClient.cpp index fea5e9a19..b9459b6f6 100644 --- a/client/NetPacksLobbyClient.cpp +++ b/client/NetPacksLobbyClient.cpp @@ -63,7 +63,7 @@ void LobbyClientDisconnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHa void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler) { - if(lobby) + if(lobby && lobby->card) { lobby->card->chat->addNewMessage(playerName + ": " + message); lobby->card->setChat(true); From c0c1be26455cb728a5d3c536ad6385c4f9ac1af3 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Wed, 4 Jan 2023 00:05:59 +0400 Subject: [PATCH 53/54] Fix invalid host connection --- server/CVCMIServer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index b399b9ef9..e913afe6e 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -201,6 +201,10 @@ void CVCMIServer::run() void CVCMIServer::establishRemoteConnections() { + //wait for host connection + while(connections.empty()) + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + uuid = cmdLineOptions["lobby-uuid"].as(); int numOfConnections = cmdLineOptions["connections"].as(); auto address = cmdLineOptions["lobby"].as(); From c3b254a815982c64a99530004c8915751f6d6364 Mon Sep 17 00:00:00 2001 From: Dydzio Date: Mon, 16 Jan 2023 18:26:31 +0100 Subject: [PATCH 54/54] Enum value can be used here as well --- client/windows/CCastleInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 8c8852be9..f55c1869b 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -47,7 +47,7 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town parent(Par), town(Town), str(Str), - stateTimeCounter(2500) + stateTimeCounter(BUILD_ANIMATION_FINISHED_TIMEPOINT) { addUsedEvents(LCLICK | RCLICK | HOVER); pos.x += str->pos.x;