/* * CAdventureMapWidget.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "StdInc.h" #include "CAdventureMapWidget.h" #include "AdventureMapShortcuts.h" #include "CInfoBar.h" #include "CList.h" #include "CMinimap.h" #include "CResDataBar.h" #include "AdventureState.h" #include "../gui/CGuiHandler.h" #include "../gui/Shortcut.h" #include "../mapView/MapView.h" #include "../render/CAnimation.h" #include "../render/IImage.h" #include "../widgets/Buttons.h" #include "../widgets/Images.h" #include "../widgets/TextControls.h" #include "../CPlayerInterface.h" #include "../PlayerLocalState.h" #include "../../lib/StringConstants.h" #include "../../lib/filesystem/ResourceID.h" CAdventureMapWidget::CAdventureMapWidget( std::shared_ptr shortcuts ) : state(EAdventureState::NOT_INITIALIZED) , shortcuts(shortcuts) , mapLevel(0) { pos.x = pos.y = 0; pos.w = GH.screenDimensions().x; pos.h = GH.screenDimensions().y; REGISTER_BUILDER("adventureInfobar", &CAdventureMapWidget::buildInfobox ); REGISTER_BUILDER("adventureMapImage", &CAdventureMapWidget::buildMapImage ); REGISTER_BUILDER("adventureMapButton", &CAdventureMapWidget::buildMapButton ); REGISTER_BUILDER("adventureMapContainer", &CAdventureMapWidget::buildMapContainer ); REGISTER_BUILDER("adventureMapGameArea", &CAdventureMapWidget::buildMapGameArea ); REGISTER_BUILDER("adventureMapHeroList", &CAdventureMapWidget::buildMapHeroList ); REGISTER_BUILDER("adventureMapIcon", &CAdventureMapWidget::buildMapIcon ); REGISTER_BUILDER("adventureMapTownList", &CAdventureMapWidget::buildMapTownList ); REGISTER_BUILDER("adventureMinimap", &CAdventureMapWidget::buildMinimap ); REGISTER_BUILDER("adventureResourceDateBar", &CAdventureMapWidget::buildResourceDateBar ); REGISTER_BUILDER("adventureStatusBar", &CAdventureMapWidget::buildStatusBar ); for (const auto & entry : shortcuts->getShortcuts()) addShortcut(entry.shortcut, entry.callback); const JsonNode config(ResourceID("config/widgets/adventureMap.json")); for(const auto & entry : config["options"]["imagesPlayerColored"].Vector()) { ResourceID resourceName(entry.String(), EResType::IMAGE); playerColorerImages.push_back(resourceName.getName()); } build(config); addUsedEvents(KEYBOARD); } void CAdventureMapWidget::onMapViewMoved(const Rect & visibleArea, int newMapLevel) { if(mapLevel == newMapLevel) return; mapLevel = newMapLevel; updateActiveState(); } Rect CAdventureMapWidget::readSourceArea(const JsonNode & source, const JsonNode & sourceCommon) { const auto & input = source.isNull() ? sourceCommon : source; return readArea(input, Rect(Point(0, 0), Point(800, 600))); } Rect CAdventureMapWidget::readTargetArea(const JsonNode & source) { if(subwidgetSizes.empty()) return readArea(source, pos); return readArea(source, subwidgetSizes.back()); } Rect CAdventureMapWidget::readArea(const JsonNode & source, const Rect & boundingBox) { const auto & object = source.Struct(); if(object.count("left") + object.count("width") + object.count("right") != 2) logGlobal->error("Invalid area definition in widget! Unable to load width!"); if(object.count("top") + object.count("height") + object.count("bottom") != 2) logGlobal->error("Invalid area definition in widget! Unable to load height!"); int left = source["left"].Integer(); int width = source["width"].Integer(); int right = source["right"].Integer(); int top = source["top"].Integer(); int height = source["height"].Integer(); int bottom = source["bottom"].Integer(); Point topLeft(left, top); Point dimensions(width, height); if(source["left"].isNull()) topLeft.x = boundingBox.w - right - width; if(source["width"].isNull()) dimensions.x = boundingBox.w - right - left; if(source["top"].isNull()) topLeft.y = boundingBox.h - bottom - height; if(source["height"].isNull()) dimensions.y = boundingBox.h - bottom - top; return Rect(topLeft + boundingBox.topLeft(), dimensions); } std::shared_ptr CAdventureMapWidget::loadImage(const std::string & name) { ResourceID resource(name, EResType::IMAGE); if(images.count(resource.getName()) == 0) images[resource.getName()] = IImage::createFromFile(resource.getName()); ; return images[resource.getName()]; } std::shared_ptr CAdventureMapWidget::loadAnimation(const std::string & name) { ResourceID resource(name, EResType::ANIMATION); if(animations.count(resource.getName()) == 0) animations[resource.getName()] = std::make_shared(resource.getName()); return animations[resource.getName()]; } std::shared_ptr CAdventureMapWidget::buildInfobox(const JsonNode & input) { Rect area = readTargetArea(input["area"]); infoBar = std::make_shared(area); return infoBar; } std::shared_ptr CAdventureMapWidget::buildMapImage(const JsonNode & input) { Rect targetArea = readTargetArea(input["area"]); Rect sourceArea = readSourceArea(input["sourceArea"], input["area"]); std::string image = input["image"].String(); return std::make_shared(loadImage(image), targetArea, sourceArea); } std::shared_ptr CAdventureMapWidget::buildMapButton(const JsonNode & input) { auto position = readTargetArea(input["area"]); auto image = input["image"].String(); auto help = readHintText(input["help"]); bool playerColored = input["playerColored"].Bool(); auto button = std::make_shared(position.topLeft(), image, help, 0, EShortcut::NONE, playerColored); loadButtonHotkey(button, input["hotkey"]); return button; } std::shared_ptr CAdventureMapWidget::buildMapContainer(const JsonNode & input) { auto position = readTargetArea(input["area"]); std::shared_ptr result; if (!input["exists"].isNull()) { if (!input["exists"]["heightMin"].isNull() && input["exists"]["heightMin"].Integer() >= pos.h) return nullptr; if (!input["exists"]["heightMax"].isNull() && input["exists"]["heightMax"].Integer() < pos.h) return nullptr; if (!input["exists"]["widthMin"].isNull() && input["exists"]["widthMin"].Integer() >= pos.w) return nullptr; if (!input["exists"]["widthMax"].isNull() && input["exists"]["widthMax"].Integer() < pos.w) return nullptr; } if (input["overlay"].Bool()) result = std::make_shared(); else result = std::make_shared(); result->disableCondition = input["hideWhen"].String(); result->moveBy(position.topLeft()); subwidgetSizes.push_back(position); for(const auto & entry : input["items"].Vector()) { auto widget = buildWidget(entry); addWidget(entry["name"].String(), widget); result->ownedChildren.push_back(widget); result->addChild(widget.get(), false); } subwidgetSizes.pop_back(); return result; } std::shared_ptr CAdventureMapWidget::buildMapGameArea(const JsonNode & input) { Rect area = readTargetArea(input["area"]); mapView = std::make_shared(area.topLeft(), area.dimensions()); return mapView; } std::shared_ptr CAdventureMapWidget::buildMapHeroList(const JsonNode & input) { Rect area = readTargetArea(input["area"]); subwidgetSizes.push_back(area); Rect item = readTargetArea(input["item"]); Point itemOffset(input["itemsOffset"]["x"].Integer(), input["itemsOffset"]["y"].Integer()); int itemsCount = input["itemsCount"].Integer(); auto result = std::make_shared(itemsCount, item.topLeft(), itemOffset, LOCPLINT->localState->getWanderingHeroes().size()); if(!input["scrollUp"].isNull()) result->setScrollUpButton(std::dynamic_pointer_cast(buildMapButton(input["scrollUp"]))); if(!input["scrollDown"].isNull()) result->setScrollDownButton(std::dynamic_pointer_cast(buildMapButton(input["scrollDown"]))); subwidgetSizes.pop_back(); heroList = result; return result; } std::shared_ptr CAdventureMapWidget::buildMapIcon(const JsonNode & input) { Rect area = readTargetArea(input["area"]); size_t index = input["index"].Integer(); size_t perPlayer = input["perPlayer"].Integer(); std::string image = input["image"].String(); return std::make_shared(area.topLeft(), loadAnimation(image), index, perPlayer); } std::shared_ptr CAdventureMapWidget::buildMapTownList(const JsonNode & input) { Rect area = readTargetArea(input["area"]); subwidgetSizes.push_back(area); Rect item = readTargetArea(input["item"]); Point itemOffset(input["itemsOffset"]["x"].Integer(), input["itemsOffset"]["y"].Integer()); int itemsCount = input["itemsCount"].Integer(); auto result = std::make_shared(itemsCount, item.topLeft(), itemOffset, LOCPLINT->localState->getOwnedTowns().size()); if(!input["scrollUp"].isNull()) result->setScrollUpButton(std::dynamic_pointer_cast(buildMapButton(input["scrollUp"]))); if(!input["scrollDown"].isNull()) result->setScrollDownButton(std::dynamic_pointer_cast(buildMapButton(input["scrollDown"]))); subwidgetSizes.pop_back(); townList = result; return result; } std::shared_ptr CAdventureMapWidget::buildMinimap(const JsonNode & input) { Rect area = readTargetArea(input["area"]); minimap = std::make_shared(area); return minimap; } std::shared_ptr CAdventureMapWidget::buildResourceDateBar(const JsonNode & input) { Rect area = readTargetArea(input["area"]); std::string image = input["image"].String(); auto result = std::make_shared(image, area.topLeft()); for(auto i = 0; i < GameConstants::RESOURCE_QUANTITY; i++) { const auto & node = input[GameConstants::RESOURCE_NAMES[i]]; if(node.isNull()) continue; result->setResourcePosition(GameResID(i), Point(node["x"].Integer(), node["y"].Integer())); } result->setDatePosition(Point(input["date"]["x"].Integer(), input["date"]["y"].Integer())); return result; } std::shared_ptr CAdventureMapWidget::buildStatusBar(const JsonNode & input) { Rect area = readTargetArea(input["area"]); std::string image = input["image"].String(); auto background = std::make_shared(image, area); return CGStatusBar::create(background); } std::shared_ptr CAdventureMapWidget::getHeroList() { return heroList; } std::shared_ptr CAdventureMapWidget::getTownList() { return townList; } std::shared_ptr CAdventureMapWidget::getMinimap() { return minimap; } std::shared_ptr CAdventureMapWidget::getMapView() { return mapView; } std::shared_ptr CAdventureMapWidget::getInfoBar() { return infoBar; } void CAdventureMapWidget::setPlayer(const PlayerColor & player) { setPlayerChildren(this, player); } void CAdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColor & player) { for(auto & entry : widget->children) { auto container = dynamic_cast(entry); auto icon = dynamic_cast(entry); auto button = dynamic_cast(entry); if(button) button->setPlayerColor(player); if(icon) icon->setPlayer(player); if(container) setPlayerChildren(container, player); } for(const auto & entry : playerColorerImages) { if(images.count(entry)) images[entry]->playerColored(player); } redraw(); } void CAdventureMapWidget::setState(EAdventureState newState) { state = newState; if(newState == EAdventureState::WORLD_VIEW) widget("worldViewContainer")->enable(); else widget("worldViewContainer")->disable(); } EAdventureState CAdventureMapWidget::getState() { return state; } CAdventureMapIcon::CAdventureMapIcon(const Point & position, std::shared_ptr animation, size_t index, size_t iconsPerPlayer) : index(index) , iconsPerPlayer(iconsPerPlayer) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; pos += position; image = std::make_shared(animation, index); } void CAdventureMapIcon::setPlayer(const PlayerColor & player) { image->setFrame(index + player.getNum() * iconsPerPlayer); } void CAdventureMapOverlayWidget::show(SDL_Surface * to) { CIntObject::showAll(to); } void CAdventureMapWidget::updateActiveStateChildden(CIntObject * widget) { for(auto & entry : widget->children) { auto container = dynamic_cast(entry); if (container) { if (container->disableCondition == "heroAwake") container->setEnabled(!shortcuts->optionHeroSleeping()); if (container->disableCondition == "heroSleeping") container->setEnabled(shortcuts->optionHeroSleeping()); if (container->disableCondition == "mapLayerSurface") container->setEnabled(shortcuts->optionMapLevelSurface()); if (container->disableCondition == "mapLayerUnderground") container->setEnabled(!shortcuts->optionMapLevelSurface()); updateActiveStateChildden(container); } } } void CAdventureMapWidget::updateActiveState() { updateActiveStateChildden(this); for (auto entry: shortcuts->getShortcuts()) setShortcutBlocked(entry.shortcut, !entry.isEnabled); }