From 182100dfd103999bdf7cfefd9731635c2ad42f3b Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 17 Aug 2025 03:30:42 +0200 Subject: [PATCH] button for more than 2 layers --- Mods/vcmi/Content/Sprites/adventureLayers.png | Bin 0 -> 1384 bytes client/adventureMap/AdventureMapShortcuts.cpp | 4 +- client/adventureMap/AdventureMapShortcuts.h | 2 +- client/adventureMap/AdventureMapWidget.cpp | 13 ++- client/render/AssetGenerator.cpp | 107 +++++++++++++++++- client/render/AssetGenerator.h | 2 + client/render/Canvas.cpp | 7 ++ client/render/Canvas.h | 3 + client/renderSDL/RenderHandler.cpp | 2 + config/widgets/adventureMap.json | 31 +++++ 10 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 Mods/vcmi/Content/Sprites/adventureLayers.png diff --git a/Mods/vcmi/Content/Sprites/adventureLayers.png b/Mods/vcmi/Content/Sprites/adventureLayers.png new file mode 100644 index 0000000000000000000000000000000000000000..67bca3d3d09196319652234c25920397205c397c GIT binary patch literal 1384 zcmV-u1(*7XP)|(1fk9LR#z7QRRB%NtCTKObMyXV7 zt!W!IsYw@uc6n)=7}GSGE>=x9jV*1hP*K|$P5V$42%;jQBB+SKh&Uk5$N-MB-9A`Z z0vN?MO%s2wXZv#Rx#wJXq9^*lD|3c9{Bhz%e*-gS#?C~t8p%>5g%a_(GPwFcZZ6(` z6<|?n>}(-;ThXY}Cr&gHZytc9zLnw=R$3kIG9l%sE~hK^x037a{}@18TAC8-t$D^L zRU36WRl?M%gGh)UNPxf6;p6uBsJddOu&4pM&DA8Od|zt#dH<0bd%pqh6|P$)r7tVk z;5U5Q)JWpZA!szJK4W}7iKX5_QSl9~S=w9@wn{~A=nJG*fCZ`XdNuBKLdaD^q5>mR zCKxeA8&Ins?Zzzit(2W>qO!t%2gnvuy=8A$W)WZ8gZhqYb0HvF!RAmW4R;)?S+}Y9SwCcwyd|$@! z0A@_nAtmZFr8gIQrJ@EhpqDFnZbr;PDMgl|QB9vb*~sWoq3DDB`d$2v(?fYhGeyU( z+;#d+3)sB3pnCUxd!J{Uks7-N!Rr7MlEWD{CX9h0x`!^@;&5@c{3a()G~)G2fbRnX z+Sg?NZf)!8*9{;u6Senx?8^wb);QFZxcG(VnUIpi)W1ldR#ZzM7zu+PHH zHYcTJP26k=W9f=zB#w^fN>vScIs3VA`gk+&iN8y|^~d6>mL32Z(_^;*YbK?o^Jdl> zbUJN^&Fyw`{#+%85B@<@T`?1rBN#SRk4hzw5^glMQBvASyJs*fR<0l+F&?efuS>j# zFSqUF$iV{uzvLBM&Fld%fBLWxtx&&)tZrRT`ovyY2qry8YXlj3u4%PqQgrw71&&6{V| z>RP-TgkXJSw3*E1OGr*hA}FZ)(bv>iI9Ys}aTCT67ia2bbGclUoH|SPx8Gs4R3nkQ zoqFY!Lx-yFcJ=G|3Q|oe2;X}Wv2bissA_aVD7BUrPLx5uatVvJ$tSAh@ok#C0DA$f~DTU$r3A9s~xu`>=44YIWNDqp=+%N z4*3@+n}eMi-x`TrYmD$WPMb1_nCM^>^@F!zOpj2bZzZI@l)@%Xr0ZKt5Hf!lVMMItvF6u#bdq{jK6 z+>f;8RwpNGnsv6>J|Wlp>(pr}6OEWn2Cn`6BXYF3&q^UbO3JUv`4D^$lk;)^gjqinterface()->cb->getMapSize().z > 1; } -bool AdventureMapShortcuts::optionMapLevelSurface() +int AdventureMapShortcuts::optionMapLevel() { - return mapLevel == 0; + return mapLevel; } bool AdventureMapShortcuts::optionHeroSleeping() diff --git a/client/adventureMap/AdventureMapShortcuts.h b/client/adventureMap/AdventureMapShortcuts.h index b314e7bbd..4268fbfd2 100644 --- a/client/adventureMap/AdventureMapShortcuts.h +++ b/client/adventureMap/AdventureMapShortcuts.h @@ -84,7 +84,7 @@ public: bool optionCanViewQuests(); bool optionCanToggleLevel(); - bool optionMapLevelSurface(); + int optionMapLevel(); bool optionHeroSleeping(); bool optionHeroAwake(); bool optionHeroSelected(); diff --git a/client/adventureMap/AdventureMapWidget.cpp b/client/adventureMap/AdventureMapWidget.cpp index f5fc3f8e2..65c0828a0 100644 --- a/client/adventureMap/AdventureMapWidget.cpp +++ b/client/adventureMap/AdventureMapWidget.cpp @@ -30,7 +30,9 @@ #include "../CPlayerInterface.h" #include "../PlayerLocalState.h" +#include "../../lib/callback/CCallback.h" #include "../../lib/constants/StringConstants.h" +#include "../../lib/mapping/CMapHeader.h" #include "../../lib/filesystem/ResourcePath.h" AdventureMapWidget::AdventureMapWidget( std::shared_ptr shortcuts ) @@ -402,6 +404,8 @@ void AdventureMapWidget::updateActiveStateChildden(CIntObject * widget) { auto container = dynamic_cast(entry); + int mapLevels = GAME->interface()->cb->getMapHeader()->mapLevels; + if (container) { if (container->disableCondition == "heroAwake") @@ -410,11 +414,14 @@ void AdventureMapWidget::updateActiveStateChildden(CIntObject * widget) if (container->disableCondition == "heroSleeping") container->setEnabled(shortcuts->optionHeroSleeping()); - if (container->disableCondition == "mapLayerSurface") // TODO: multilevel support - container->setEnabled(shortcuts->optionMapLevelSurface()); + if (container->disableCondition == "mapLayerSurface") + container->setEnabled(shortcuts->optionMapLevel() == 0); if (container->disableCondition == "mapLayerUnderground") - container->setEnabled(!shortcuts->optionMapLevelSurface()); + container->setEnabled(shortcuts->optionMapLevel() == mapLevels - 1); + + if (container->disableCondition == "mapLayerOther") + container->setEnabled(shortcuts->optionMapLevel() > 0 && shortcuts->optionMapLevel() < mapLevels - 1); if (container->disableCondition == "mapViewMode") container->setEnabled(shortcuts->optionInWorldView()); diff --git a/client/render/AssetGenerator.cpp b/client/render/AssetGenerator.cpp index e7e36540e..039bce5b4 100644 --- a/client/render/AssetGenerator.cpp +++ b/client/render/AssetGenerator.cpp @@ -56,6 +56,8 @@ void AssetGenerator::initialize() for(int i = 1; i < 9; i++) imageFiles[ImagePath::builtin("CampaignHc" + std::to_string(i) + "Image.png")] = [this, i](){ return createChroniclesCampaignImages(i);}; + + animationFiles[AnimationPath::builtin("SPRITES/adventureLayersButton")] = createAdventureMapButton(ImagePath::builtin("adventureLayers.png")); createPaletteShiftedSprites(); } @@ -428,5 +430,108 @@ AssetGenerator::CanvasPtr AssetGenerator::createPaletteShiftedImage(const Animat canvas.draw(img, Point((32 - img->dimensions().x) / 2, (32 - img->dimensions().y) / 2)); return image; - +} + +void meanImage(AssetGenerator::CanvasPtr dst, std::vector & images) +{ + auto image = dst->getCanvas(); + + for(int x = 0; x < dst->width(); x++) + for(int y = 0; y < dst->height(); y++) + { + int sumR = 0; + int sumG = 0; + int sumB = 0; + int sumA = 0; + for(auto & img : images) + { + auto color = img.getPixel(Point(x, y)); + sumR += color.r; + sumG += color.g; + sumB += color.b; + sumA += color.a; + } + int ct = images.size(); + image.drawPoint(Point(x, y), ColorRGBA(sumR / ct, sumG / ct, sumB / ct, sumA / ct)); + } +} + +AssetGenerator::CanvasPtr AssetGenerator::createAdventureMapButtonClear(const PlayerColor & player) const +{ + auto imageNames = { "iam002", "iam003", "iam004", "iam005", "iam006", "iam007", "iam008", "iam009", "iam010", "iam011" }; + std::vector images; + + CanvasPtr dst = nullptr; + for(auto & imageName : imageNames) + { + auto animation = ENGINE->renderHandler().loadAnimation(AnimationPath::builtin(imageName), EImageBlitMode::COLORKEY); + animation->playerColored(player); + auto image = ENGINE->renderHandler().createImage(animation->getImage(2)->dimensions(), CanvasScalingPolicy::IGNORE); + if(!dst) + dst = ENGINE->renderHandler().createImage(animation->getImage(2)->dimensions(), CanvasScalingPolicy::IGNORE); + Canvas canvas = image->getCanvas(); + canvas.draw(animation->getImage(2), Point(0, 0)); + images.push_back(image->getCanvas()); + } + + meanImage(dst, images); + + return dst; +} + +AssetGenerator::AnimationLayoutMap AssetGenerator::createAdventureMapButton(const ImagePath & overlay) +{ + std::shared_ptr overlayImg = ENGINE->renderHandler().loadImage(ImageLocator(overlay, EImageBlitMode::OPAQUE)); + auto overlayCanvasImg = ENGINE->renderHandler().createImage(overlayImg->dimensions(), CanvasScalingPolicy::IGNORE); + Canvas overlayCanvas = overlayCanvasImg->getCanvas(); + overlayCanvas.draw(overlayImg, Point(0, 0)); + + AnimationLayoutMap layout; + for (PlayerColor color(0); color < PlayerColor::PLAYER_LIMIT; ++color) + { + auto clearButtonImg = createAdventureMapButtonClear(color); + for(int i = 0; i < 4; i++) + { + ImagePath spriteName = ImagePath::builtin(overlay.getOriginalName() + "Btn" + std::to_string(i) + ".png"); + ImagePath spriteNameColor = ImagePath::builtin(overlay.getOriginalName() + "Btn" + std::to_string(i) + "-" + color.toString() + ".png"); + + imageFiles[spriteNameColor] = [this, overlayCanvasImg, clearButtonImg, i](){ + auto newImg = ENGINE->renderHandler().createImage(overlayCanvasImg->dimensions(), CanvasScalingPolicy::IGNORE); + auto canvas = newImg->getCanvas(); + canvas.draw(clearButtonImg, Point(0, 0)); + switch (i) + { + case 0: + canvas.draw(overlayCanvasImg, Point(0, 0)); + return newImg; + case 1: + canvas.draw(clearButtonImg, Point(1, 1)); + canvas.draw(overlayCanvasImg, Point(1, 1)); + canvas.drawLine(Point(0, 0), Point(newImg->width() - 1, 0), ColorRGBA(0, 0, 0), ColorRGBA(0, 0, 0)); + canvas.drawLine(Point(0, 0), Point(0, newImg->height() - 1), ColorRGBA(0, 0, 0), ColorRGBA(0, 0, 0)); + canvas.drawColorBlended(Rect(0, 0, newImg->width(), 4), ColorRGBA(0, 0, 0, 160)); + canvas.drawColorBlended(Rect(0, 0, 4, newImg->height()), ColorRGBA(0, 0, 0, 160)); + return newImg; + case 2: + canvas.drawTransparent(overlayCanvasImg->getCanvas(), Point(0, 0), 0.25); + return newImg; + default: + canvas.draw(overlayCanvasImg, Point(0, 0)); + canvas.drawLine(Point(0, 0), Point(newImg->width() - 1, 0), ColorRGBA(255, 255, 255), ColorRGBA(255, 255, 255)); + canvas.drawLine(Point(newImg->width() - 1, 0), Point(newImg->width() - 1, newImg->height() - 1), ColorRGBA(255, 255, 255), ColorRGBA(255, 255, 255)); + canvas.drawLine(Point(newImg->width() - 1, newImg->height() - 1), Point(0, newImg->height() - 1), ColorRGBA(255, 255, 255), ColorRGBA(255, 255, 255)); + canvas.drawLine(Point(0, newImg->height() - 1), Point(0, 0), ColorRGBA(255, 255, 255), ColorRGBA(255, 255, 255)); + return newImg; + } + }; + + if(color == PlayerColor(0)) + { + layout[0].push_back(ImageLocator(spriteName, EImageBlitMode::SIMPLE)); + imageFiles[spriteName] = imageFiles[spriteNameColor]; + } + } + } + + return layout; } diff --git a/client/render/AssetGenerator.h b/client/render/AssetGenerator.h index 5baf38daa..bb2f500c7 100644 --- a/client/render/AssetGenerator.h +++ b/client/render/AssetGenerator.h @@ -53,6 +53,8 @@ private: CanvasPtr createSpellTabNone() const; CanvasPtr createChroniclesCampaignImages(int chronicle) const; CanvasPtr createPaletteShiftedImage(const AnimationPath & source, const std::vector & animation, int frameIndex, int paletteShiftCounter) const; + CanvasPtr createAdventureMapButtonClear(const PlayerColor & player) const; + AnimationLayoutMap createAdventureMapButton(const ImagePath & overlay); void createPaletteShiftedSprites(); void generatePaletteShiftedAnimation(const AnimationPath & source, const std::vector & animation); diff --git a/client/render/Canvas.cpp b/client/render/Canvas.cpp index 449645ec3..81017deb6 100644 --- a/client/render/Canvas.cpp +++ b/client/render/Canvas.cpp @@ -238,6 +238,13 @@ Rect Canvas::getRenderArea() const return renderArea; } +ColorRGBA Canvas::getPixel(const Point & position) const +{ + SDL_Color color; + SDL_GetRGBA(CSDL_Ext::getPixel(surface, position.x, position.y), surface->format, &color.r, &color.g, &color.b, &color.a); + return ColorRGBA(color.r, color.g, color.b, color.a); +} + CanvasClipRectGuard::CanvasClipRectGuard(Canvas & canvas, const Rect & rect): surf(canvas.surface) { CSDL_Ext::getClipRect(surf, oldRect); diff --git a/client/render/Canvas.h b/client/render/Canvas.h index 1e41e06c7..37d2f0fa4 100644 --- a/client/render/Canvas.h +++ b/client/render/Canvas.h @@ -122,6 +122,9 @@ public: /// get the render area Rect getRenderArea() const; + + /// get pixel color + ColorRGBA getPixel(const Point & position) const; }; class CanvasClipRectGuard : boost::noncopyable diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index b113851aa..7333440ed 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -322,6 +322,8 @@ std::shared_ptr RenderHandler::loadScaledImage(const ImageLocato img = std::make_shared(imagePathData, optimizeImage); else if(CResourceHandler::get()->existsResource(imagePath)) img = std::make_shared(imagePath, optimizeImage); + else if(locator.scalingFactor == 1) + img = std::dynamic_pointer_cast(assetGenerator->generateImage(imagePath)); if(img) { diff --git a/config/widgets/adventureMap.json b/config/widgets/adventureMap.json index 2c6a1ebcc..bc9609602 100644 --- a/config/widgets/adventureMap.json +++ b/config/widgets/adventureMap.json @@ -144,6 +144,22 @@ } ] }, + { + "type": "adventureMapContainer", + "hideWhen" : "mapLayerOther", + "area": { "top" : 0, "left": 32, "width" : 32, "height" : 32 }, + "items" : [ + { + "type": "adventureMapButton", + "name": "worldViewOther", + "image" : "adventureLayersButton", + "help" : "core.help.294", + "hotkey": "adventureToggleMapLevel", + "playerColored" : true, + "area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 } + } + ] + }, { "type": "adventureMapButton", "name": "buttonQuestLog", @@ -544,6 +560,21 @@ } ] }, + { + "type": "adventureMapContainer", + "hideWhen" : "mapLayerOther", + "area": { "top" : 343, "left": 79, "width" : 32, "height" : 32 }, + "items" : [ + { + "type": "adventureMapButton", + "name": "worldViewOther", + "image" : "adventureLayersButton", + "playerColored" : true, + "hotkey": "adventureToggleMapLevel", + "area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 } + } + ] + }, { "type": "adventureMapButton", "name": "worldViewExit",