1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Merge branch 'develop' into AIMapObjectEvaluation

This commit is contained in:
Dydzio 2018-07-30 01:33:49 +02:00 committed by GitHub
commit 2288e9b8aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 2702 additions and 1750 deletions

View File

@ -126,6 +126,11 @@ const CGHeroInstance * HeroPtr::operator*() const
return get();
}
bool HeroPtr::operator==(const HeroPtr & rhs) const
{
return h == rhs.get(true);
}
void foreach_tile_pos(std::function<void(const int3 & pos)> foo)
{
// some micro-optimizations since this function gets called a LOT
@ -475,6 +480,50 @@ void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & o
}
}
creInfo infoFromDC(const dwellingContent & dc)
{
creInfo ci;
ci.count = dc.first;
ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed
if (ci.creID != -1)
{
ci.cre = VLC->creh->creatures[ci.creID];
ci.level = ci.cre->level; //this is cretaure tier, while tryRealize expects dwelling level. Ignore.
}
else
{
ci.cre = nullptr;
ci.level = 0;
}
return ci;
}
ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGTownInstance * t)
{
ui64 aivalue = 0;
int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
for(auto const dc : t->creatures)
{
creInfo ci = infoFromDC(dc);
if(ci.count && ci.creID != -1) //valid creature at this level
{
//can be merged with another stack?
SlotID dst = h->getSlotFor(ci.creID);
if (!h->hasStackAtSlot(dst)) //need another new slot for this stack
if (!freeHeroSlots) //no more place for stacks
continue;
else
freeHeroSlots--; //new slot will be occupied
//we found matching occupied or free slot
aivalue += ci.count * ci.cre->AIValue;
}
}
return aivalue;
}
ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t)
{
ui64 ret = 0;

View File

@ -20,9 +20,11 @@
#include "../../lib/CPathfinder.h"
class CCallback;
struct creInfo;
typedef const int3 & crint3;
typedef const std::string & crstring;
typedef std::pair<ui32, std::vector<CreatureID>> dwellingContent;
const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1;
const int ACTUAL_RESOURCE_COUNT = 7;
@ -35,7 +37,7 @@ extern const int GOLD_RESERVE;
//provisional class for AI to store a reference to an owned hero object
//checks if it's valid on access, should be used in place of const CGHeroInstance*
struct HeroPtr
struct DLL_EXPORT HeroPtr
{
const CGHeroInstance * h;
ObjectInstanceID hid;
@ -56,6 +58,7 @@ public:
bool operator<(const HeroPtr & rhs) const;
const CGHeroInstance * operator->() const;
const CGHeroInstance * operator*() const; //not that consistent with -> but all interfaces use CGHeroInstance*, so it's convenient
bool operator==(const HeroPtr & rhs) const;
const CGHeroInstance * get(bool doWeExpectNull = false) const;
bool validAndSet() const;
@ -137,6 +140,15 @@ bool objWithID(const CGObjectInstance * obj)
return obj->ID == id;
}
struct creInfo
{
int count;
CreatureID creID;
CCreature * cre;
int level;
};
creInfo infoFromDC(const dwellingContent & dc);
void foreach_tile_pos(std::function<void(const int3 & pos)> foo);
void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3 & pos)> foo); // avoid costly retrieval of thread-specific pointer
void foreach_neighbour(const int3 & pos, std::function<void(const int3 & pos)> foo);
@ -160,6 +172,7 @@ bool compareMovement(HeroPtr lhs, HeroPtr rhs);
bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
ui64 howManyReinforcementsCanBuy(HeroPtr h, const CGTownInstance * t);
ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance * t);
int3 whereToExplore(HeroPtr h);

89
AI/VCAI/AIhelper.cpp Normal file
View File

@ -0,0 +1,89 @@
/*
* AIhelper.h, 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 "AIhelper.h"
#include "ResourceManager.h"
boost::thread_specific_ptr<AIhelper> ah;
AIhelper::AIhelper()
{
resourceManager.reset(new ResourceManager());
}
AIhelper::~AIhelper()
{
}
bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal)
{
return resourceManager->notifyGoalCompleted(goal);
}
void AIhelper::setCB(CPlayerSpecificInfoCallback * CB)
{
resourceManager->setCB(CB);
}
void AIhelper::setAI(VCAI * AI)
{
resourceManager->setAI(AI);
}
Goals::TSubgoal AIhelper::whatToDo(TResources & res, Goals::TSubgoal goal)
{
return resourceManager->whatToDo(res, goal);
}
Goals::TSubgoal AIhelper::whatToDo() const
{
return resourceManager->whatToDo();
}
bool AIhelper::containsObjective(Goals::TSubgoal goal) const
{
return resourceManager->containsObjective(goal);
}
bool AIhelper::hasTasksLeft() const
{
return resourceManager->hasTasksLeft();
}
bool AIhelper::canAfford(const TResources & cost) const
{
return resourceManager->canAfford(cost);
}
TResources AIhelper::reservedResources() const
{
return resourceManager->reservedResources();
}
TResources AIhelper::freeResources() const
{
return resourceManager->freeResources();
}
TResource AIhelper::freeGold() const
{
return resourceManager->freeGold();
}
TResources AIhelper::allResources() const
{
return resourceManager->allResources();
}
TResource AIhelper::allGold() const
{
return resourceManager->allGold();
}

53
AI/VCAI/AIhelper.h Normal file
View File

@ -0,0 +1,53 @@
/*
* AIhelper.h, 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
*
*/
#pragma once
/*
!!! Note: Include THIS file at the end of include list to avoid "undefined base class" error
*/
#include "ResourceManager.h"
class ResourceManager;
//indirection interface for various modules
class DLL_EXPORT AIhelper : public IResourceManager
{
friend class VCAI;
friend struct SetGlobalState; //mess?
//members are thread_specific. AIhelper is global
std::shared_ptr<ResourceManager> resourceManager;
std::shared_ptr<VCAI> ai;
public:
AIhelper();
~AIhelper();
//TODO: consider common interface with Resource Manager?
bool canAfford(const TResources & cost) const;
TResources reservedResources() const override;
TResources freeResources() const override;
TResource freeGold() const override;
TResources allResources() const override;
TResource allGold() const override;
Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) override;
Goals::TSubgoal whatToDo() const override; //peek highest-priority goal
bool containsObjective(Goals::TSubgoal goal) const;
bool hasTasksLeft() const override;
private:
bool notifyGoalCompleted(Goals::TSubgoal goal);
void setCB(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
};

View File

@ -9,6 +9,8 @@ set(VCAI_SRCS
StdInc.cpp
AIUtility.cpp
AIhelper.cpp
ResourceManager.cpp
Fuzzy.cpp
Goals.cpp
main.cpp
@ -19,6 +21,8 @@ set(VCAI_HEADERS
StdInc.h
AIUtility.h
AIhelper.h
ResourceManager.h
Fuzzy.h
Goals.h
VCAI.h

View File

@ -371,7 +371,7 @@ float FuzzyHelper::evaluate(Goals::Explore & g)
}
float FuzzyHelper::evaluate(Goals::RecruitHero & g)
{
return 1; //just try to recruit hero as one of options
return 1;
}
FuzzyHelper::EvalVisitTile::~EvalVisitTile()
{
@ -603,7 +603,7 @@ float FuzzyHelper::evaluate(Goals::ClearWayTo & g)
float FuzzyHelper::evaluate(Goals::BuildThis & g)
{
return 1;
return g.priority; //TODO
}
float FuzzyHelper::evaluate(Goals::DigAtTile & g)
{
@ -611,12 +611,16 @@ float FuzzyHelper::evaluate(Goals::DigAtTile & g)
}
float FuzzyHelper::evaluate(Goals::CollectRes & g)
{
return 0;
return g.priority; //handled by ResourceManager
}
float FuzzyHelper::evaluate(Goals::Build & g)
{
return 0;
}
float FuzzyHelper::evaluate(Goals::BuyArmy & g)
{
return g.priority;
}
float FuzzyHelper::evaluate(Goals::Invalid & g)
{
return -1e10;
@ -626,7 +630,7 @@ float FuzzyHelper::evaluate(Goals::AbstractGoal & g)
logAi->warn("Cannot evaluate goal %s", g.name());
return g.priority;
}
void FuzzyHelper::setPriority(Goals::TSubgoal & g)
void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pattern
{
g->setpriority(g->accept(this)); //this enforces returned value is set
}

View File

@ -84,6 +84,7 @@ public:
float evaluate(Goals::DigAtTile & g);
float evaluate(Goals::CollectRes & g);
float evaluate(Goals::Build & g);
float evaluate(Goals::BuyArmy & g);
float evaluate(Goals::GatherArmy & g);
float evaluate(Goals::ClearWayTo & g);
float evaluate(Goals::Invalid & g);

View File

@ -11,18 +11,23 @@
#include "Goals.h"
#include "VCAI.h"
#include "Fuzzy.h"
#include "ResourceManager.h"
#include "../../lib/mapping/CMap.h" //for victory conditions
#include "../../lib/CPathfinder.h"
#include "../../lib/StringConstants.h"
#include "AIhelper.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh; //TODO: this logic should be moved inside VCAI
extern boost::thread_specific_ptr<AIhelper> ah;
extern FuzzyHelper * fh;
using namespace Goals;
TSubgoal Goals::sptr(const AbstractGoal & tmp)
{
std::shared_ptr<AbstractGoal> ptr;
TSubgoal ptr;
ptr.reset(tmp.clone());
return ptr;
}
@ -48,6 +53,9 @@ std::string Goals::AbstractGoal::name() const //TODO: virtualize
case GATHER_ARMY:
desc = "GATHER ARMY";
break;
case BUY_ARMY:
return "BUY ARMY";
break;
case BOOST_HERO:
desc = "BOOST_HERO (unsupported)";
break;
@ -56,7 +64,7 @@ std::string Goals::AbstractGoal::name() const //TODO: virtualize
case BUILD_STRUCTURE:
return "BUILD STRUCTURE";
case COLLECT_RES:
desc = "COLLECT RESOURCE";
desc = "COLLECT RESOURCE " + GameConstants::RESOURCE_NAMES[resID] + " (" + boost::lexical_cast<std::string>(value) + ")";
break;
case GATHER_TROOPS:
desc = "GATHER TROOPS";
@ -114,21 +122,25 @@ bool Goals::AbstractGoal::operator==(AbstractGoal & g)
case INVALID:
case WIN:
case DO_NOT_LOSE:
case RECRUIT_HERO: //recruit any hero, as yet
case RECRUIT_HERO: //overloaded
return true;
break;
//assigned to hero, no parameters
case CONQUER:
case EXPLORE:
case GATHER_ARMY: //actual value is indifferent
case BOOST_HERO:
return g.hero.h == hero.h; //how comes HeroPtrs are equal for different heroes?
break;
case GATHER_ARMY: //actual value is indifferent
return (g.hero.h == hero.h || town == g.town); //TODO: gather army for town maybe?
break;
//assigned hero and tile
case VISIT_TILE:
case CLEAR_WAY_TO:
case DIG_AT_TILE:
return (g.hero.h == hero.h && g.tile == tile);
break;
@ -137,16 +149,20 @@ bool Goals::AbstractGoal::operator==(AbstractGoal & g)
case FIND_OBJ: //TODO: use subtype?
case VISIT_HERO:
case GET_ART_TYPE:
case DIG_AT_TILE:
return (g.hero.h == hero.h && g.objid == objid);
break;
case BUILD_STRUCTURE:
return (town == g.town && bid == g.bid); //build specific structure in specific town
break;
//no check atm
case COLLECT_RES:
return (resID == g.resID); //every hero may collect resources
break;
case GATHER_TROOPS:
case ISSUE_COMMAND:
case BUILD: //TODO: should be decomposed to build specific structures
case BUILD_STRUCTURE:
default:
return false;
}
@ -156,28 +172,56 @@ bool Goals::AbstractGoal::operator==(AbstractGoal & g)
namespace Goals
{
template<>
void CGoal<Win>::accept(VCAI * ai)
{
ai->tryRealize(static_cast<Win &>(*this));
}
template<>
void CGoal<Win>::accept(VCAI * ai)
{
ai->tryRealize(static_cast<Win &>(*this));
}
template<>
void CGoal<Build>::accept(VCAI * ai)
{
ai->tryRealize(static_cast<Build &>(*this));
}
template<>
float CGoal<Win>::accept(FuzzyHelper * f)
{
return f->evaluate(static_cast<Win &>(*this));
}
template<>
void CGoal<Build>::accept(VCAI * ai)
{
ai->tryRealize(static_cast<Build &>(*this));
}
template<>
float CGoal<Win>::accept(FuzzyHelper * f)
{
return f->evaluate(static_cast<Win &>(*this));
}
template<>
float CGoal<Build>::accept(FuzzyHelper * f)
{
return f->evaluate(static_cast<Build &>(*this));
}
template<>
float CGoal<Build>::accept(FuzzyHelper * f)
{
return f->evaluate(static_cast<Build &>(*this));
}
bool TSubgoal::operator==(const TSubgoal & rhs) const
{
return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match
}
bool BuyArmy::operator==(BuyArmy & g)
{
//if (hero && hero != g.hero)
// return false;
return town == g.town;
}
bool BuyArmy::fulfillsMe(TSubgoal goal)
{
//if (hero && hero != goal->hero)
// return false;
return town == goal->town && goal->value >= value; //can always buy more army
}
TSubgoal BuyArmy::whatToDoToAchieve()
{
//TODO: calculate the actual cost of units instead
TResources price;
price[Res::GOLD] = value * 0.4f; //some approximate value
return ah->whatToDo(price, iAmElementar()); //buy right now or gather resources
}
std::string BuyArmy::completeMessage() const
{
return boost::format("Bought army of value %d in town of %s") % value, town->name;
}
}
//TSubgoal AbstractGoal::whatToDoToAchieve()
@ -251,7 +295,7 @@ TSubgoal Win::whatToDoToAchieve()
if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
{
const CGTownInstance * t = h->visitedTown;
return sptr(Goals::BuildThis(BuildingID::GRAIL, t));
return sptr(Goals::BuildThis(BuildingID::GRAIL, t).setpriority(10));
}
else
{
@ -371,6 +415,19 @@ TSubgoal FindObj::whatToDoToAchieve()
return sptr(Goals::Explore());
}
bool Goals::FindObj::fulfillsMe(TSubgoal goal)
{
if (goal->goalType == Goals::VISIT_TILE) //visiting tile visits object at same time
{
if (!hero || hero == goal->hero)
for (auto obj : cb->getVisitableObjs(goal->tile)) //check if any object on that tile matches criteria
if (obj->visitablePos() == goal->tile) //object could be removed
if (obj->ID == objid && obj->subID == resID) //same type and subtype
return true;
}
return false;
}
std::string GetObj::completeMessage() const
{
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
@ -403,13 +460,15 @@ TSubgoal GetObj::whatToDoToAchieve()
bool GetObj::fulfillsMe(TSubgoal goal)
{
if(goal->goalType == Goals::VISIT_TILE)
if(goal->goalType == Goals::VISIT_TILE) //visiting tile visits object at same time
{
auto obj = cb->getObj(ObjectInstanceID(objid));
if(obj && obj->visitablePos() == goal->tile) //object could be removed
return true;
if (!hero || hero == goal->hero)
{
auto obj = cb->getObj(ObjectInstanceID(objid));
if (obj && obj->visitablePos() == goal->tile) //object could be removed
return true;
}
}
return false;
}
@ -441,17 +500,18 @@ TSubgoal VisitHero::whatToDoToAchieve()
bool VisitHero::fulfillsMe(TSubgoal goal)
{
if(goal->goalType != Goals::VISIT_TILE)
//TODO: VisitObj shoudl not be used for heroes, but...
if(goal->goalType == Goals::VISIT_TILE)
{
return false;
auto obj = cb->getObj(ObjectInstanceID(objid));
if (!obj)
{
logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile.toString(), objid);
return false;
}
return obj->visitablePos() == goal->tile;
}
auto obj = cb->getObj(ObjectInstanceID(objid));
if(!obj)
{
logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile.toString(), objid);
return false;
}
return obj->visitablePos() == goal->tile;
return false;
}
TSubgoal GetArtOfType::whatToDoToAchieve()
@ -474,6 +534,16 @@ TSubgoal ClearWayTo::whatToDoToAchieve()
return (fh->chooseSolution(getAllPossibleSubgoals()));
}
bool Goals::ClearWayTo::fulfillsMe(TSubgoal goal)
{
if (goal->goalType == Goals::VISIT_TILE)
{
if (!hero || hero == goal->hero)
return tile == goal->tile;
}
return false;
}
TGoalVec ClearWayTo::getAllPossibleSubgoals()
{
TGoalVec ret;
@ -648,6 +718,7 @@ TGoalVec Explore::getAllPossibleSubgoals()
}
}
auto primaryHero = ai->primaryHero().h;
for(auto h : heroes)
{
auto sm = ai->getCachedSectorMap(h);
@ -666,15 +737,18 @@ TGoalVec Explore::getAllPossibleSubgoals()
}
else
{
ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore
//possible issues when gathering army to break
if(hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost
//FIXME: possible issues when gathering army to break
if(hero.h == h || //exporation is assigned to this hero
(!hero && h == primaryHero)) //not assigned to specific hero, let our main hero do the job
{
t = ai->explorationDesperate(h);
if(t.valid()) //don't waste time if we are completely blocked
t = ai->explorationDesperate(h); //check this only ONCE, high cost
if (t.valid()) //don't waste time if we are completely blocked
{
ret.push_back(sptr(Goals::ClearWayTo(t, h).setisAbstract(true)));
continue;
}
}
ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore
}
}
//we either don't have hero yet or none of heroes can explore
@ -702,17 +776,21 @@ bool Explore::fulfillsMe(TSubgoal goal)
return false;
}
TSubgoal RecruitHero::whatToDoToAchieve()
{
const CGTownInstance * t = ai->findTownWithTavern();
if(!t)
return sptr(Goals::BuildThis(BuildingID::TAVERN));
return sptr(Goals::BuildThis(BuildingID::TAVERN).setpriority(2));
if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
return sptr(Goals::CollectRes(Res::GOLD, GameConstants::HERO_GOLD_COST));
TResources res;
res[Res::GOLD] = GameConstants::HERO_GOLD_COST;
return ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res
}
return iAmElementar();
bool Goals::RecruitHero::operator==(RecruitHero & g)
{
//TODO: check town and hero
return true; //for now, recruiting any hero will do
}
std::string VisitTile::completeMessage() const
@ -798,25 +876,156 @@ TSubgoal DigAtTile::whatToDoToAchieve()
TSubgoal BuildThis::whatToDoToAchieve()
{
//TODO check res
//look for town
//prerequisites?
return iAmElementar();
auto b = BuildingID(bid);
// find town if not set
if (!town && hero)
town = hero->visitedTown;
if (!town)
{
for (const CGTownInstance * t : cb->getTownsInfo())
{
switch (cb->canBuildStructure(town, b))
{
case EBuildingState::ALLOWED:
town = t;
break; //TODO: look for prerequisites? this is not our reponsibility
default:
continue;
}
}
}
if (town) //we have specific town to build this
{
auto res = town->town->buildings.at(BuildingID(bid))->resources;
return ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
}
else
throw cannotFulfillGoalException("Cannot find town to build this");
}
TGoalVec Goals::CollectRes::getAllPossibleSubgoals()
{
TGoalVec ret;
auto givesResource = [this](const CGObjectInstance * obj) -> bool
{
//TODO: move this logic to object side
//TODO: remember mithril exists
//TODO: water objects
//TODO: Creature banks
//return false first from once-visitable, before checking if they were even visited
switch (obj->ID.num)
{
case Obj::TREASURE_CHEST:
return resID == Res::GOLD;
break;
case Obj::RESOURCE:
return obj->subID == resID;
break;
case Obj::MINE:
return (obj->subID == resID &&
(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
break;
case Obj::CAMPFIRE:
return true; //contains all resources
break;
case Obj::WINDMILL:
switch (resID)
{
case Res::GOLD:
case Res::WOOD:
return false;
}
break;
case Obj::WATER_WHEEL:
if (resID != Res::GOLD)
return false;
break;
case Obj::MYSTICAL_GARDEN:
if ((resID != Res::GOLD) && (resID != Res::GEMS))
return false;
break;
case Obj::LEAN_TO:
case Obj::WAGON:
if (resID != Res::GOLD)
return false;
break;
default:
return false;
break;
}
return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
};
std::vector<const CGObjectInstance *> objs;
for (auto obj : ai->visitableObjs)
{
if (givesResource(obj))
objs.push_back(obj);
}
for (auto h : cb->getHeroesInfo())
{
auto sm = ai->getCachedSectorMap(h);
std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
{
if (givesResource(obj))
ourObjs.push_back(obj);
}
for (auto obj : ourObjs)
{
int3 dest = obj->visitablePos();
auto t = sm->firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded
if (t.valid()) //we know any path at all
{
if (ai->isTileNotReserved(h, t)) //no other hero wants to conquer that tile
{
if (isSafeToVisit(h, dest))
{
if (dest != t) //there is something blocking our way
ret.push_back(sptr(Goals::ClearWayTo(dest, h).setisAbstract(true)));
else
ret.push_back(sptr(Goals::VisitTile(dest).sethero(h).setisAbstract(true)));
}
else //we need to get army in order to pick that object
ret.push_back(sptr(Goals::GatherArmy(evaluateDanger(dest, h) * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true)));
}
}
}
}
return ret;
}
TSubgoal CollectRes::whatToDoToAchieve()
{
auto goals = getAllPossibleSubgoals();
auto trade = whatToDoToTrade();
if (!trade->invalid())
goals.push_back(trade);
if (goals.empty())
return sptr(Goals::Explore()); //we can always do that
else
return fh->chooseSolution(goals); //TODO: evaluate trading
}
TSubgoal Goals::CollectRes::whatToDoToTrade()
{
std::vector<const IMarket *> markets;
std::vector<const CGObjectInstance *> visObjs;
ai->retrieveVisitableObjs(visObjs, true);
for(const CGObjectInstance * obj : visObjs)
for (const CGObjectInstance * obj : visObjs)
{
if(const IMarket * m = IMarket::castFrom(obj, false))
if (const IMarket * m = IMarket::castFrom(obj, false))
{
if(obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
markets.push_back(m);
else if(obj->ID == Obj::TRADING_POST) //TODO a moze po prostu test na pozwalanie handlu?
else if (obj->ID == Obj::TRADING_POST)
markets.push_back(m);
}
}
@ -828,20 +1037,20 @@ TSubgoal CollectRes::whatToDoToAchieve()
markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
{
if(!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
{
if(!ai->isAccessible(market->o->visitablePos()))
if (!ai->isAccessible(market->o->visitablePos()))
return true;
}
return false;
}), markets.end());
if(!markets.size())
if (!markets.size())
{
for(const CGTownInstance * t : cb->getTownsInfo())
for (const CGTownInstance * t : cb->getTownsInfo())
{
if(cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
return sptr(Goals::BuildThis(BuildingID::MARKETPLACE, t));
if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
return sptr(Goals::BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
}
}
else
@ -849,9 +1058,9 @@ TSubgoal CollectRes::whatToDoToAchieve()
const IMarket * m = markets.back();
//attempt trade at back (best prices)
int howManyCanWeBuy = 0;
for(Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
{
if(i == resID)
if (i == resID)
continue;
int toGive = -1, toReceive = -1;
m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
@ -859,21 +1068,33 @@ TSubgoal CollectRes::whatToDoToAchieve()
howManyCanWeBuy += toReceive * (cb->getResourceAmount(i) / toGive);
}
if(howManyCanWeBuy + cb->getResourceAmount(static_cast<Res::ERes>(resID)) >= value)
if (howManyCanWeBuy >= value)
{
auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
assert(backObj);
if(backObj->tempOwner != ai->playerID)
auto objid = m->o->id.getNum();
if (backObj->tempOwner != ai->playerID) //top object not owned
{
return sptr(Goals::GetObj(m->o->id.getNum()));
return sptr(Goals::GetObj(objid)); //just go there
}
else
else //either it's our town, or we have hero there
{
return sptr(Goals::GetObj(m->o->id.getNum()).setisElementar(true));
return sptr(setobjid(objid).setisElementar(true));
}
}
}
return sptr(setisElementar(true)); //all the conditions for trade are met
return sptr(Goals::Invalid()); //cannot trade
//TODO: separate goal to execute trade?
//return sptr(setisElementar(true)); //not sure why we are here
}
bool CollectRes::fulfillsMe(TSubgoal goal)
{
if (goal->resID == resID)
if (goal->value >= value)
return true;
return false;
}
TSubgoal GatherTroops::whatToDoToAchieve()
@ -899,7 +1120,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
}
else
{
return sptr(Goals::BuildThis(bid, t));
return sptr(Goals::BuildThis(bid, t).setpriority(priority));
}
}
}
@ -915,7 +1136,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
{
for(auto type : creature.second)
{
if(type == objid && ai->freeResources().canAfford(VLC->creh->creatures[type]->cost))
if(type == objid && ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
dwellings.push_back(d);
}
}
@ -962,6 +1183,15 @@ TSubgoal GatherTroops::whatToDoToAchieve()
//TODO: exchange troops between heroes
}
bool Goals::GatherTroops::fulfillsMe(TSubgoal goal)
{
if (!hero || hero == goal->hero) //we got army for desired hero or any hero
if (goal->objid == objid) //same creature type //TODO: consider upgrades?
if (goal->value >= value) //notify every time we get resources?
return true;
return false;
}
TSubgoal Conquer::whatToDoToAchieve()
{
return fh->chooseSolution(getAllPossibleSubgoals());
@ -1050,6 +1280,14 @@ TSubgoal Build::whatToDoToAchieve()
return iAmElementar();
}
bool Goals::Build::fulfillsMe(TSubgoal goal)
{
if (goal->goalType == Goals::BUILD || goal->goalType == Goals::BUILD_STRUCTURE)
return (!town || town == goal->town); //building anything will do, in this town if set
else
return false;
}
TSubgoal Invalid::whatToDoToAchieve()
{
return iAmElementar();
@ -1082,14 +1320,31 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
auto pos = t->visitablePos();
if(ai->isAccessibleForHero(pos, hero))
{
//grab army from town
if(!t->visitingHero && howManyReinforcementsCanGet(hero, t))
{
if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
ret.push_back(sptr(Goals::VisitTile(pos).sethero(hero)));
}
//buy army in town
if (!t->visitingHero || t->visitingHero != hero.get(true))
{
ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero, t));
if (val)
{
auto goal = sptr(Goals::BuyArmy(t, val).sethero(hero));
if (!ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
ret.push_back(goal);
}
}
//build dwelling
auto bid = ai->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
if(bid != BuildingID::NONE)
ret.push_back(sptr(BuildThis(bid, t)));
if (bid != BuildingID::NONE)
{
auto goal = sptr(BuildThis(bid, t).setpriority(priority));
if (!ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
ret.push_back(goal);
}
}
}
@ -1134,7 +1389,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
for(auto & creatureID : creLevel.second)
{
auto creature = VLC->creh->creatures[creatureID];
if(ai->freeResources().canAfford(creature->cost))
if(ah->freeResources().canAfford(creature->cost))
objs.push_back(obj);
}
}
@ -1154,7 +1409,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
}
}
if(ai->canRecruitAnyHero() && ai->freeResources()[Res::GOLD] > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
if(ai->canRecruitAnyHero() && ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
{
if(auto t = ai->findTownWithTavern())
{

View File

@ -23,14 +23,21 @@ namespace Goals
{
class AbstractGoal;
class VisitTile;
typedef std::shared_ptr<Goals::AbstractGoal> TSubgoal;
class DLL_EXPORT TSubgoal : public std::shared_ptr<Goals::AbstractGoal>
{
public:
bool operator==(const TSubgoal & rhs) const;
//TODO: serialize?
};
typedef std::vector<TSubgoal> TGoalVec;
enum EGoals
{
INVALID = -1,
WIN, DO_NOT_LOSE, CONQUER, BUILD, //build needs to get a real reasoning
EXPLORE, GATHER_ARMY, BOOST_HERO,
EXPLORE, GATHER_ARMY,
BOOST_HERO,
RECRUIT_HERO,
BUILD_STRUCTURE, //if hero set, then in visited town
COLLECT_RES,
@ -48,7 +55,8 @@ enum EGoals
VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable
CLEAR_WAY_TO,
DIG_AT_TILE //elementar with hero on tile
DIG_AT_TILE,//elementar with hero on tile
BUY_ARMY //at specific town
};
//method chaining + clone pattern
@ -61,9 +69,9 @@ enum EGoals
enum {LOW_PR = -1};
TSubgoal sptr(const AbstractGoal & tmp);
DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
class AbstractGoal
class DLL_EXPORT AbstractGoal
{
public:
bool isElementar; VSETTER(bool, isElementar)
@ -129,7 +137,7 @@ public:
virtual bool operator==(AbstractGoal & g);
virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check
{
return false;
return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately
}
template<typename Handler> void serialize(Handler & h, const int version)
@ -149,7 +157,7 @@ public:
}
};
template<typename T> class CGoal : public AbstractGoal
template<typename T> class DLL_EXPORT CGoal : public AbstractGoal
{
public:
CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
@ -186,8 +194,8 @@ public:
}
TSubgoal iAmElementar()
{
setisElementar(true);
std::shared_ptr<AbstractGoal> ptr;
setisElementar(true); //FIXME: it's not const-correct, maybe we shoudl only set returned clone?
TSubgoal ptr;
ptr.reset(clone());
return ptr;
}
@ -199,7 +207,7 @@ public:
}
};
class Invalid : public CGoal<Invalid>
class DLL_EXPORT Invalid : public CGoal<Invalid>
{
public:
Invalid()
@ -214,7 +222,7 @@ public:
TSubgoal whatToDoToAchieve() override;
};
class Win : public CGoal<Win>
class DLL_EXPORT Win : public CGoal<Win>
{
public:
Win()
@ -229,7 +237,7 @@ public:
TSubgoal whatToDoToAchieve() override;
};
class NotLose : public CGoal<NotLose>
class DLL_EXPORT NotLose : public CGoal<NotLose>
{
public:
NotLose()
@ -244,7 +252,7 @@ public:
//TSubgoal whatToDoToAchieve() override;
};
class Conquer : public CGoal<Conquer>
class DLL_EXPORT Conquer : public CGoal<Conquer>
{
public:
Conquer()
@ -256,7 +264,7 @@ public:
TSubgoal whatToDoToAchieve() override;
};
class Build : public CGoal<Build>
class DLL_EXPORT Build : public CGoal<Build>
{
public:
Build()
@ -269,9 +277,10 @@ public:
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
bool fulfillsMe(TSubgoal goal) override;
};
class Explore : public CGoal<Explore>
class DLL_EXPORT Explore : public CGoal<Explore>
{
public:
Explore()
@ -291,7 +300,7 @@ public:
bool fulfillsMe(TSubgoal goal) override;
};
class GatherArmy : public CGoal<GatherArmy>
class DLL_EXPORT GatherArmy : public CGoal<GatherArmy>
{
public:
GatherArmy()
@ -309,7 +318,28 @@ public:
std::string completeMessage() const override;
};
class BoostHero : public CGoal<BoostHero>
class DLL_EXPORT BuyArmy : public CGoal<BuyArmy>
{
private:
BuyArmy()
: CGoal(Goals::BUY_ARMY)
{}
public:
BuyArmy(const CGTownInstance * Town, int val)
: CGoal(Goals::BUY_ARMY)
{
town = Town; //where to buy this army
value = val; //expressed in AI unit strength
priority = 2;//TODO: evaluate?
}
bool operator==(BuyArmy & g);
bool fulfillsMe(TSubgoal goal) override;
TSubgoal whatToDoToAchieve() override;
std::string completeMessage() const override;
};
class DLL_EXPORT BoostHero : public CGoal<BoostHero>
{
public:
BoostHero()
@ -324,7 +354,7 @@ public:
//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());};
};
class RecruitHero : public CGoal<RecruitHero>
class DLL_EXPORT RecruitHero : public CGoal<RecruitHero>
{
public:
RecruitHero()
@ -337,37 +367,37 @@ public:
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
bool operator==(RecruitHero & g);
};
class BuildThis : public CGoal<BuildThis>
class DLL_EXPORT BuildThis : public CGoal<BuildThis>
{
public:
BuildThis()
BuildThis() //should be private, but unit test uses it
: CGoal(Goals::BUILD_STRUCTURE)
{
//FIXME: should be not allowed (private)
}
{}
BuildThis(BuildingID Bid, const CGTownInstance * tid)
: CGoal(Goals::BUILD_STRUCTURE)
{
bid = Bid;
town = tid;
priority = 5;
priority = 1;
}
BuildThis(BuildingID Bid)
: CGoal(Goals::BUILD_STRUCTURE)
{
bid = Bid;
priority = 5;
priority = 1;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
//bool fulfillsMe(TSubgoal goal) override;
};
class CollectRes : public CGoal<CollectRes>
class DLL_EXPORT CollectRes : public CGoal<CollectRes>
{
public:
CollectRes()
@ -381,14 +411,13 @@ public:
value = val;
priority = 2;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
};
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
TSubgoal whatToDoToTrade();
bool fulfillsMe(TSubgoal goal) override;
};
class GatherTroops : public CGoal<GatherTroops>
class DLL_EXPORT GatherTroops : public CGoal<GatherTroops>
{
public:
GatherTroops()
@ -408,9 +437,10 @@ public:
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
bool fulfillsMe(TSubgoal goal) override;
};
class GetObj : public CGoal<GetObj>
class DLL_EXPORT GetObj : public CGoal<GetObj>
{
public:
GetObj() {} // empty constructor not allowed
@ -434,7 +464,7 @@ public:
std::string completeMessage() const override;
};
class FindObj : public CGoal<FindObj>
class DLL_EXPORT FindObj : public CGoal<FindObj>
{
public:
FindObj() {} // empty constructor not allowed
@ -443,6 +473,7 @@ public:
: CGoal(Goals::FIND_OBJ)
{
objid = ID;
resID = -1; //subid unspecified
priority = 1;
}
FindObj(int ID, int subID)
@ -457,9 +488,10 @@ public:
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
bool fulfillsMe(TSubgoal goal) override;
};
class VisitHero : public CGoal<VisitHero>
class DLL_EXPORT VisitHero : public CGoal<VisitHero>
{
public:
VisitHero()
@ -485,7 +517,7 @@ public:
std::string completeMessage() const override;
};
class GetArtOfType : public CGoal<GetArtOfType>
class DLL_EXPORT GetArtOfType : public CGoal<GetArtOfType>
{
public:
GetArtOfType()
@ -505,7 +537,7 @@ public:
TSubgoal whatToDoToAchieve() override;
};
class VisitTile : public CGoal<VisitTile>
class DLL_EXPORT VisitTile : public CGoal<VisitTile>
//tile, in conjunction with hero elementar; assumes tile is reachable
{
public:
@ -526,7 +558,7 @@ public:
std::string completeMessage() const override;
};
class ClearWayTo : public CGoal<ClearWayTo>
class DLL_EXPORT ClearWayTo : public CGoal<ClearWayTo>
{
public:
ClearWayTo()
@ -552,9 +584,10 @@ public:
{
return g.goalType == goalType && g.tile == tile;
}
bool fulfillsMe(TSubgoal goal) override;
};
class DigAtTile : public CGoal<DigAtTile>
class DLL_EXPORT DigAtTile : public CGoal<DigAtTile>
//elementar with hero on tile
{
public:
@ -579,7 +612,7 @@ public:
}
};
class CIssueCommand : public CGoal<CIssueCommand>
class DLL_EXPORT CIssueCommand : public CGoal<CIssueCommand>
{
std::function<bool()> command;

324
AI/VCAI/ResourceManager.cpp Normal file
View File

@ -0,0 +1,324 @@
/*
* ResourceManager.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 "ResourceManager.h"
#include "../../CCallback.h"
#include "../../lib/mapObjects/MapObjects.h"
#define GOLD_RESERVE (10000); //at least we'll be able to reach capitol
ResourceObjective::ResourceObjective(const TResources & Res, Goals::TSubgoal Goal)
: resources(Res), goal(Goal)
{
}
bool ResourceObjective::operator<(const ResourceObjective & ro) const
{
return goal->priority < ro.goal->priority;
}
ResourceManager::ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI)
: ai(AI), cb(CB)
{
}
void ResourceManager::setCB(CPlayerSpecificInfoCallback * CB)
{
cb = CB;
}
void ResourceManager::setAI(VCAI * AI)
{
ai = AI;
}
bool ResourceManager::canAfford(const TResources & cost) const
{
return freeResources().canAfford(cost);
}
TResources ResourceManager::estimateIncome() const
{
TResources ret;
for (const CGTownInstance * t : cb->getTownsInfo())
{
ret += t->dailyIncome();
}
for (const CGObjectInstance * obj : ai->getFlaggedObjects())
{
if (obj->ID == Obj::MINE)
{
switch (obj->subID)
{
case Res::WOOD:
case Res::ORE:
ret[obj->subID] += WOOD_ORE_MINE_PRODUCTION;
break;
case Res::GOLD:
case 7: //abandoned mine -> also gold
ret[Res::GOLD] += GOLD_MINE_PRODUCTION;
break;
default:
ret[obj->subID] += RESOURCE_MINE_PRODUCTION;
break;
}
}
}
return ret;
}
void ResourceManager::reserveResoures(const TResources & res, Goals::TSubgoal goal)
{
if (!goal->invalid())
tryPush(ResourceObjective(res, goal));
else
logAi->warn("Attempt to reserve resources for Invalid goal");
}
Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o) const
{
auto allResources = cb->getResourceAmount();
auto income = estimateIncome();
Res::ERes resourceType = Res::INVALID;
TResource amountToCollect = 0;
typedef std::pair<Res::ERes, TResource> resPair;
std::map<Res::ERes, TResource> missingResources;
//TODO: unit test for complex resource sets
//sum missing resources of given type for ALL reserved objectives
for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
{
//choose specific resources we need for this goal (not 0)
for (auto r = Res::ResourceSet::nziterator(o.resources); r.valid(); r++)
missingResources[r->resType] += it->resources[r->resType]; //goal it costs r units of resType
}
for (auto it = Res::ResourceSet::nziterator(o.resources); it.valid(); it++)
{
missingResources[it->resType] -= allResources[it->resType]; //missing = (what we need) - (what we have)
vstd::amax(missingResources[it->resType], 0); // if we have more resources than reserved, we don't need them
}
vstd::erase_if(missingResources, [=](const resPair & p) -> bool
{
return !(p.second); //in case evaluated to 0 or less
});
if (missingResources.empty()) //FIXME: should be unit-tested out
{
logAi->error("We don't need to collect resources %s for goal %s", o.resources.toString(), o.goal->name());
return o.goal;
}
float goalPriority = 10; //arbitrary, will be divided
for (const resPair & p : missingResources)
{
if (!income[p.first]) //prioritize resources with 0 income
{
resourceType = p.first;
amountToCollect = p.second;
goalPriority /= amountToCollect; //need more resources -> lower priority
break;
}
}
if (resourceType == Res::INVALID) //no needed resources has 0 income,
{
//find the one which takes longest to collect
typedef std::pair<Res::ERes, float> timePair;
std::map<Res::ERes, float> daysToEarn;
for (auto it : missingResources)
daysToEarn[it.first] = (float)missingResources[it.first] / income[it.first];
auto incomeComparer = [&income](const timePair & lhs, const timePair & rhs) -> bool
{
//theoretically income can be negative, but that falls into this comparison
return lhs.second < rhs.second;
};
resourceType = boost::max_element(daysToEarn, incomeComparer)->first;
amountToCollect = missingResources[resourceType];
goalPriority /= daysToEarn[resourceType]; //more days - lower priority
}
if (resourceType == Res::GOLD)
goalPriority *= 1000;
//this is abstract goal and might take soem time to complete
return Goals::sptr(Goals::CollectRes(resourceType, amountToCollect).setisAbstract(true));
}
Goals::TSubgoal ResourceManager::whatToDo() const //suggest any goal
{
if (queue.size())
{
auto o = queue.top();
auto allResources = cb->getResourceAmount(); //we don't consider savings, it's out top-priority goal
if (allResources.canAfford(o.resources))
return o.goal;
else //we can't afford even top-priority goal, need to collect resources
return collectResourcesForOurGoal(o);
}
else
return Goals::sptr(Goals::Invalid()); //nothing else to do
}
Goals::TSubgoal ResourceManager::whatToDo(TResources &res, Goals::TSubgoal goal)
{
TResources accumulatedResources;
auto allResources = cb->getResourceAmount();
ResourceObjective ro(res, goal);
tryPush(ro);
//check if we can afford all the objectives with higher priority first
for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
{
accumulatedResources += it->resources;
if (!accumulatedResources.canBeAfforded(allResources)) //can't afford
return collectResourcesForOurGoal(ro);
else //can afford all goals up to this point
{
if (it->goal == goal)
return goal; //can afford immediately
}
}
return collectResourcesForOurGoal(ro); //fallback, ever needed?
}
bool ResourceManager::containsObjective(Goals::TSubgoal goal) const
{
//TODO: unit tests for once
for (auto objective : queue)
{
if (objective.goal == goal)
return true;
}
return false;
}
bool ResourceManager::notifyGoalCompleted(Goals::TSubgoal goal)
{
if (goal->invalid())
logAi->warn("Attempt to complete Invalid goal");
bool removedGoal = false;
while (true)
{ //unfortunatelly we can't use remove_if on heap
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
{
return ro.goal == goal || ro.goal->fulfillsMe (goal);
});
if (it != queue.end()) //removed at least one
{
queue.erase(queue.s_handle_from_iterator(it));
logAi->debug("Removed goal %s from ResourceManager.", it->goal->name());
removedGoal = true;
}
else //found nothing more to remove
return removedGoal;
}
return removedGoal;
}
bool ResourceManager::updateGoal(Goals::TSubgoal goal)
{
//we update priority of goal if it is easier or more difficult to complete
if (goal->invalid())
logAi->warn("Attempt to update Invalid goal");
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
{
return ro.goal == goal;
});
if (it != queue.end())
{
it->goal->setpriority(goal->priority);
auto handle = queue.s_handle_from_iterator(it);
queue.update(handle); //restore order
return true;
}
else
return false;
}
bool ResourceManager::tryPush(const ResourceObjective & o)
{
auto goal = o.goal;
auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
{
return ro.goal == goal;
});
if (it != queue.end())
{
auto handle = queue.s_handle_from_iterator(it);
vstd::amax(goal->priority, it->goal->priority); //increase priority if case
//update resources with new value
queue.update(handle, ResourceObjective(o.resources, goal)); //restore order
return false;
}
else
{
queue.push(o); //add new objective
logAi->debug("Reserved resources (%s) for %s", o.resources.toString(), goal->name());
return true;
}
}
bool ResourceManager::hasTasksLeft() const
{
return !queue.empty();
}
TResources ResourceManager::reservedResources() const
{
TResources res;
for (auto it : queue) //substract the value of reserved goals
res += it.resources;
return res;
}
TResources ResourceManager::freeResources() const
{
TResources myRes = cb->getResourceAmount();
auto towns = cb->getTownsInfo();
if (towns.size()) //we don't save for Capitol if there are no towns
{
if (std::none_of(towns.begin(), towns.end(), [](const CGTownInstance * x) -> bool
{
return x->builtBuildings.find(BuildingID::CAPITOL) != x->builtBuildings.end();
}))
{
myRes[Res::GOLD] -= GOLD_RESERVE;
//what if capitol is blocked from building in all possessed towns (set in map editor)?
}
}
myRes -= reservedResources(); //substract the value of reserved goals
for (auto & val : myRes)
vstd::amax(val, 0); //never negative
return myRes;
}
TResource ResourceManager::freeGold() const
{
return freeResources()[Res::GOLD];
}
TResources ResourceManager::allResources() const
{
return cb->getResourceAmount();
}
TResource ResourceManager::allGold() const
{
return cb->getResourceAmount()[Res::GOLD];
}

111
AI/VCAI/ResourceManager.h Normal file
View File

@ -0,0 +1,111 @@
/*
* ResourceManager.h, 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
*
*/
#pragma once
#include "AIUtility.h"
#include "Goals.h"
#include "../../lib/GameConstants.h"
#include "../../lib/VCMI_Lib.h"
#include "VCAI.h"
#include <boost/heap/binomial_heap.hpp>
class AIhelper;
class IResourceManager;
struct DLL_EXPORT ResourceObjective
{
ResourceObjective() = default;
ResourceObjective(const TResources &res, Goals::TSubgoal goal);
bool operator < (const ResourceObjective &ro) const;
TResources resources; //how many resoures do we need
Goals::TSubgoal goal; //what for (build, gather army etc...)
//TODO: register?
template<typename Handler> void serializeInternal(Handler & h, const int version)
{
h & resources;
//h & goal; //FIXME: goal serialization is broken
}
};
class IResourceManager //: public: IAbstractManager
{
public:
virtual ~IResourceManager() = default;
virtual void setCB(CPlayerSpecificInfoCallback * CB) = 0;
virtual void setAI(VCAI * AI) = 0;
virtual TResources reservedResources() const = 0;
virtual TResources freeResources() const = 0;
virtual TResource freeGold() const = 0;
virtual TResources allResources() const = 0;
virtual TResource allGold() const = 0;
virtual Goals::TSubgoal whatToDo() const = 0;//get highest-priority goal
virtual Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) = 0;
virtual bool containsObjective(Goals::TSubgoal goal) const = 0;
virtual bool hasTasksLeft() const = 0;
private:
virtual bool notifyGoalCompleted(Goals::TSubgoal goal) = 0;
};
class DLL_EXPORT ResourceManager : public IResourceManager
{
/*Resource Manager is a smart shopping list for AI to help
Uses priority queue based on CGoal.priority */
friend class VCAI;
friend class AIhelper;
friend struct SetGlobalState;
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
VCAI * ai;
public:
ResourceManager() = default;
ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
bool canAfford(const TResources & cost) const;
TResources reservedResources() const override;
TResources freeResources() const override;
TResource freeGold() const override;
TResources allResources() const override;
TResource allGold() const override;
Goals::TSubgoal whatToDo() const override; //peek highest-priority goal
Goals::TSubgoal whatToDo(TResources & res, Goals::TSubgoal goal); //can we afford this goal or need to CollectRes?
bool containsObjective(Goals::TSubgoal goal) const;
bool hasTasksLeft() const override;
protected: //not-const actions only for AI
virtual void reserveResoures(const TResources & res, Goals::TSubgoal goal = Goals::TSubgoal());
virtual bool notifyGoalCompleted(Goals::TSubgoal goal);
virtual bool updateGoal(Goals::TSubgoal goal); //new goal must have same properties but different priority
virtual bool tryPush(const ResourceObjective &o);
//inner processing
virtual TResources estimateIncome() const;
virtual Goals::TSubgoal collectResourcesForOurGoal(ResourceObjective &o) const;
void setCB(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
private:
TResources saving;
boost::heap::binomial_heap<ResourceObjective> queue;
//TODO: register?
template<typename Handler> void serializeInternal(Handler & h, const int version)
{
h & saving;
h & queue;
}
};

View File

@ -83,10 +83,14 @@
</Linker>
<Unit filename="AIUtility.cpp" />
<Unit filename="AIUtility.h" />
<Unit filename="AIhelper.cpp" />
<Unit filename="AIhelper.h" />
<Unit filename="Fuzzy.cpp" />
<Unit filename="Fuzzy.h" />
<Unit filename="Goals.cpp" />
<Unit filename="Goals.h" />
<Unit filename="ResourceManager.cpp" />
<Unit filename="ResourceManager.h" />
<Unit filename="StdInc.h">
<Option compile="1" />
<Option weight="0" />

View File

@ -10,6 +10,7 @@
#include "StdInc.h"
#include "VCAI.h"
#include "Fuzzy.h"
#include "ResourceManager.h"
#include "../../lib/UnlockGuard.h"
#include "../../lib/mapObjects/MapObjects.h"
@ -22,16 +23,18 @@
#include "../../lib/serializer/BinarySerializer.h"
#include "../../lib/serializer/BinaryDeserializer.h"
#include "AIhelper.h"
extern FuzzyHelper * fh;
class CGVisitableOPW;
const double SAFE_ATTACK_CONSTANT = 1.5;
const int GOLD_RESERVE = 10000; //when buying creatures we want to keep at least this much gold (10000 so at least we'll be able to reach capitol)
//one thread may be turn of AI and another will be handling a side effect for AI2
boost::thread_specific_ptr<CCallback> cb;
boost::thread_specific_ptr<VCAI> ai;
extern boost::thread_specific_ptr<AIhelper> ah;
//std::map<int, std::map<int, int> > HeroView::infosCount;
@ -45,9 +48,15 @@ struct SetGlobalState
ai.reset(AI);
cb.reset(AI->myCb.get());
if (!ah.get())
ah.reset(new AIhelper());
ah->setAI(AI); //does this make any sense?
ah->setCB(cb.get());
}
~SetGlobalState()
{
//TODO: how to handle rm? shouldn't be called after ai is destroyed, hopefully
//TODO: to ensure that, make rm unique_ptr
ai.release();
cb.release();
}
@ -537,6 +546,8 @@ void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
void VCAI::buildChanged(const CGTownInstance * town, BuildingID buildingID, int what)
{
LOG_TRACE_PARAMS(logAi, "what '%i'", what);
if (town->getOwner() == playerID && what == 1) //built
completeGoal(sptr(Goals::BuildThis(buildingID, town)));
NET_EVENT_HANDLER;
}
@ -564,7 +575,10 @@ void VCAI::init(std::shared_ptr<CCallback> CB)
LOG_TRACE(logAi);
myCb = CB;
cbc = CB;
NET_EVENT_HANDLER;
ah.reset(new AIhelper());
NET_EVENT_HANDLER; //sets ah->rm->cb
playerID = *myCb->getMyColor();
myCb->waitTillRealize = true;
myCb->unlockGsWhenWaiting = true;
@ -768,18 +782,22 @@ void VCAI::makeTurn()
makeTurnInternal();
}
/*This method defines core of AI behavior. It is not consistent system, just "a bit of everything for everyone" done in some example order.
It is not supposed to work this way in final version of VCAI. It consists of few actions/loops done in particular order, hard parts are explained below with focus on explaining hero management logic*/
void VCAI::makeTurnInternal()
{
saving = 0;
//it looks messy here, but it's better to have armed heroes before attempting realizing goals
for(const CGTownInstance * t : cb->getTownsInfo())
moveCreaturesToHero(t);
try
{
//Pick objects reserved in previous turn - we expect only nerby objects there
/*Below loop causes heroes with objects locked to them to keep trying to realize previous goal. By design when object is locked another heroes do not attempt to visit it.
Object lock happens on turn when some hero gets assigned visit tile with appropiate map object. So basically all heroes that had VisitTile goal with object assigned and not completed
will be in the loop. Sometimes heroes get assigned more important objectives, but still keep reserved objects for later. There is a problem with that - they have
reserved objects in the list, so they fall to this loop at start of turn and visiting object isn't delayed. Comments for that function are supposed to help newer VCAI maintainers*/
//Pick objects reserved in previous turn - we expect only nearby objects there
auto reservedHeroesCopy = reservedHeroesMap; //work on copy => the map may be changed while iterating (eg because hero died when attempting a goal)
for(auto hero : reservedHeroesCopy)
{
@ -805,8 +823,17 @@ void VCAI::makeTurnInternal()
}
//now try to win
/*below line performs goal decomposition, result of the function is ONE goal for ONE hero to realize.*/
striveToGoal(sptr(Goals::Win()));
//TODO: add ResourceManager goals to the pool and process them all at once
if (ah->hasTasksLeft())
striveToGoal(ah->whatToDo());
/*Explanation of below loop: At the time of writing this - goals get decomposited either to GatherArmy or Visit Tile.
Visit tile that is about visiting object gets processed at beginning of MakeTurnInternal without re-evaluation.
Rest of goals that got started via striveToGoal(sptr(Goals::Win())); in previous turns and not finished get continued here.
Also they are subject for re-evaluation to see if there is better goal to start (still talking only about heroes that got goals started by via striveToGoal(sptr(Goals::Win())); in previous turns.*/
//finally, continue our abstract long-term goals
int oldMovement = 0;
int newMovement = 0;
@ -851,7 +878,11 @@ void VCAI::makeTurnInternal()
striveToQuest(quest);
}
//TODO: striveToGoal
striveToGoal(sptr(Goals::Build())); //TODO: smarter building management
/*Below function is also responsible for hero movement via internal wander function. By design it is separate logic for heroes that have nothing to do.
Heroes that were not picked by striveToGoal(sptr(Goals::Win())); recently (so they do not have new goals and cannot continue/reevaluate previously locked goals) will do logic in wander().*/
performTypicalActions();
//for debug purpose
@ -899,7 +930,7 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
if(h->visitedTown) //we are inside, not just attacking
{
townVisitsThisWeek[h].insert(h->visitedTown);
if(!h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST + saving[Res::GOLD])
if(!h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST)
{
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
@ -1135,6 +1166,7 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter)
{
//now used only for visited dwellings / towns, not BuyArmy goal
for(int i = 0; i < d->creatures.size(); i++)
{
if(!d->creatures[i].second.size())
@ -1142,17 +1174,14 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit
int count = d->creatures[i].first;
CreatureID creID = d->creatures[i].second.back();
// const CCreature *c = VLC->creh->creatures[creID];
// if(containsSavedRes(c->cost))
// continue;
vstd::amin(count, freeResources() / VLC->creh->creatures[creID]->cost);
vstd::amin(count, ah->freeResources() / VLC->creh->creatures[creID]->cost);
if(count > 0)
cb->recruitCreatures(d, recruiter, creID, count, i);
}
}
bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays) const
bool VCAI::tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays)
{
if(maxDays == 0)
{
@ -1184,52 +1213,52 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
if(maxDays && toBuild.size() > maxDays)
return false;
TResources currentRes = cb->getResourceAmount();
//TODO: calculate if we have enough resources to build it in maxDays
//TODO: calculate if we have enough resources to build it in maxDays?
for(const auto & buildID : toBuild)
{
const CBuilding * b = t->town->buildings.at(buildID);
EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
if(canBuild == EBuildingState::ALLOWED)
if (canBuild == EBuildingState::ALLOWED)
{
if(!containsSavedRes(b->resources))
{
logAi->debug("Player %d will build %s in town of %s at %s", playerID, b->Name(), t->name, t->pos.toString());
cb->buildBuilding(t, buildID);
return true;
}
continue;
buildStructure(t, buildID);
return true;
}
else if(canBuild == EBuildingState::NO_RESOURCES)
{
//We can't do anything about it - no requests from this function
continue;
}
else if(canBuild == EBuildingState::PREREQUIRES)
else if (canBuild == EBuildingState::PREREQUIRES)
{
// can happen when dependencies have their own missing dependencies
if(tryBuildStructure(t, buildID, maxDays - 1))
if (tryBuildThisStructure(t, buildID, maxDays - 1))
return true;
}
else if(canBuild == EBuildingState::MISSING_BASE)
else if (canBuild == EBuildingState::MISSING_BASE)
{
if(tryBuildStructure(t, b->upgrade, maxDays - 1))
return true;
if (tryBuildThisStructure(t, b->upgrade, maxDays - 1))
return true;
}
else if (canBuild == EBuildingState::NO_RESOURCES)
{
//we may need to gather resources for those
PotentialBuilding pb;
pb.bid = buildID;
pb.price = t->getBuildingCost(buildID);
potentialBuildings.push_back(pb); //these are checked again in try
return false;
}
else
return false;
}
return false;
}
bool VCAI::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays) const
bool VCAI::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
{
for(const auto & building : buildList)
{
if(t->hasBuilt(building))
continue;
if(tryBuildStructure(t, building, maxDays))
return true;
return tryBuildThisStructure(t, building, maxDays);
}
return false; //Can't build anything
}
@ -1246,17 +1275,24 @@ BuildingID VCAI::canBuildAnyStructure(const CGTownInstance * t, std::vector<Buil
return BuildingID::NONE; //Can't build anything
}
bool VCAI::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays) const
bool VCAI::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
{
for(const auto & building : buildList)
{
if(t->hasBuilt(building))
continue;
return tryBuildStructure(t, building, maxDays);
return tryBuildThisStructure(t, building, maxDays);
}
return false; //Nothing to build
}
void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
{
auto name = t->town->buildings.at(building)->Name();
logAi->debug("Player %d will build %s in town of %s at %s", playerID, name, t->name, t->pos.toString());
cb->buildBuilding(t, building); //just do this;
}
//Set of buildings for different goals. Does not include any prerequisites.
static const BuildingID essential[] = {BuildingID::TAVERN, BuildingID::TOWN_HALL};
static const BuildingID goldSource[] = {BuildingID::TOWN_HALL, BuildingID::CITY_HALL, BuildingID::CAPITOL};
@ -1272,7 +1308,7 @@ static const BuildingID _spells[] = {BuildingID::MAGES_GUILD_1, BuildingID::MAGE
static const BuildingID extra[] = {BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
BuildingID::SPECIAL_4, BuildingID::SHIPYARD}; // all remaining buildings
void VCAI::buildStructure(const CGTownInstance * t) const
bool VCAI::tryBuildStructure(const CGTownInstance * t)
{
//TODO make *real* town development system
//TODO: faction-specific development: use special buildings, build dwellings in better order, etc
@ -1284,41 +1320,40 @@ void VCAI::buildStructure(const CGTownInstance * t) const
TResources currentIncome = t->dailyIncome();
if(tryBuildAnyStructure(t, std::vector<BuildingID>(essential, essential + ARRAY_COUNT(essential))))
return;
return true;
//the more gold the better and less problems later
if(tryBuildNextStructure(t, std::vector<BuildingID>(goldSource, goldSource + ARRAY_COUNT(goldSource))))
return;
return true;
//workaround for mantis #2696 - build fort and citadel - building castle will be handled without bug
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL)
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) &&
cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL)
{
if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
{
if(tryBuildNextStructure(t, std::vector<BuildingID>(capitolRequirements, capitolRequirements + ARRAY_COUNT(capitolRequirements))))
return;
if(tryBuildNextStructure(t, std::vector<BuildingID>(capitolRequirements,
capitolRequirements + ARRAY_COUNT(capitolRequirements))))
return true;
}
}
//save money for capitol or city hall if capitol unavailable, do not build other things (unless gold source buildings are disabled in map editor)
if(!vstd::contains(t->builtBuildings, BuildingID::CAPITOL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
return;
else if(!vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) == EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
return;
else if(!vstd::contains(t->builtBuildings, BuildingID::TOWN_HALL) && cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)
return;
//TODO: save money for capitol or city hall if capitol unavailable
//do not build other things (unless gold source buildings are disabled in map editor)
if(cb->getDate(Date::DAY_OF_WEEK) > 6) // last 2 days of week - try to focus on growth
{
if(tryBuildNextStructure(t, std::vector<BuildingID>(unitGrowth, unitGrowth + ARRAY_COUNT(unitGrowth)), 2))
return;
return true;
}
// first in-game week or second half of any week: try build dwellings
if(cb->getDate(Date::DAY) < 7 || cb->getDate(Date::DAY_OF_WEEK) > 3)
{
if(tryBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)))
return;
if(tryBuildAnyStructure(t, std::vector<BuildingID>(unitsSource,
unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)))
return true;
}
//try to upgrade dwelling
@ -1326,16 +1361,16 @@ void VCAI::buildStructure(const CGTownInstance * t) const
{
if(t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]))
{
if(tryBuildStructure(t, unitsUpgrade[i]))
return;
if(tryBuildThisStructure(t, unitsUpgrade[i]))
return true;
}
}
//remaining tasks
if(tryBuildNextStructure(t, std::vector<BuildingID>(_spells, _spells + ARRAY_COUNT(_spells))))
return;
return true;
if(tryBuildAnyStructure(t, std::vector<BuildingID>(extra, extra + ARRAY_COUNT(extra))))
return;
return true;
//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
std::vector<BuildingID> extraBuildings;
@ -1345,7 +1380,9 @@ void VCAI::buildStructure(const CGTownInstance * t) const
extraBuildings.push_back(buildingInfo.first);
}
if(tryBuildAnyStructure(t, extraBuildings))
return;
return true;
return false;
}
bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, SectorMap & sm)
@ -1404,7 +1441,7 @@ bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
t = findTownWithTavern();
if(!t)
return false;
if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST) //TODO: use ResourceManager
return false;
if(cb->getHeroesInfo().size() >= ALLOWED_ROAMING_HEROES)
return false;
@ -1416,6 +1453,17 @@ bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
void VCAI::wander(HeroPtr h)
{
auto visitTownIfAny = [this](HeroPtr h) -> bool
{
if (h->visitedTown)
{
townVisitsThisWeek[h].insert(h->visitedTown);
buildArmyIn(h->visitedTown);
return true;
}
};
//unclaim objects that are now dangerous for us
auto reservedObjsSetCopy = reservedHeroesMap[h];
for(auto obj : reservedObjsSetCopy)
@ -1476,14 +1524,19 @@ void VCAI::wander(HeroPtr h)
auto compareReinforcements = [h](const CGTownInstance * lhs, const CGTownInstance * rhs) -> bool
{
return howManyReinforcementsCanGet(h, lhs) < howManyReinforcementsCanGet(h, rhs);
auto r1 = howManyReinforcementsCanGet(h, lhs),
r2 = howManyReinforcementsCanGet(h, rhs);
if (r1 != r2)
return r1 < r2;
else
return howManyReinforcementsCanBuy(h, lhs) < howManyReinforcementsCanBuy(h, rhs);
};
std::vector<const CGTownInstance *> townsReachable;
std::vector<const CGTownInstance *> townsNotReachable;
for(const CGTownInstance * t : cb->getTownsInfo())
{
if(!t->visitingHero && howManyReinforcementsCanGet(h, t) && !vstd::contains(townVisitsThisWeek[h], t))
if(!t->visitingHero && !vstd::contains(townVisitsThisWeek[h], t))
{
if(isAccessibleForHero(t->visitablePos(), h))
townsReachable.push_back(t);
@ -1491,10 +1544,9 @@ void VCAI::wander(HeroPtr h)
townsNotReachable.push_back(t);
}
}
if(townsReachable.size())
if(townsReachable.size()) //travel to town with largest garrison, or empty - better than nothing
{
boost::sort(townsReachable, compareReinforcements);
dests.push_back(townsReachable.back());
dests.push_back(*boost::max_element(townsReachable, compareReinforcements));
}
else if(townsNotReachable.size())
{
@ -1549,26 +1601,23 @@ void VCAI::wander(HeroPtr h)
//wander should not cause heroes to be reserved - they are always considered free
logAi->debug("Of all %d destinations, object oid=%d seems nice", dests.size(), dest.id.getNum());
if(!goVisitObj(dest, h))
if (!goVisitObj(dest, h))
{
if(!dest)
if (!dest)
{
logAi->debug("Visit attempt made the object (id=%d) gone...", dest.id.getNum());
}
else
{
logAi->debug("Hero %s apparently used all MPs (%d left)", h->name, h->movement);
return;
break;
}
}
}
if(h->visitedTown)
{
townVisitsThisWeek[h].insert(h->visitedTown);
buildArmyIn(h->visitedTown);
else //we reached our destination
visitTownIfAny(h);
}
}
visitTownIfAny(h); //in case hero is just sitting in town
}
void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal)
@ -1592,12 +1641,13 @@ void VCAI::evaluateGoal(HeroPtr h)
void VCAI::completeGoal(Goals::TSubgoal goal)
{
logAi->trace("Completing goal: %s", goal->name());
ah->notifyGoalCompleted(goal);
if(const CGHeroInstance * h = goal->hero.get(true))
{
auto it = lockedHeroes.find(h);
if(it != lockedHeroes.end())
{
if(it->second == goal)
if(it->second == goal || it->second->fulfillsMe(goal)) //FIXME this is overspecified, fulfillsMe shoudl be complete
{
logAi->debug(goal->completeMessage());
lockedHeroes.erase(it); //goal fulfilled, free hero
@ -1608,7 +1658,7 @@ void VCAI::completeGoal(Goals::TSubgoal goal)
{
vstd::erase_if(lockedHeroes, [goal](std::pair<HeroPtr, Goals::TSubgoal> p)
{
if(*(p.second) == *goal || p.second->fulfillsMe(goal)) //we could have fulfilled goals of other heroes by chance
if(p.second == goal || p.second->fulfillsMe(goal)) //we could have fulfilled goals of other heroes by chance
{
logAi->debug(p.second->completeMessage());
return true;
@ -2080,30 +2130,19 @@ void VCAI::tryRealize(Goals::VisitHero & g)
void VCAI::tryRealize(Goals::BuildThis & g)
{
const CGTownInstance * t = g.town;
auto b = BuildingID(g.bid);
auto t = g.town;
if(!t && g.hero)
t = g.hero->visitedTown;
if(!t)
if (t)
{
for(const CGTownInstance * t : cb->getTownsInfo())
if (cb->canBuildStructure(t, b) == EBuildingState::ALLOWED)
{
switch(cb->canBuildStructure(t, BuildingID(g.bid)))
{
case EBuildingState::ALLOWED:
cb->buildBuilding(t, BuildingID(g.bid));
return;
default:
break;
}
logAi->debug("Player %d will build %s in town of %s at %s",
playerID, t->town->buildings.at(b)->Name(), t->name, t->pos.toString());
cb->buildBuilding(t, b);
throw goalFulfilledException(sptr(g));
}
}
else if(cb->canBuildStructure(t, BuildingID(g.bid)) == EBuildingState::ALLOWED)
{
cb->buildBuilding(t, BuildingID(g.bid));
return;
}
throw cannotFulfillGoalException("Cannot build a given structure!");
}
@ -2122,10 +2161,10 @@ void VCAI::tryRealize(Goals::DigAtTile & g)
}
}
void VCAI::tryRealize(Goals::CollectRes & g)
void VCAI::tryRealize(Goals::CollectRes & g) //trade
{
if(cb->getResourceAmount(static_cast<Res::ERes>(g.resID)) >= g.value)
throw cannotFulfillGoalException("Goal is already fulfilled!");
if(ah->freeResources()[g.resID] >= g.value) //goal is already fulfilled. Why we need this check, anyway?
throw goalFulfilledException(sptr(g));
if(const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid), false))
{
@ -2133,15 +2172,16 @@ void VCAI::tryRealize(Goals::CollectRes & g)
{
for(Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
{
if(i == g.resID)
if(i == g.resID) //sell any other resource
continue;
//TODO: check all our reserved resources and avoid them
int toGive, toGet;
m->getOffer(i, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
toGive = toGive * (cb->getResourceAmount(i) / toGive);
//TODO trade only as much as needed
cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, i, g.resID, toGive);
if(cb->getResourceAmount(static_cast<Res::ERes>(g.resID)) >= g.value)
return;
if (ah->freeResources()[g.resID] >= g.value)
throw goalFulfilledException(sptr(g));
}
throw cannotFulfillGoalException("I cannot get needed resources by trade!");
@ -2153,28 +2193,87 @@ void VCAI::tryRealize(Goals::CollectRes & g)
}
else
{
saving[g.resID] = 1;
throw cannotFulfillGoalException("No object that could be used to raise resources!");
}
}
void VCAI::tryRealize(Goals::Build & g)
{
bool didWeBuildSomething = false;
for(const CGTownInstance * t : cb->getTownsInfo())
{
logAi->debug("Looking into %s", t->name);
buildStructure(t);
if(!ai->primaryHero() ||
(t->getArmyStrength() > ai->primaryHero()->getArmyStrength() * 2 && !isAccessibleForHero(t->visitablePos(), ai->primaryHero())))
potentialBuildings.clear(); //start fresh with every town
if (tryBuildStructure(t))
didWeBuildSomething = true;
else if (potentialBuildings.size())
{
recruitHero(t);
buildArmyIn(t);
auto pb = potentialBuildings.front(); //gather resources for any we can't afford
auto goal = ah->whatToDo(pb.price, sptr(Goals::BuildThis(pb.bid, t)));
if (goal->goalType == Goals::BUILD_STRUCTURE)
{
logAi->error("We were supposed to NOT afford any building");
buildStructure(t, pb.bid); //do it right now
didWeBuildSomething = true;
}
else
{
//TODO: right now we do that for every town in order. Consider comparison of all potential goals.
striveToGoal(goal); //gather resources, or something else?
}
}
}
throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
if (!didWeBuildSomething)
throw cannotFulfillGoalException("BUILD has been realized as much as possible."); //who catches it and what for?
}
void VCAI::tryRealize(Goals::BuyArmy & g)
{
auto t = g.town;
ui64 valueBought = 0;
//buy the stacks with largest AI value
while (valueBought < g.value)
{
auto res = ah->allResources();
std::vector<creInfo> creaturesInDwellings;
for (int i = 0; i < t->creatures.size(); i++)
{
auto ci = infoFromDC(t->creatures[i]);
ci.level = i; //this is important for Dungeon Summoning Portal
creaturesInDwellings.push_back(ci);
}
vstd::erase_if(creaturesInDwellings, [](const creInfo & ci) -> bool
{
return !ci.count || ci.creID == -1;
});
if (creaturesInDwellings.empty())
throw cannotFulfillGoalException("Can't buy any more creatures!");
creInfo ci =
*boost::max_element(creaturesInDwellings, [&res](const creInfo & lhs, const creInfo & rhs)
{
//max value of creatures we can buy with our res
int value1 = lhs.cre->AIValue * (std::min(lhs.count, res / lhs.cre->cost)),
value2 = rhs.cre->AIValue * (std::min(rhs.count, res / rhs.cre->cost));
return value1 < value2;
});
vstd::amin(ci.count, res / ci.cre->cost); //max count we can afford
if (ci.count > 0)
{
cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
valueBought += ci.count * ci.cre->AIValue;
}
else
throw cannotFulfillGoalException("Can't buy any more creatures!");
}
throw goalFulfilledException(sptr(g)); //we bought as many creatures as we wanted
}
void VCAI::tryRealize(Goals::Invalid & g)
{
throw cannotFulfillGoalException("I don't know how to fulfill this!");
@ -2293,7 +2392,7 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
boost::this_thread::interruption_point();
goal = goal->whatToDoToAchieve();
--maxGoals;
if(*goal == *ultimateGoal) //compare objects by value
if(goal == ultimateGoal) //compare objects by value
throw cannotFulfillGoalException("Goal dependency loop detected!");
}
catch(goalFulfilledException & e)
@ -2309,7 +2408,6 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
return sptr(Goals::Invalid());
}
}
try
{
boost::this_thread::interruption_point();
@ -2318,10 +2416,10 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
{
if(ultimateGoal->hero) // we seemingly don't know what to do with hero, free him
vstd::erase_if_present(lockedHeroes, ultimateGoal->hero);
std::runtime_error e("Too many subgoals, don't know what to do");
throw (e);
throw (std::runtime_error("Too many subgoals, don't know what to do"));
}
else //we can proceed
else //we found elementar goal and can proceed
{
if(goal->hero) //lock this hero to fulfill ultimate goal
{
@ -2335,7 +2433,7 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
logAi->debug("Choosing abstract goal %s", goal->name());
break;
}
else
else //try realize
{
logAi->debug("Trying to realize %s (value %2.3f)", goal->name(), goal->priority);
goal->accept(this);
@ -2350,7 +2448,9 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
}
catch(goalFulfilledException & e)
{
//the goal was completed successfully
//the sub-goal was completed successfully
completeGoal(e.goal);
//local goal was also completed... TODO: or not?
completeGoal(goal);
//completed goal was main goal //TODO: find better condition
if(ultimateGoal->fulfillsMe(goal) || maxGoals > searchDepth2)
@ -2505,11 +2605,6 @@ void VCAI::striveToQuest(const QuestInfo & q)
void VCAI::performTypicalActions()
{
//TODO: build army only on request
for(auto t : cb->getTownsInfo())
{
buildArmyIn(t);
}
for(auto h : getUnblockedHeroes())
{
if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn
@ -2678,49 +2773,6 @@ int3 VCAI::explorationDesperate(HeroPtr h)
return bestTile;
}
TResources VCAI::estimateIncome() const
{
TResources ret;
for(const CGTownInstance * t : cb->getTownsInfo())
{
ret += t->dailyIncome();
}
for(const CGObjectInstance * obj : getFlaggedObjects())
{
if(obj->ID == Obj::MINE)
{
switch(obj->subID)
{
case Res::WOOD:
case Res::ORE:
ret[obj->subID] += WOOD_ORE_MINE_PRODUCTION;
break;
case Res::GOLD:
case 7: //abandoned mine -> also gold
ret[Res::GOLD] += GOLD_MINE_PRODUCTION;
break;
default:
ret[obj->subID] += RESOURCE_MINE_PRODUCTION;
break;
}
}
}
return ret;
}
bool VCAI::containsSavedRes(const TResources & cost) const
{
for(int i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
{
if(saving[i] && cost[i])
return true;
}
return false;
}
void VCAI::checkHeroArmy(HeroPtr h)
{
auto it = lockedHeroes.find(h);
@ -2745,6 +2797,7 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
hero = heroes[1];
}
cb->recruitHero(t, hero);
throw goalFulfilledException(sptr(Goals::RecruitHero().settown(t)));
}
else if(throwing)
{
@ -2839,20 +2892,6 @@ void VCAI::validateObject(ObjectIdRef obj)
}
}
TResources VCAI::freeResources() const
{
TResources myRes = cb->getResourceAmount();
auto iterator = cb->getTownsInfo();
if(std::none_of(iterator.begin(), iterator.end(), [](const CGTownInstance * x) -> bool
{
return x->builtBuildings.find(BuildingID::CAPITOL) != x->builtBuildings.end();
})
/*|| std::all_of(iterator.begin(), iterator.end(), [](const CGTownInstance * x) -> bool { return x->forbiddenBuildings.find(BuildingID::CAPITOL) != x->forbiddenBuildings.end(); })*/ )
myRes[Res::GOLD] -= GOLD_RESERVE; //what if capitol is blocked from building in all possessed towns (set in map editor)? What about reserve for city hall or something similar in that case?
vstd::amax(myRes[Res::GOLD], 0);
return myRes;
}
std::shared_ptr<SectorMap> VCAI::getCachedSectorMap(HeroPtr h)
{
auto it = cachedSectorMaps.find(h);
@ -3261,8 +3300,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
case Obj::SCHOOL_OF_MAGIC:
case Obj::SCHOOL_OF_WAR:
{
TResources myRes = ai->myCb->getResourceAmount();
if(myRes[Res::GOLD] - GOLD_RESERVE < 1000)
if (ah->freeGold() < 1000)
return false;
break;
}
@ -3272,8 +3310,8 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
break;
case Obj::TREE_OF_KNOWLEDGE:
{
TResources myRes = ai->myCb->getResourceAmount();
if(myRes[Res::GOLD] - GOLD_RESERVE < 2000 || myRes[Res::GEMS] < 10)
TResources myRes = ah->freeResources();
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
return false;
break;
}
@ -3287,7 +3325,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
//TODO: only on request
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
return false;
else if(ai->myCb->getResourceAmount()[Res::GOLD] - GOLD_RESERVE < GameConstants::HERO_GOLD_COST)
else if(ah->freeGold() < GameConstants::HERO_GOLD_COST)
return false;
break;
}

View File

@ -126,21 +126,33 @@ struct SectorMap
int3 findFirstVisitableTile(HeroPtr h, crint3 dst);
};
class VCAI : public CAdventureAI
class DLL_EXPORT VCAI : public CAdventureAI
{
public:
//internal methods for town development
//TODO: refactor to separate class BuildManager
//try build an unbuilt structure in maxDays at most (0 = indefinite)
/*bool canBuildStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays=7);*/
bool tryBuildStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays = 7) const;
//try build anything in given town, and execute resulting Goal if any
bool tryBuildStructure(const CGTownInstance * t);
bool tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
//try build first unbuilt structure
bool tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays = 7);
//try build ANY unbuilt structure
BuildingID canBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7) const;
bool tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7) const;
//try build first unbuilt structure
bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7) const;
bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
void buildStructure(const CGTownInstance * t, BuildingID building); //actually execute build operation
struct PotentialBuilding
{
BuildingID bid;
TResources price;
//days to build?
};
std::vector<PotentialBuilding> potentialBuildings; //what we can build in current town
friend class FuzzyHelper;
friend class ResourceManager;
std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
@ -161,8 +173,6 @@ public:
std::map<HeroPtr, std::shared_ptr<SectorMap>> cachedSectorMaps; //TODO: serialize? not necessary
TResources saving;
AIStatus status;
std::string battlename;
@ -182,6 +192,7 @@ public:
void tryRealize(Goals::DigAtTile & g);
void tryRealize(Goals::CollectRes & g);
void tryRealize(Goals::Build & g);
void tryRealize(Goals::BuyArmy & g);
void tryRealize(Goals::Invalid & g);
void tryRealize(Goals::AbstractGoal & g);
@ -189,71 +200,70 @@ public:
int3 explorationNewPoint(HeroPtr h);
int3 explorationDesperate(HeroPtr h);
bool isTileNotReserved(const CGHeroInstance * h, int3 t); //the tile is not occupied by allied hero and the object is not reserved
void recruitHero();
virtual std::string getBattleAIName() const override;
std::string getBattleAIName() const override;
virtual void init(std::shared_ptr<CCallback> CB) override;
virtual void yourTurn() override;
void init(std::shared_ptr<CCallback> CB) override;
void yourTurn() override;
virtual void heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
virtual void commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
virtual void showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
virtual void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
virtual void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
void commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
void showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
void showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
virtual void saveGame(BinarySerializer & h, const int version) override; //saving
virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading
virtual void finish() override;
void saveGame(BinarySerializer & h, const int version) override; //saving
void loadGame(BinaryDeserializer & h, const int version) override; //loading
void finish() override;
virtual void availableCreaturesChanged(const CGDwelling * town) override;
virtual void heroMoved(const TryMoveHero & details) override;
virtual void heroInGarrisonChange(const CGTownInstance * town) override;
virtual void centerView(int3 pos, int focusTime) override;
virtual void tileHidden(const std::unordered_set<int3, ShashInt3> & pos) override;
virtual void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
virtual void artifactAssembled(const ArtifactLocation & al) override;
virtual void showTavernWindow(const CGObjectInstance * townOrTavern) override;
virtual void showThievesGuildWindow(const CGObjectInstance * obj) override;
virtual void playerBlocked(int reason, bool start) override;
virtual void showPuzzleMap() override;
virtual void showShipyardDialog(const IShipyard * obj) override;
virtual void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
virtual void artifactPut(const ArtifactLocation & al) override;
virtual void artifactRemoved(const ArtifactLocation & al) override;
virtual void artifactDisassembled(const ArtifactLocation & al) override;
virtual void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
virtual void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
virtual void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
virtual void tileRevealed(const std::unordered_set<int3, ShashInt3> & pos) override;
virtual void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
virtual void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
virtual void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;
virtual void heroMovePointsChanged(const CGHeroInstance * hero) override;
virtual void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
virtual void newObject(const CGObjectInstance * obj) override;
virtual void showHillFortWindow(const CGObjectInstance * object, const CGHeroInstance * visitor) override;
virtual void playerBonusChanged(const Bonus & bonus, bool gain) override;
virtual void heroCreated(const CGHeroInstance *) override;
virtual void advmapSpellCast(const CGHeroInstance * caster, int spellID) override;
virtual void showInfoDialog(const std::string & text, const std::vector<Component> & components, int soundID) override;
virtual void requestRealized(PackageApplied * pa) override;
virtual void receivedResource() override;
virtual void objectRemoved(const CGObjectInstance * obj) override;
virtual void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
virtual void heroManaPointsChanged(const CGHeroInstance * hero) override;
virtual void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
virtual void battleResultsApplied() override;
virtual void objectPropertyChanged(const SetObjectProperty * sop) override;
virtual void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
virtual void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
virtual void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
void availableCreaturesChanged(const CGDwelling * town) override;
void heroMoved(const TryMoveHero & details) override;
void heroInGarrisonChange(const CGTownInstance * town) override;
void centerView(int3 pos, int focusTime) override;
void tileHidden(const std::unordered_set<int3, ShashInt3> & pos) override;
void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
void artifactAssembled(const ArtifactLocation & al) override;
void showTavernWindow(const CGObjectInstance * townOrTavern) override;
void showThievesGuildWindow(const CGObjectInstance * obj) override;
void playerBlocked(int reason, bool start) override;
void showPuzzleMap() override;
void showShipyardDialog(const IShipyard * obj) override;
void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
void artifactPut(const ArtifactLocation & al) override;
void artifactRemoved(const ArtifactLocation & al) override;
void artifactDisassembled(const ArtifactLocation & al) override;
void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
void tileRevealed(const std::unordered_set<int3, ShashInt3> & pos) override;
void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
void heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) override;
void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level) override;
void heroMovePointsChanged(const CGHeroInstance * hero) override;
void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
void newObject(const CGObjectInstance * obj) override;
void showHillFortWindow(const CGObjectInstance * object, const CGHeroInstance * visitor) override;
void playerBonusChanged(const Bonus & bonus, bool gain) override;
void heroCreated(const CGHeroInstance *) override;
void advmapSpellCast(const CGHeroInstance * caster, int spellID) override;
void showInfoDialog(const std::string & text, const std::vector<Component> & components, int soundID) override;
void requestRealized(PackageApplied * pa) override;
void receivedResource() override;
void objectRemoved(const CGObjectInstance * obj) override;
void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
void heroManaPointsChanged(const CGHeroInstance * hero) override;
void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
void battleResultsApplied() override;
void objectPropertyChanged(const SetObjectProperty * sop) override;
void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
virtual void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) override;
virtual void battleEnd(const BattleResult * br) override;
void makeTurn();
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) override;
void battleEnd(const BattleResult * br) override;
void makeTurn();
void makeTurnInternal();
void performTypicalActions();
@ -269,7 +279,6 @@ public:
void recruitHero(const CGTownInstance * t, bool throwing = false);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, SectorMap & sm);
void buildStructure(const CGTownInstance * t) const;
//void recruitCreatures(const CGTownInstance * t);
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
bool canGetArmy(const CGHeroInstance * h, const CGHeroInstance * source); //can we get any better stacks from other hero?
@ -299,7 +308,7 @@ public:
void validateVisitableObjs();
void retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned = false) const;
void retrieveVisitableObjs();
std::vector<const CGObjectInstance *> getFlaggedObjects() const;
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
const CGObjectInstance * lookForArt(int aid) const;
bool isAccessible(const int3 & pos);
@ -317,9 +326,6 @@ public:
bool canAct(HeroPtr h) const;
std::vector<HeroPtr> getUnblockedHeroes() const;
HeroPtr primaryHero() const;
TResources freeResources() const; //owned resources minus gold reserve
TResources estimateIncome() const;
bool containsSavedRes(const TResources & cost) const;
void checkHeroArmy(HeroPtr h);
void requestSent(const CPackForServer * pack, int requestID) override;
@ -406,7 +412,11 @@ public:
h & visitableObjs;
h & alreadyVisited;
h & reservedObjs;
h & saving;
if (version < 788 && !h.saving)
{
TResources saving;
h & saving; //mind the ambiguity
}
h & status;
h & battlename;
h & heroesUnableToExplore;

View File

@ -166,7 +166,7 @@ bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
return true;
}
int CBattleCallback::battleMakeAction(BattleAction* action)
int CBattleCallback::battleMakeAction(const BattleAction * action)
{
assert(action->actionType == EActionType::HERO_SPELL);
MakeCustomAction mca(*action);

View File

@ -36,8 +36,8 @@ public:
bool waitTillRealize; //if true, request functions will return after they are realized by server
bool unlockGsWhenWaiting;//if true after sending each request, gs mutex will be unlocked so the changes can be applied; NOTICE caller must have gs mx locked prior to any call to actiob callback!
//battle
virtual int battleMakeAction(BattleAction* action)=0;//for casting spells by hero - DO NOT use it for moving active stack
virtual bool battleMakeTacticAction(BattleAction * action) =0; // performs tactic phase actions
virtual int battleMakeAction(const BattleAction * action) = 0;//for casting spells by hero - DO NOT use it for moving active stack
virtual bool battleMakeTacticAction(BattleAction * action) = 0; // performs tactic phase actions
};
class IGameActionCallback
@ -88,7 +88,7 @@ protected:
public:
CBattleCallback(boost::optional<PlayerColor> Player, CClient *C);
int battleMakeAction(BattleAction* action) override;//for casting spells by hero - DO NOT use it for moving active stack
int battleMakeAction(const BattleAction * action) override;//for casting spells by hero - DO NOT use it for moving active stack
bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions
friend class CCallback;

View File

@ -46,7 +46,7 @@ set(VCMI_VERSION_PATCH 0)
option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF)
option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
option(ENABLE_TEST "Enable compilation of unit tests" ON)
option(ENABLE_TEST "Enable compilation of unit tests" OFF)
option(ENABLE_PCH "Enable compilation using precompiled headers" ON)
option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON)
option(ENABLE_DEBUG_CONSOLE "Enable debug console for Windows builds" ON)

View File

@ -57,7 +57,7 @@ SDL_Surface * BitmapHandler::loadH3PCX(ui8 * pcx, size_t size)
if (format==PCX8B)
{
ret = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0);
ret = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
it = 0xC;
for (int i=0; i<height; i++)
@ -89,7 +89,7 @@ SDL_Surface * BitmapHandler::loadH3PCX(ui8 * pcx, size_t size)
int gmask = 0x00ff00;
int rmask = 0xff0000;
#endif
ret = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, rmask, gmask, bmask, 0);
ret = SDL_CreateRGBSurface(0, width, height, 24, rmask, gmask, bmask, 0);
//it == 0xC;
for (int i=0; i<height; i++)

View File

@ -118,32 +118,13 @@ static void mainLoop();
void init()
{
CStopWatch tmh, pomtime;
CStopWatch tmh;
loadDLLClasses();
const_cast<CGameInfo*>(CGI)->setFromLib();
logGlobal->info("Initializing VCMI_Lib: %d ms", tmh.getDiff());
if(!settings["session"]["headless"].Bool())
{
pomtime.getDiff();
CCS->curh = new CCursorHandler();
graphics = new Graphics(); // should be before curh->init()
CCS->curh->initCursor();
CCS->curh->show();
logGlobal->info("Screen handler: %d ms", pomtime.getDiff());
pomtime.getDiff();
graphics->load();
logGlobal->info("\tMain graphics: %d ms", pomtime.getDiff());
logGlobal->info("Initializing game graphics: %d ms", tmh.getDiff());
CMessage::init();
logGlobal->info("Message handler: %d ms", tmh.getDiff());
}
}
static void prog_version()
@ -466,8 +447,10 @@ int main(int argc, char * argv[])
playIntro();
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderClear(mainRenderer);
SDL_RenderPresent(mainRenderer);
}
SDL_RenderPresent(mainRenderer);
#ifndef VCMI_NO_THREADED_LOAD
#ifdef VCMI_ANDROID // android loads the data quite slowly so we display native progressbar to prevent having only black screen for few seconds
{
@ -480,6 +463,27 @@ int main(int argc, char * argv[])
}
#endif // ANDROID
#endif // THREADED
if(!settings["session"]["headless"].Bool())
{
pomtime.getDiff();
CCS->curh = new CCursorHandler();
graphics = new Graphics(); // should be before curh->init()
CCS->curh->initCursor();
logGlobal->info("Screen handler: %d ms", pomtime.getDiff());
pomtime.getDiff();
graphics->load();//must be after Content loading but should be in main thread
logGlobal->info("Main graphics: %d ms", pomtime.getDiff());
CMessage::init();
logGlobal->info("Message handler: %d ms", pomtime.getDiff());
CCS->curh->show();
}
logGlobal->info("Initialization of VCMI (together): %d ms", total.getDiff());
session["autoSkip"].Bool() = vm.count("autoSkip");
@ -500,7 +504,7 @@ int main(int argc, char * argv[])
}
else
{
GH.curInt = CMainMenu::create();
GH.curInt = CMainMenu::create().get();
}
if(!settings["session"]["headless"].Bool())
@ -786,9 +790,9 @@ void processCommand(const std::string &message)
}
else if(cn == "gui")
{
for(const IShowActivatable *child : GH.listInt)
for(auto child : GH.listInt)
{
if(const CIntObject *obj = dynamic_cast<const CIntObject *>(child))
if(const CIntObject *obj = dynamic_cast<const CIntObject *>(child.get()))
printInfoAboutIntObject(obj, 0);
else
std::cout << typeid(*child).name() << std::endl;
@ -970,9 +974,9 @@ void processCommand(const std::string &message)
//plays intro, ends when intro is over or button has been pressed (handles events)
void playIntro()
{
if(CCS->videoh->openAndPlayVideo("3DOLOGO.SMK", 0, 1, screen, true, true))
if(CCS->videoh->openAndPlayVideo("3DOLOGO.SMK", 0, 1, true, true))
{
CCS->videoh->openAndPlayVideo("AZVS.SMK", 0, 1, screen, true, true);
CCS->videoh->openAndPlayVideo("AZVS.SMK", 0, 1, true, true);
}
}
@ -1044,18 +1048,6 @@ static void cleanupRenderer()
SDL_DestroyTexture(screenTexture);
screenTexture = nullptr;
}
if(nullptr != mainRenderer)
{
SDL_DestroyRenderer(mainRenderer);
mainRenderer = nullptr;
}
if(nullptr != mainWindow)
{
SDL_DestroyWindow(mainWindow);
mainWindow = nullptr;
}
}
static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIndex)
@ -1080,47 +1072,85 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
}
bool bufOnScreen = (screenBuf == screen);
bool realFullscreen = settings["video"]["realFullscreen"].Bool();
cleanupRenderer();
bool realFullscreen = settings["video"]["realFullscreen"].Bool();
#ifdef VCMI_ANDROID
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN);
#else
if(fullscreen)
if(nullptr == mainWindow)
{
if(realFullscreen)
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), w, h, SDL_WINDOW_FULLSCREEN);
else //in windowed full-screen mode use desktop resolution
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
#ifdef VCMI_ANDROID
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN);
#else
if(fullscreen)
{
if(realFullscreen)
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), w, h, SDL_WINDOW_FULLSCREEN);
else //in windowed full-screen mode use desktop resolution
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
}
else
{
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), w, h, 0);
}
#endif
if(nullptr == mainWindow)
{
throw std::runtime_error("Unable to create window\n");
}
//create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible
mainRenderer = SDL_CreateRenderer(mainWindow,preferredDriverIndex,0);
if(nullptr == mainRenderer)
{
throw std::runtime_error("Unable to create renderer\n");
}
SDL_RendererInfo info;
SDL_GetRendererInfo(mainRenderer, &info);
logGlobal->info("Created renderer %s", info.name);
}
else
{
mainWindow = SDL_CreateWindow(NAME.c_str(), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex),SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), w, h, 0);
}
#ifndef VCMI_ANDROID
if(fullscreen)
{
if(realFullscreen)
{
SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN);
SDL_DisplayMode mode;
SDL_GetDesktopDisplayMode(displayIndex, &mode);
mode.w = w;
mode.h = h;
SDL_SetWindowDisplayMode(mainWindow, &mode);
}
else
{
SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex));
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
}
else
{
SDL_SetWindowFullscreen(mainWindow, 0);
SDL_SetWindowSize(mainWindow, w, h);
SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex), SDL_WINDOWPOS_CENTERED_DISPLAY(displayIndex));
}
#endif
if(nullptr == mainWindow)
{
throw std::runtime_error("Unable to create window\n");
}
//create first available renderer if preferred not set. Use no flags, so HW accelerated will be preferred but SW renderer also will possible
mainRenderer = SDL_CreateRenderer(mainWindow,preferredDriverIndex,0);
if(nullptr == mainRenderer)
{
throw std::runtime_error("Unable to create renderer\n");
}
SDL_RendererInfo info;
SDL_GetRendererInfo(mainRenderer, &info);
logGlobal->info("Created renderer %s", info.name);
if(!(fullscreen && realFullscreen))
{
SDL_RenderSetLogicalSize(mainRenderer, w, h);
@ -1270,7 +1300,7 @@ static void handleEvent(SDL_Event & ev)
};
if(epilogue.hasPrologEpilog)
{
GH.pushInt(new CPrologEpilogVideo(epilogue, finisher));
GH.pushIntT<CPrologEpilogVideo>(epilogue, finisher);
}
else
{
@ -1306,6 +1336,13 @@ static void handleEvent(SDL_Event & ev)
}
return;
}
//preprocessing
if(ev.type == SDL_MOUSEMOTION)
{
CCS->curh->cursorMove(ev.motion.x, ev.motion.y);
}
{
boost::unique_lock<boost::mutex> lock(eventsM);
events.push(ev);
@ -1349,6 +1386,19 @@ void handleQuit(bool ask)
if(!settings["session"]["headless"].Bool())
{
cleanupRenderer();
if(nullptr != mainRenderer)
{
SDL_DestroyRenderer(mainRenderer);
mainRenderer = nullptr;
}
if(nullptr != mainWindow)
{
SDL_DestroyWindow(mainWindow);
mainWindow = nullptr;
}
SDL_Quit();
}

View File

@ -97,7 +97,7 @@ void CMessage::dispose()
SDL_Surface * CMessage::drawDialogBox(int w, int h, PlayerColor playerColor)
{
//prepare surface
SDL_Surface * ret = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
SDL_Surface * ret = SDL_CreateRGBSurface(0, w, h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
for (int i=0; i<w; i+=background->w)//background
{
for (int j=0; j<h; j+=background->h)

View File

@ -19,16 +19,13 @@ class CComponent;
/// Class which draws formatted text messages and generates chat windows
class CMessage
{
public:
//Function usd only in CMessage.cpp
static std::pair<int,int> getMaxSizes(std::vector<std::vector<SDL_Surface*> > * txtg, int fontHeight);
/// Draw border on exiting surface
static void drawBorder(PlayerColor playerColor, SDL_Surface * ret, int w, int h, int x=0, int y=0);
/// Draw simple dialog box (borders and background only)
static SDL_Surface * drawDialogBox(int w, int h, PlayerColor playerColor = PlayerColor(1));
public:
/// Draw border on exiting surface
static void drawBorder(PlayerColor playerColor, SDL_Surface * ret, int w, int h, int x=0, int y=0);
static void drawIWindow(CInfoWindow * ret, std::string text, PlayerColor player);
/// split text in lines

View File

@ -121,8 +121,8 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player)
playerID=Player;
human=true;
currentSelection = nullptr;
castleInt = nullptr;
battleInt = nullptr;
castleInt = nullptr;
makingTurn = false;
showingDialog = new CondSh<bool>(false);
cingconsole = new CInGameConsole();
@ -150,9 +150,7 @@ void CPlayerInterface::init(std::shared_ptr<CCallback> CB)
initializeHeroTownList();
// always recreate advmap interface to avoid possible memory-corruption bugs
if (adventureInt)
delete adventureInt;
adventureInt = new CAdvMapInt();
adventureInt.reset(new CAdvMapInt());
}
void CPlayerInterface::yourTurn()
{
@ -464,11 +462,13 @@ void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
}
void CPlayerInterface::openTownWindow(const CGTownInstance * town)
{
if (castleInt)
if(castleInt)
castleInt->close();
castleInt = nullptr;
castleInt = new CCastleInterface(town);
GH.pushInt(castleInt);
auto newCastleInt = std::make_shared<CCastleInterface>(town);
GH.pushInt(newCastleInt);
}
int3 CPlayerInterface::repairScreenPos(int3 pos)
@ -496,7 +496,7 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int
EVENT_HANDLER_CALLED_BY_CLIENT;
if (which == 4)
{
if (CAltarWindow *ctw = dynamic_cast<CAltarWindow *>(GH.topInt()))
if (CAltarWindow *ctw = dynamic_cast<CAltarWindow *>(GH.topInt().get()))
ctw->setExpToLevel();
}
else if (which < GameConstants::PRIMARY_SKILLS) //no need to redraw infowin if this is experience (exp is treated as prim skill with id==4)
@ -506,7 +506,7 @@ void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int
void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
CUniversityWindow* cuw = dynamic_cast<CUniversityWindow*>(GH.topInt());
CUniversityWindow* cuw = dynamic_cast<CUniversityWindow*>(GH.topInt().get());
if (cuw) //university window is open
{
GH.totalRedraw();
@ -529,7 +529,7 @@ void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
void CPlayerInterface::receivedResource()
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if (CMarketplaceWindow *mw = dynamic_cast<CMarketplaceWindow *>(GH.topInt()))
if (CMarketplaceWindow *mw = dynamic_cast<CMarketplaceWindow *>(GH.topInt().get()))
mw->resourceChanged();
GH.totalRedraw();
@ -540,21 +540,21 @@ void CPlayerInterface::heroGotLevel(const CGHeroInstance *hero, PrimarySkill::Pr
EVENT_HANDLER_CALLED_BY_CLIENT;
waitWhileDialog();
CCS->soundh->playSound(soundBase::heroNewLevel);
CLevelWindow *lw = new CLevelWindow(hero,pskill,skills,
[=](ui32 selection){ cb->selectionMade(selection, queryID); });
GH.pushInt(lw);
GH.pushIntT<CLevelWindow>(hero, pskill, skills, [=](ui32 selection)
{
cb->selectionMade(selection, queryID);
});
}
void CPlayerInterface::commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
waitWhileDialog();
CCS->soundh->playSound(soundBase::heroNewLevel);
GH.pushInt(new CStackWindow(commander, skills, [=](ui32 selection)
GH.pushIntT<CStackWindow>(commander, skills, [=](ui32 selection)
{
cb->selectionMade(selection, queryID);
}));
});
}
void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
@ -578,17 +578,17 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
adventureInt->heroList.update();
adventureInt->updateNextHero(nullptr);
if (CCastleInterface *c = castleInt)
if(castleInt)
{
c->garr->selectSlot(nullptr);
c->garr->setArmy(town->getUpperArmy(), 0);
c->garr->setArmy(town->visitingHero, 1);
c->garr->recreateSlots();
c->heroes->update();
castleInt->garr->selectSlot(nullptr);
castleInt->garr->setArmy(town->getUpperArmy(), 0);
castleInt->garr->setArmy(town->visitingHero, 1);
castleInt->garr->recreateSlots();
castleInt->heroes->update();
}
for (IShowActivatable *isa : GH.listInt)
for (auto isa : GH.listInt)
{
CKingdomInterface *ki = dynamic_cast<CKingdomInterface*>(isa);
CKingdomInterface *ki = dynamic_cast<CKingdomInterface*>(isa.get());
if (ki)
{
ki->townChanged(town);
@ -632,11 +632,11 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CGObjectInstance *> ob
for (auto & elem : GH.listInt)
{
CGarrisonHolder *cgh = dynamic_cast<CGarrisonHolder*>(elem);
CGarrisonHolder *cgh = dynamic_cast<CGarrisonHolder*>(elem.get());
if (cgh)
cgh->updateGarrisons();
if (CTradeWindow *cmw = dynamic_cast<CTradeWindow*>(elem))
if (CTradeWindow *cmw = dynamic_cast<CTradeWindow*>(elem.get()))
{
if (vstd::contains(objs, cmw->hero))
cmw->garrisonChanged();
@ -739,7 +739,7 @@ void CPlayerInterface::battleUnitsChanged(const std::vector<UnitChanges> & units
continue;
}
CCreatureAnimation * animation = iter->second;
auto animation = iter->second;
if(unit->alive() && animation->isDead())
animation->setType(CCreatureAnim::HOLDING);
@ -905,8 +905,7 @@ void CPlayerInterface::battleEnd(const BattleResult *br)
if (!battleInt)
{
auto resWindow = new CBattleResultWindow(*br, *this);
GH.pushInt(resWindow);
GH.pushIntT<CBattleResultWindow>(*br, *this);
// #1490 - during AI turn when quick combat is on, we need to display the message and wait for user to close it.
// Otherwise NewTurn causes freeze.
waitWhileDialog();
@ -1129,7 +1128,7 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector
{
return;
}
CInfoWindow *temp = CInfoWindow::create(text, playerID, components);
std::shared_ptr<CInfoWindow> temp = CInfoWindow::create(text, playerID, components);
if (makingTurn && GH.listInt.size() && LOCPLINT == this)
{
@ -1164,22 +1163,6 @@ void CPlayerInterface::showYesNoDialog(const std::string &text, CFunctionList<vo
CInfoWindow::showYesNoDialog(text, components, onYes, onNo, playerID);
}
void CPlayerInterface::showOkDialog(std::vector<Component> & components, const MetaString & text, const std::function<void()> & onOk)
{
boost::unique_lock<boost::recursive_mutex> un(*pim);
std::string str;
text.toString(str);
stopMovement();
showingDialog->setn(true);
std::vector<std::shared_ptr<CComponent>> intComps;
for (auto & component : components)
intComps.push_back(std::make_shared<CComponent>(component));
CInfoWindow::showOkDialog(str, intComps, onOk, playerID);
}
void CPlayerInterface::showBlockingDialog( const std::string &text, const std::vector<Component> &components, QueryID askID, const int soundID, bool selection, bool cancel )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
@ -1212,11 +1195,9 @@ void CPlayerInterface::showBlockingDialog( const std::string &text, const std::v
int charperline = 35;
if (pom.size() > 1)
charperline = 50;
auto temp = new CSelWindow(text, playerID, charperline, intComps, pom, askID);
GH.pushInt(temp);
GH.pushIntT<CSelWindow>(text, playerID, charperline, intComps, pom, askID);
intComps[0]->clickLeft(true, false);
}
}
void CPlayerInterface::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
@ -1256,13 +1237,12 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
for(auto item : objects)
tempList.push_back(item.getNum());
CComponent * localIconC = new CComponent(icon);
CComponent localIconC(icon);
std::shared_ptr<CIntObject> localIcon = localIconC->image;
localIconC->removeChild(localIcon.get(), false);
delete localIconC;
std::shared_ptr<CIntObject> localIcon = localIconC.image;
localIconC.removeChild(localIcon.get(), false);
CObjectListWindow * wnd = new CObjectListWindow(tempList, localIcon, localTitle, localDescription, selectCallback);
std::shared_ptr<CObjectListWindow> wnd = std::make_shared<CObjectListWindow>(tempList, localIcon, localTitle, localDescription, selectCallback);
wnd->onExit = cancelCallback;
GH.pushInt(wnd);
}
@ -1289,7 +1269,7 @@ void CPlayerInterface::tileHidden(const std::unordered_set<int3, ShashInt3> &pos
void CPlayerInterface::openHeroWindow(const CGHeroInstance *hero)
{
boost::unique_lock<boost::recursive_mutex> un(*pim);
GH.pushInt(new CHeroWindow(hero));
GH.pushIntT<CHeroWindow>(hero);
}
void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
@ -1297,13 +1277,13 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
EVENT_HANDLER_CALLED_BY_CLIENT;
if (const CGTownInstance * townObj = dynamic_cast<const CGTownInstance*>(town))
{
CFortScreen *fs = dynamic_cast<CFortScreen*>(GH.topInt());
CFortScreen *fs = dynamic_cast<CFortScreen*>(GH.topInt().get());
if (fs)
fs->creaturesChanged();
for (IShowActivatable *isa : GH.listInt)
for(auto isa : GH.listInt)
{
CKingdomInterface *ki = dynamic_cast<CKingdomInterface*>(isa);
CKingdomInterface *ki = dynamic_cast<CKingdomInterface*>(isa.get());
if (ki && townObj)
ki->townChanged(townObj);
}
@ -1311,7 +1291,7 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
else if (GH.listInt.size() && (town->ID == Obj::CREATURE_GENERATOR1
|| town->ID == Obj::CREATURE_GENERATOR4 || town->ID == Obj::WAR_MACHINE_FACTORY))
{
CRecruitmentWindow *crw = dynamic_cast<CRecruitmentWindow*>(GH.topInt());
CRecruitmentWindow *crw = dynamic_cast<CRecruitmentWindow*>(GH.topInt().get());
if (crw && crw->dwelling == town)
crw->availableCreaturesChanged();
}
@ -1434,7 +1414,7 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
waitForAllDialogs();
auto cgw = new CGarrisonWindow(up,down,removableUnits);
auto cgw = std::make_shared<CGarrisonWindow>(up, down, removableUnits);
cgw->quit->addCallback(onEnd);
GH.pushInt(cgw);
}
@ -1497,7 +1477,7 @@ void CPlayerInterface::requestRealized( PackageApplied *pa )
void CPlayerInterface::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
GH.pushInt(new CExchangeWindow(hero1, hero2, query));
GH.pushIntT<CExchangeWindow>(hero1, hero2, query);
}
void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
@ -1551,9 +1531,11 @@ void CPlayerInterface::showRecruitmentDialog(const CGDwelling *dwelling, const C
{
EVENT_HANDLER_CALLED_BY_CLIENT;
waitWhileDialog();
auto recruitCb = [=](CreatureID id, int count){ LOCPLINT->cb->recruitCreatures(dwelling, dst, id, count, -1); };
CRecruitmentWindow *cr = new CRecruitmentWindow(dwelling, level, dst, recruitCb);
GH.pushInt(cr);
auto recruitCb = [=](CreatureID id, int count)
{
LOCPLINT->cb->recruitCreatures(dwelling, dst, id, count, -1);
};
GH.pushIntT<CRecruitmentWindow>(dwelling, level, dst, recruitCb);
}
void CPlayerInterface::waitWhileDialog(bool unlockPim)
@ -1576,8 +1558,7 @@ void CPlayerInterface::showShipyardDialog(const IShipyard *obj)
auto state = obj->shipyardStatus();
std::vector<si32> cost;
obj->getBoatCost(cost);
CShipyardWindow *csw = new CShipyardWindow(cost, state, obj->getBoatType(), [=](){ cb->buildBoat(obj); });
GH.pushInt(csw);
GH.pushIntT<CShipyardWindow>(cost, state, obj->getBoatType(), [=](){ cb->buildBoat(obj); });
}
void CPlayerInterface::newObject( const CGObjectInstance * obj )
@ -2159,7 +2140,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
adventureInt->deactivate();
if (GH.topInt() == adventureInt)
GH.popInt(adventureInt);
vstd::clear_pointer(adventureInt);
adventureInt.reset();
}
}
@ -2201,7 +2182,7 @@ void CPlayerInterface::showPuzzleMap()
double ratio = 0;
int3 grailPos = cb->getGrailPos(&ratio);
GH.pushInt(new CPuzzleWindow(grailPos, ratio));
GH.pushIntT<CPuzzleWindow>(grailPos, ratio);
}
void CPlayerInterface::viewWorldMap()
@ -2213,8 +2194,8 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(dynamic_cast<CSpellWindow *>(GH.topInt()))
GH.popIntTotally(GH.topInt());
if(dynamic_cast<CSpellWindow *>(GH.topInt().get()))
GH.popInts(1);
if(spellID == SpellID::FLY || spellID == SpellID::WATER_WALK)
eraseCurrentPathOf(caster, false);
@ -2287,7 +2268,7 @@ void CPlayerInterface::acceptTurn()
if (settings["session"]["autoSkip"].Bool())
{
centerView = false;
while(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt()))
while(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
iw->close();
}
waitWhileDialog();
@ -2328,7 +2309,7 @@ void CPlayerInterface::acceptTurn()
if(settings["session"]["autoSkip"].Bool() && !LOCPLINT->shiftPressed())
{
if(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt()))
if(CInfoWindow *iw = dynamic_cast<CInfoWindow *>(GH.topInt().get()))
iw->close();
adventureInt->fendTurn();
@ -2425,54 +2406,51 @@ void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInsta
{
//EEMarketMode mode = market->availableModes().front();
if (market->allowsTrade(EMarketMode::ARTIFACT_EXP) && visitor->getAlignment() != EAlignment::EVIL)
GH.pushInt(new CAltarWindow(market, visitor, EMarketMode::ARTIFACT_EXP));
GH.pushIntT<CAltarWindow>(market, visitor, EMarketMode::ARTIFACT_EXP);
else if (market->allowsTrade(EMarketMode::CREATURE_EXP) && visitor->getAlignment() != EAlignment::GOOD)
GH.pushInt(new CAltarWindow(market, visitor, EMarketMode::CREATURE_EXP));
GH.pushIntT<CAltarWindow>(market, visitor, EMarketMode::CREATURE_EXP);
}
else
GH.pushInt(new CMarketplaceWindow(market, visitor, market->availableModes().front()));
{
GH.pushIntT<CMarketplaceWindow>(market, visitor, market->availableModes().front());
}
}
void CPlayerInterface::showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
auto cuw = new CUniversityWindow(visitor, market);
GH.pushInt(cuw);
GH.pushIntT<CUniversityWindow>(visitor, market);
}
void CPlayerInterface::showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
auto chfw = new CHillFortWindow(visitor, object);
GH.pushInt(chfw);
GH.pushIntT<CHillFortWindow>(visitor, object);
}
void CPlayerInterface::availableArtifactsChanged(const CGBlackMarket * bm)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if (CMarketplaceWindow *cmw = dynamic_cast<CMarketplaceWindow*>(GH.topInt()))
if (CMarketplaceWindow *cmw = dynamic_cast<CMarketplaceWindow*>(GH.topInt().get()))
cmw->artifactsChanged(false);
}
void CPlayerInterface::showTavernWindow(const CGObjectInstance *townOrTavern)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
auto tv = new CTavernWindow(townOrTavern);
GH.pushInt(tv);
GH.pushIntT<CTavernWindow>(townOrTavern);
}
void CPlayerInterface::showThievesGuildWindow (const CGObjectInstance * obj)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
auto tgw = new CThievesGuildWindow(obj);
GH.pushInt(tgw);
GH.pushIntT<CThievesGuildWindow>(obj);
}
void CPlayerInterface::showQuestLog()
{
EVENT_HANDLER_CALLED_BY_CLIENT;
CQuestLog * ql = new CQuestLog (LOCPLINT->cb->getMyQuests());
GH.pushInt (ql);
GH.pushIntT<CQuestLog>(LOCPLINT->cb->getMyQuests());
}
void CPlayerInterface::showShipyardDialogOrProblemPopup(const IShipyard *obj)
@ -2529,9 +2507,9 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->infoBar.showSelection();
for (IShowActivatable *isa : GH.listInt)
for(auto isa : GH.listInt)
{
auto artWin = dynamic_cast<CArtifactHolder*>(isa);
auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
if (artWin)
artWin->artifactRemoved(al);
}
@ -2541,9 +2519,9 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->infoBar.showSelection();
for (IShowActivatable *isa : GH.listInt)
for(auto isa : GH.listInt)
{
auto artWin = dynamic_cast<CArtifactHolder*>(isa);
auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
if (artWin)
artWin->artifactMoved(src, dst);
}
@ -2554,9 +2532,9 @@ void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->infoBar.showSelection();
for (IShowActivatable *isa : GH.listInt)
for(auto isa : GH.listInt)
{
auto artWin = dynamic_cast<CArtifactHolder*>(isa);
auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
if (artWin)
artWin->artifactAssembled(al);
}
@ -2566,9 +2544,9 @@ void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
adventureInt->infoBar.showSelection();
for (IShowActivatable *isa : GH.listInt)
for(auto isa : GH.listInt)
{
auto artWin = dynamic_cast<CArtifactHolder*>(isa);
auto artWin = dynamic_cast<CArtifactHolder*>(isa.get());
if (artWin)
artWin->artifactDisassembled(al);
}
@ -2585,7 +2563,7 @@ void CPlayerInterface::playerStartsTurn(PlayerColor player)
else
{
adventureInt->infoBar.showSelection();
while (GH.listInt.front() != adventureInt && !dynamic_cast<CInfoWindow*>(GH.listInt.front())) //don't remove dialogs that expect query answer
while (GH.listInt.front() != adventureInt && !dynamic_cast<CInfoWindow*>(GH.listInt.front().get())) //don't remove dialogs that expect query answer
GH.popInts(1);
}
@ -2820,7 +2798,7 @@ void CPlayerInterface::updateAmbientSounds(bool resetAll)
CCS->soundh->ambientStopAllChannels();
return;
}
else if(!dynamic_cast<CAdvMapInt *>(GH.topInt()))
else if(!dynamic_cast<CAdvMapInt *>(GH.topInt().get()))
{
return;
}

View File

@ -82,7 +82,7 @@ public:
std::shared_ptr<CCallback> cb; //to communicate with engine
const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr)
std::list<CInfoWindow *> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!)
std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
std::vector<const CGTownInstance *> towns; //our towns on the adventure map
@ -217,7 +217,6 @@ public:
void showInfoDialog(const std::string &text, std::shared_ptr<CComponent> component);
void showInfoDialog(const std::string &text, const std::vector<std::shared_ptr<CComponent>> & components = std::vector<std::shared_ptr<CComponent>>(), int soundID = 0);
void showInfoDialogAndWait(std::vector<Component> & components, const MetaString & text);
void showOkDialog(std::vector<Component> & components, const MetaString & text, const std::function<void()> & onOk);
void showYesNoDialog(const std::string &text, CFunctionList<void()> onYes, CFunctionList<void()> onNo, const std::vector<std::shared_ptr<CComponent>> & components = std::vector<std::shared_ptr<CComponent>>());
void stopMovement();

View File

@ -494,12 +494,12 @@ void CServerHandler::endGameplay(bool closeConnection, bool restart)
{
if(CMM)
{
GH.curInt = CMM;
GH.curInt = CMM.get();
CMM->enable();
}
else
{
GH.curInt = CMainMenu::create();
GH.curInt = CMainMenu::create().get();
}
}
}
@ -566,7 +566,7 @@ void CServerHandler::debugStartTest(std::string filename, bool save)
else
startLocalServerAndConnect();
while(!settings["session"]["headless"].Bool() && !dynamic_cast<CLobbyScreen *>(GH.topInt()))
while(!settings["session"]["headless"].Bool() && !dynamic_cast<CLobbyScreen *>(GH.topInt().get()))
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
while(!mi || mapInfo->fileURI != CSH->mi->fileURI)
{

View File

@ -396,7 +396,7 @@ void CVideoPlayer::close()
}
// Plays a video. Only works for overlays.
bool CVideoPlayer::playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey)
bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
{
// Note: either the windows player or the linux player is
// broken. Compensate here until the bug is found.
@ -407,11 +407,10 @@ bool CVideoPlayer::playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey)
while(nextFrame())
{
if(stopOnKey && keyDown())
return false;
SDL_RenderCopy(mainRenderer, texture, NULL, &pos);
SDL_RenderCopy(mainRenderer, texture, nullptr, &pos);
SDL_RenderPresent(mainRenderer);
// Wait 3 frames
@ -423,10 +422,10 @@ bool CVideoPlayer::playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey)
return true;
}
bool CVideoPlayer::openAndPlayVideo(std::string name, int x, int y, SDL_Surface *dst, bool stopOnKey, bool scale)
bool CVideoPlayer::openAndPlayVideo(std::string name, int x, int y, bool stopOnKey, bool scale)
{
open(name, false, true, scale);
bool ret = playVideo(x, y, dst, stopOnKey);
bool ret = playVideo(x, y, stopOnKey);
close();
return ret;
}

View File

@ -31,7 +31,7 @@ public:
std::string fname; //name of current video file (empty if idle)
virtual void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true){}
virtual bool openAndPlayVideo(std::string name, int x, int y, SDL_Surface *dst, bool stopOnKey = false, bool scale = false)
virtual bool openAndPlayVideo(std::string name, int x, int y, bool stopOnKey = false, bool scale = false)
{
return false;
}
@ -111,7 +111,7 @@ class CVideoPlayer : public IMainVideoPlayer
int refreshCount;
bool doLoop; // loop through video
bool playVideo(int x, int y, SDL_Surface *dst, bool stopOnKey);
bool playVideo(int x, int y, bool stopOnKey);
bool open(std::string fname, bool loop, bool useOverlay = false, bool scale = false);
public:
@ -128,7 +128,7 @@ public:
void update(int x, int y, SDL_Surface *dst, bool forceRedraw, bool update = true) override; //moves to next frame if appropriate, and blits it or blits only if redraw parameter is set true
// Opens video, calls playVideo, closes video; returns playVideo result (if whole video has been played)
bool openAndPlayVideo(std::string name, int x, int y, SDL_Surface *dst, bool stopOnKey = false, bool scale = false) override;
bool openAndPlayVideo(std::string name, int x, int y, bool stopOnKey = false, bool scale = false) override;
//TODO:
bool wait() override {return false;};

View File

@ -491,14 +491,11 @@ void CClient::battleStarted(const BattleInfo * info)
if(!settings["session"]["headless"].Bool())
{
Rect battleIntRect((screen->w - 800)/2, (screen->h - 600)/2, 800, 600);
if(!!att || !!def)
{
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
Rect((screen->w - 800)/2,
(screen->h - 600)/2, 800, 600), att, def);
GH.pushInt(bi);
GH.pushIntT<CBattleInterface>(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, battleIntRect, att, def);
}
else if(settings["session"]["spectate"].Bool() && !settings["session"]["spectate-skip-battle"].Bool())
{
@ -506,11 +503,7 @@ void CClient::battleStarted(const BattleInfo * info)
auto spectratorInt = std::dynamic_pointer_cast<CPlayerInterface>(playerint[PlayerColor::SPECTATOR]);
spectratorInt->cb->setBattle(info);
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
Rect((screen->w - 800) / 2,
(screen->h - 600) / 2, 800, 600), att, def, spectratorInt);
GH.pushInt(bi);
GH.pushIntT<CBattleInterface>(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero, battleIntRect, att, def, spectratorInt);
}
}

View File

@ -35,7 +35,7 @@ bool LobbyClientConnected::applyOnLobbyHandler(CServerHandler * handler)
{
handler->c->connectionID = clientId;
if(!settings["session"]["headless"].Bool())
GH.pushInt(new CLobbyScreen(static_cast<ESelectionScreen>(handler->screenType)));
GH.pushIntT<CLobbyScreen>(static_cast<ESelectionScreen>(handler->screenType));
handler->state = EClientState::LOBBY;
return true;
}
@ -60,7 +60,7 @@ bool LobbyClientDisconnected::applyOnLobbyHandler(CServerHandler * handler)
void LobbyClientDisconnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
{
GH.popIntTotally(lobby);
GH.popInts(1);
}
void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
@ -114,7 +114,7 @@ bool LobbyStartGame::applyOnLobbyHandler(CServerHandler * handler)
void LobbyStartGame::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
{
GH.pushInt(new CLoadingScreen(std::bind(&CServerHandler::startGameplay, handler)));
GH.pushIntT<CLoadingScreen>(std::bind(&CServerHandler::startGameplay, handler));
}
bool LobbyUpdateState::applyOnLobbyHandler(CServerHandler * handler)
@ -128,7 +128,7 @@ void LobbyUpdateState::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler *
{
if(!lobby->bonusSel && handler->si->campState && handler->state == EClientState::LOBBY_CAMPAIGN)
{
lobby->bonusSel = new CBonusSelection();
lobby->bonusSel = std::make_shared<CBonusSelection>();
GH.pushInt(lobby->bonusSel);
}

View File

@ -207,7 +207,7 @@ bool CDefenceAnimation::init()
if (!rangedAttack && getMyAnimType() != CCreatureAnim::DEFENCE)
{
float frameLength = AnimationControls::getCreatureAnimationSpeed(
stack->getCreature(), owner->creAnims[stack->ID], getMyAnimType());
stack->getCreature(), owner->creAnims[stack->ID].get(), getMyAnimType());
timeToWait = myAnim->framesInGroup(getMyAnimType()) * frameLength / 2;

View File

@ -39,7 +39,7 @@ public:
class CBattleStackAnimation : public CBattleAnimation
{
public:
CCreatureAnimation * myAnim; //animation for our stack, managed by CBattleInterface
std::shared_ptr<CCreatureAnimation> myAnim; //animation for our stack, managed by CBattleInterface
const CStack * stack; //id of stack whose animation it is
CBattleStackAnimation(CBattleInterface * _owner, const CStack * _stack);

View File

@ -47,7 +47,7 @@
CondSh<bool> CBattleInterface::animsAreDisplayed(false);
CondSh<BattleAction *> CBattleInterface::givenCommand(nullptr);
static void onAnimationFinished(const CStack *stack, CCreatureAnimation *anim)
static void onAnimationFinished(const CStack *stack, std::shared_ptr<CCreatureAnimation> anim)
{
if (anim->isIdle())
{
@ -92,36 +92,37 @@ void CBattleInterface::addNewAnim(CBattleAnimation *anim)
}
CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2,
const CGHeroInstance *hero1, const CGHeroInstance *hero2,
const SDL_Rect & myRect,
std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt)
: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1),
currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellToCast(nullptr), sp(nullptr),
creatureSpellToCast(-1),
siegeH(nullptr), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0),
myTurn(false), resWindow(nullptr), moveStarted(false), moveSoundHander(-1), bresult(nullptr)
const CGHeroInstance *hero1, const CGHeroInstance *hero2,
const SDL_Rect & myRect,
std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt)
: background(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1),
currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellToCast(nullptr), sp(nullptr),
creatureSpellToCast(-1),
siegeH(nullptr), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0),
myTurn(false), moveStarted(false), moveSoundHander(-1), bresult(nullptr)
{
OBJ_CONSTRUCTION;
if(spectatorInt)
{
curInt = spectatorInt;
}
else if(!curInt)
{
//May happen when we are defending during network MP game -> attacker interface is just not present
curInt = defenderInt;
}
animsAreDisplayed.setn(false);
pos = myRect;
strongInterest = true;
givenCommand.setn(nullptr);
//hot-seat -> check tactics for both players (defender may be local human)
if (attackerInt && attackerInt->cb->battleGetTacticDist())
if(attackerInt && attackerInt->cb->battleGetTacticDist())
tacticianInterface = attackerInt;
else if (defenderInt && defenderInt->cb->battleGetTacticDist())
else if(defenderInt && defenderInt->cb->battleGetTacticDist())
tacticianInterface = defenderInt;
//if we found interface of player with tactics, then enter tactics mode
@ -138,7 +139,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
else
embedQueue = screen->h < 700 || queueSize == "small";
queue = new CStackQueue(embedQueue, this);
queue = std::make_shared<CStackQueue>(embedQueue, this);
if(!embedQueue)
{
if (settings["battle"]["showQueue"].Bool())
@ -150,24 +151,24 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
//preparing siege info
const CGTownInstance *town = curInt->cb->battleGetDefendedTown();
if (town && town->hasFort())
if(town && town->hasFort())
{
siegeH = new SiegeHelper(town, this);
}
curInt->battleInt = this;
CPlayerInterface::battleInt = this;
//initializing armies
this->army1 = army1;
this->army2 = army2;
std::vector<const CStack*> stacks = curInt->cb->battleGetAllStacks(true);
for (const CStack *s : stacks)
for(const CStack * s : stacks)
{
unitAdded(s);
}
//preparing menu background and terrain
if (siegeH)
if(siegeH)
{
background = BitmapHandler::loadBitmap( siegeH->getSiegeName(0), false );
ui8 siegeLevel = curInt->cb->battleGetSiegeLevel();
@ -204,9 +205,6 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
}
}
//preparing menu background
//graphics->blueToPlayersAdv(menu, hero1->tempOwner);
//preparing graphics for displaying amounts of creatures
amountNormal = BitmapHandler::loadBitmap("CMNUMWIN.BMP");
CSDL_Ext::alphaTransform(amountNormal);
@ -224,77 +222,68 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
CSDL_Ext::alphaTransform(amountEffNeutral);
transformPalette(amountEffNeutral, 1.00, 1.00, 0.18);
////blitting menu background and terrain
// blitAt(background, pos.x, pos.y);
// blitAt(menu, pos.x, 556 + pos.y);
//preparing buttons and console
bOptions = new CButton (Point( 3, 561), "icm003.def", CGI->generaltexth->zelp[381], std::bind(&CBattleInterface::bOptionsf,this), SDLK_o);
bSurrender = new CButton (Point( 54, 561), "icm001.def", CGI->generaltexth->zelp[379], std::bind(&CBattleInterface::bSurrenderf,this), SDLK_s);
bFlee = new CButton (Point(105, 561), "icm002.def", CGI->generaltexth->zelp[380], std::bind(&CBattleInterface::bFleef,this), SDLK_r);
bAutofight = new CButton (Point(157, 561), "icm004.def", CGI->generaltexth->zelp[382], std::bind(&CBattleInterface::bAutofightf,this), SDLK_a);
bSpell = new CButton (Point(645, 561), "icm005.def", CGI->generaltexth->zelp[385], std::bind(&CBattleInterface::bSpellf,this), SDLK_c);
bWait = new CButton (Point(696, 561), "icm006.def", CGI->generaltexth->zelp[386], std::bind(&CBattleInterface::bWaitf,this), SDLK_w);
bDefence = new CButton (Point(747, 561), "icm007.def", CGI->generaltexth->zelp[387], std::bind(&CBattleInterface::bDefencef,this), SDLK_d);
bOptions = std::make_shared<CButton>(Point( 3, 561), "icm003.def", CGI->generaltexth->zelp[381], std::bind(&CBattleInterface::bOptionsf,this), SDLK_o);
bSurrender = std::make_shared<CButton>(Point( 54, 561), "icm001.def", CGI->generaltexth->zelp[379], std::bind(&CBattleInterface::bSurrenderf,this), SDLK_s);
bFlee = std::make_shared<CButton>(Point(105, 561), "icm002.def", CGI->generaltexth->zelp[380], std::bind(&CBattleInterface::bFleef,this), SDLK_r);
bAutofight = std::make_shared<CButton>(Point(157, 561), "icm004.def", CGI->generaltexth->zelp[382], std::bind(&CBattleInterface::bAutofightf,this), SDLK_a);
bSpell = std::make_shared<CButton>(Point(645, 561), "icm005.def", CGI->generaltexth->zelp[385], std::bind(&CBattleInterface::bSpellf,this), SDLK_c);
bWait = std::make_shared<CButton>(Point(696, 561), "icm006.def", CGI->generaltexth->zelp[386], std::bind(&CBattleInterface::bWaitf,this), SDLK_w);
bDefence = std::make_shared<CButton>(Point(747, 561), "icm007.def", CGI->generaltexth->zelp[387], std::bind(&CBattleInterface::bDefencef,this), SDLK_d);
bDefence->assignedKeys.insert(SDLK_SPACE);
bConsoleUp = new CButton (Point(624, 561), "ComSlide.def", std::make_pair("", ""), std::bind(&CBattleInterface::bConsoleUpf,this), SDLK_UP);
bConsoleDown = new CButton (Point(624, 580), "ComSlide.def", std::make_pair("", ""), std::bind(&CBattleInterface::bConsoleDownf,this), SDLK_DOWN);
bConsoleUp = std::make_shared<CButton>(Point(624, 561), "ComSlide.def", std::make_pair("", ""), std::bind(&CBattleInterface::bConsoleUpf,this), SDLK_UP);
bConsoleDown = std::make_shared<CButton>(Point(624, 580), "ComSlide.def", std::make_pair("", ""), std::bind(&CBattleInterface::bConsoleDownf,this), SDLK_DOWN);
bConsoleDown->setImageOrder(2, 3, 4, 5);
console = new CBattleConsole();
console = std::make_shared<CBattleConsole>();
console->pos.x += 211;
console->pos.y += 560;
console->pos.w = 406;
console->pos.h = 38;
if (tacticsMode)
if(tacticsMode)
{
btactNext = new CButton(Point(213, 560), "icm011.def", std::make_pair("", ""), [&](){ bTacticNextStack(nullptr);}, SDLK_SPACE);
btactEnd = new CButton(Point(419, 560), "icm012.def", std::make_pair("", ""), [&](){ bEndTacticPhase();}, SDLK_RETURN);
btactNext = std::make_shared<CButton>(Point(213, 560), "icm011.def", std::make_pair("", ""), [&](){ bTacticNextStack(nullptr);}, SDLK_SPACE);
btactEnd = std::make_shared<CButton>(Point(419, 560), "icm012.def", std::make_pair("", ""), [&](){ bEndTacticPhase();}, SDLK_RETURN);
menu = BitmapHandler::loadBitmap("COPLACBR.BMP");
}
else
{
menu = BitmapHandler::loadBitmap("CBAR.BMP");
btactEnd = btactNext = nullptr;
}
graphics->blueToPlayersAdv(menu, curInt->playerID);
//loading hero animations
if (hero1) // attacking hero
if(hero1) // attacking hero
{
std::string battleImage;
if ( hero1->sex )
if(hero1->sex)
battleImage = hero1->type->heroClass->imageBattleFemale;
else
battleImage = hero1->type->heroClass->imageBattleMale;
attackingHero = new CBattleHero(battleImage, false, hero1->tempOwner, hero1->tempOwner == curInt->playerID ? hero1 : nullptr, this);
attackingHero = std::make_shared<CBattleHero>(battleImage, false, hero1->tempOwner, hero1->tempOwner == curInt->playerID ? hero1 : nullptr, this);
auto img = attackingHero->animation->getImage(0, 0, true);
if(img)
attackingHero->pos = genRect(img->height(), img->width(), pos.x - 43, pos.y - 19);
}
else
{
attackingHero = nullptr;
}
if (hero2) // defending hero
if(hero2) // defending hero
{
std::string battleImage;
if ( hero2->sex )
if(hero2->sex)
battleImage = hero2->type->heroClass->imageBattleFemale;
else
battleImage = hero2->type->heroClass->imageBattleMale;
defendingHero = new CBattleHero(battleImage, true, hero2->tempOwner, hero2->tempOwner == curInt->playerID ? hero2 : nullptr, this);
defendingHero = std::make_shared<CBattleHero>(battleImage, true, hero2->tempOwner, hero2->tempOwner == curInt->playerID ? hero2 : nullptr, this);
auto img = defendingHero->animation->getImage(0, 0, true);
if(img)
defendingHero->pos = genRect(img->height(), img->width(), pos.x + 693, pos.y - 19);
}
else
{
defendingHero = nullptr;
}
//preparing cells and hexes
cellBorder = BitmapHandler::loadBitmap("CCELLGRD.BMP");
@ -303,7 +292,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
CSDL_Ext::alphaTransform(cellShade);
for (int h = 0; h < GameConstants::BFIELD_SIZE; ++h)
{
auto hex = new CClickableHex();
auto hex = std::make_shared<CClickableHex>();
hex->myNumber = h;
hex->pos = hexPosition(h);
hex->accessible = true;
@ -385,8 +374,8 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
}
}
for (auto hex : bfield)
addChild(hex);
for(auto hex : bfield)
addChild(hex.get());
if (tacticsMode)
bTacticNextStack();
@ -412,7 +401,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
CBattleInterface::~CBattleInterface()
{
curInt->battleInt = nullptr;
CPlayerInterface::battleInt = nullptr;
givenCommand.cond.notify_all(); //that two lines should make any activeStack waiting thread to finish
if (active) //dirty fix for #485
@ -427,31 +416,11 @@ CBattleInterface::~CBattleInterface()
SDL_FreeSurface(amountEffNeutral);
SDL_FreeSurface(cellBorders);
SDL_FreeSurface(backgroundWithHexes);
delete bOptions;
delete bSurrender;
delete bFlee;
delete bAutofight;
delete bSpell;
delete bWait;
delete bDefence;
for (auto hex : bfield)
delete hex;
delete bConsoleUp;
delete bConsoleDown;
delete console;
delete attackingHero;
delete defendingHero;
delete queue;
SDL_FreeSurface(cellBorder);
SDL_FreeSurface(cellShade);
for (auto & elem : creAnims)
delete elem.second;
delete siegeH;
//TODO: play AI tracks if battle was during AI turn
@ -588,7 +557,7 @@ void CBattleInterface::keyPressed(const SDL_KeyboardEvent & key)
}
void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
{
auto hexItr = std::find_if (bfield.begin(), bfield.end(), [](const CClickableHex *hex)
auto hexItr = std::find_if(bfield.begin(), bfield.end(), [](std::shared_ptr<CClickableHex> hex)
{
return hex->hovered && hex->strictHovered;
});
@ -786,8 +755,7 @@ void CBattleInterface::bOptionsf()
Rect tempRect = genRect(431, 481, 160, 84);
tempRect += pos.topLeft();
auto optionsWin = new CBattleOptionsWindow(tempRect, this);
GH.pushInt(optionsWin);
GH.pushIntT<CBattleOptionsWindow>(tempRect, this);
}
void CBattleInterface::bSurrenderf()
@ -900,7 +868,7 @@ void CBattleInterface::bSpellf()
if(spellCastProblem == ESpellCastProblem::OK)
{
GH.pushInt(new CSpellWindow(myHero, curInt.get()));
GH.pushIntT<CSpellWindow>(myHero, curInt.get());
}
else if (spellCastProblem == ESpellCastProblem::MAGIC_IS_BLOCKED)
{
@ -1276,13 +1244,13 @@ void CBattleInterface::displayBattleFinished()
CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
if(settings["session"]["spectate"].Bool() && settings["session"]["spectate-skip-battle-result"].Bool())
{
GH.popIntTotally(this);
close();
return;
}
resWindow = new CBattleResultWindow(*bresult, *this->curInt);
GH.pushInt(resWindow);
GH.pushInt(std::make_shared<CBattleResultWindow>(*bresult, *(this->curInt)));
curInt->waitWhileDialog(); // Avoid freeze when AI end turn after battle. Check bug #1897
CPlayerInterface::battleInt = nullptr;
}
void CBattleInterface::spellCast(const BattleSpellCast * sc)
@ -1436,7 +1404,7 @@ void CBattleInterface::setHeroAnimation(ui8 side, int phase)
void CBattleInterface::castThisSpell(SpellID spellID)
{
spellToCast = new BattleAction();
spellToCast = std::make_shared<BattleAction>();
spellToCast->actionType = EActionType::HERO_SPELL;
spellToCast->actionSubtype = spellID; //spell number
spellToCast->stackNumber = (attackingHeroInstance->tempOwner == curInt->playerID) ? -1 : -2;
@ -1453,7 +1421,7 @@ void CBattleInterface::castThisSpell(SpellID spellID)
if (spellSelMode == NO_LOCATION) //user does not have to select location
{
spellToCast->aimToHex(BattleHex::INVALID);
curInt->cb->battleMakeAction(spellToCast);
curInt->cb->battleMakeAction(spellToCast.get());
endCastingSpell();
}
else
@ -1680,15 +1648,15 @@ void CBattleInterface::activateStack()
void CBattleInterface::endCastingSpell()
{
if (spellDestSelectMode)
if(spellDestSelectMode)
{
vstd::clear_pointer(spellToCast);
spellToCast.reset();
sp = nullptr;
spellDestSelectMode = false;
CCS->curh->changeGraphic(ECursor::COMBAT, ECursor::COMBAT_POINTER);
if (activeStack)
if(activeStack)
{
getPossibleActionsForStack(activeStack, false); //restore actions after they were cleared
myTurn = true;
@ -1696,7 +1664,7 @@ void CBattleInterface::endCastingSpell()
}
else
{
if (activeStack)
if(activeStack)
{
getPossibleActionsForStack(activeStack, false);
GH.fakeMouseMove();
@ -2419,7 +2387,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
{
cursorFrame = ECursor::COMBAT_QUERY;
consoleMsg = (boost::format(CGI->generaltexth->allTexts[297]) % shere->getName()).str();
realizeAction = [=](){ GH.pushInt(new CStackWindow(shere, false)); };
realizeAction = [=](){ GH.pushIntT<CStackWindow>(shere, false); };
break;
}
}
@ -2511,7 +2479,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
spellToCast->aimToHex(myNumber);
break;
}
curInt->cb->battleMakeAction(spellToCast);
curInt->cb->battleMakeAction(spellToCast.get());
endCastingSpell();
}
selectedStack = nullptr;

View File

@ -106,7 +106,7 @@ struct CatapultProjectileInfo
/// Big class which handles the overall battle interface actions and it is also responsible for
/// drawing everything correctly.
class CBattleInterface : public CIntObject
class CBattleInterface : public WindowBase
{
enum PossibleActions // actions performed at l-click
{
@ -121,14 +121,26 @@ class CBattleInterface : public CIntObject
private:
SDL_Surface *background, *menu, *amountNormal, *amountNegative, *amountPositive, *amountEffNeutral, *cellBorders, *backgroundWithHexes;
CButton *bOptions, *bSurrender, *bFlee, *bAutofight, *bSpell,
* bWait, *bDefence, *bConsoleUp, *bConsoleDown, *btactNext, *btactEnd;
CBattleConsole *console;
CBattleHero *attackingHero, *defendingHero; //fighting heroes
CStackQueue *queue;
std::shared_ptr<CButton> bOptions;
std::shared_ptr<CButton> bSurrender;
std::shared_ptr<CButton> bFlee;
std::shared_ptr<CButton> bAutofight;
std::shared_ptr<CButton> bSpell;
std::shared_ptr<CButton> bWait;
std::shared_ptr<CButton> bDefence;
std::shared_ptr<CButton> bConsoleUp;
std::shared_ptr<CButton> bConsoleDown;
std::shared_ptr<CButton> btactNext;
std::shared_ptr<CButton> btactEnd;
std::shared_ptr<CBattleConsole> console;
std::shared_ptr<CBattleHero> attackingHero;
std::shared_ptr<CBattleHero> defendingHero;
std::shared_ptr<CStackQueue> queue;
const CCreatureSet *army1, *army2; //copy of initial armies (for result window)
const CGHeroInstance *attackingHeroInstance, *defendingHeroInstance;
std::map<int, CCreatureAnimation *> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
std::map<int32_t, std::shared_ptr<CCreatureAnimation>> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
std::map<int, std::shared_ptr<CAnimation>> idToProjectile;
@ -154,7 +166,7 @@ private:
bool stackCanCastSpell; //if true, active stack could possibly cast some target spell
bool creatureCasting; //if true, stack currently aims to cats a spell
bool spellDestSelectMode; //if true, player is choosing destination for his spell - only for GUI / console
BattleAction *spellToCast; //spell for which player is choosing destination
std::shared_ptr<BattleAction> spellToCast; //spell for which player is choosing destination
const CSpell *sp; //spell pointer for convenience
si32 creatureSpellToCast;
std::vector<PossibleActions> possibleActions; //all actions possible to call at the moment by player
@ -284,11 +296,10 @@ public:
int getAnimSpeed() const; //speed of animation; range 1..100
CPlayerInterface *getCurrentPlayerInterface() const;
std::vector<CClickableHex*> bfield; //11 lines, 17 hexes on each
std::vector<std::shared_ptr<CClickableHex>> bfield; //11 lines, 17 hexes on each
SDL_Surface *cellBorder, *cellShade;
bool myTurn; //if true, interface is active (commands can be ordered)
CBattleResultWindow *resWindow; //window of end of battle
bool moveStarted; //if true, the creature that is already moving is going to make its first step
int moveSoundHander; // sound handler used when moving a unit

View File

@ -212,7 +212,7 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
}
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
GH.pushInt(new CSpellWindow(myHero, myOwner->getCurrentPlayerInterface()));
GH.pushIntT<CSpellWindow>(myHero, myOwner->getCurrentPlayerInterface());
}
}
@ -230,7 +230,7 @@ void CBattleHero::clickRight(tribool down, bool previousState)
{
auto h = flip ? myOwner->defendingHeroInstance : myOwner->attackingHeroInstance;
targetHero.initFromHero(h, InfoAboutHero::EInfoLevel::INBATTLE);
GH.pushInt(new CHeroInfoWindow(targetHero, &windowPosition));
GH.pushIntT<CHeroInfoWindow>(targetHero, &windowPosition);
}
}
@ -391,7 +391,7 @@ void CBattleOptionsWindow::bDefaultf()
void CBattleOptionsWindow::bExitf()
{
GH.popIntTotally(this);
close();
}
CBattleResultWindow::CBattleResultWindow(const BattleResult & br, CPlayerInterface & _owner)
@ -560,8 +560,10 @@ void CBattleResultWindow::show(SDL_Surface * to)
void CBattleResultWindow::bExitf()
{
CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon
GH.popIntTotally(this);
if(dynamic_cast<CBattleInterface*>(GH.topInt()))
close();
if(dynamic_cast<CBattleInterface*>(GH.topInt().get()))
GH.popInts(1); //pop battle interface if present
//Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle,
@ -689,11 +691,10 @@ void CClickableHex::clickRight(tribool down, bool previousState)
const CStack * myst = myInterface->getCurrentPlayerInterface()->cb->battleGetStackByPos(myNumber); //stack info
if(hovered && strictHovered && myst!=nullptr)
{
if(!myst->alive()) return;
if(down)
{
GH.pushInt(new CStackWindow(myst, true));
GH.pushIntT<CStackWindow>(myst, true);
}
}
}

View File

@ -89,7 +89,7 @@ public:
};
/// Class which manages the battle options window
class CBattleOptionsWindow : public CIntObject
class CBattleOptionsWindow : public WindowBase
{
private:
std::shared_ptr<CPicture> background;
@ -106,7 +106,7 @@ public:
};
/// Class which is responsible for showing the battle result window
class CBattleResultWindow : public CIntObject
class CBattleResultWindow : public WindowBase
{
private:
std::shared_ptr<CPicture> background;

View File

@ -34,10 +34,10 @@ SDL_Color AnimationControls::getNoBorder()
return creatureNoBorder;
}
CCreatureAnimation * AnimationControls::getAnimation(const CCreature * creature)
std::shared_ptr<CCreatureAnimation> AnimationControls::getAnimation(const CCreature * creature)
{
auto func = std::bind(&AnimationControls::getCreatureAnimationSpeed, creature, _1, _2);
return new CCreatureAnimation(creature->animDefName, func);
return std::make_shared<CCreatureAnimation>(creature->animDefName, func);
}
float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, const CCreatureAnimation * anim, size_t group)

View File

@ -25,7 +25,7 @@ namespace AnimationControls
SDL_Color getNoBorder();
/// creates animation object with preset speed control
CCreatureAnimation * getAnimation(const CCreature * creature);
std::shared_ptr<CCreatureAnimation> getAnimation(const CCreature * creature);
/// returns animation speed of specific group, taking in mind game setting (in frames per second)
float getCreatureAnimationSpeed(const CCreature * creature, const CCreatureAnimation * anim, size_t groupID);

View File

@ -23,7 +23,6 @@
#include "../lib/CRandomGenerator.h"
class SDLImageLoader;
class CompImageLoader;
typedef std::map <size_t, std::vector <JsonNode> > source_map;
typedef std::map<size_t, IImage* > image_map;
@ -79,9 +78,9 @@ public:
public:
//Load image from def file
SDLImage(CDefFile *data, size_t frame, size_t group=0, bool compressed=false);
SDLImage(CDefFile *data, size_t frame, size_t group=0);
//Load from bitmap file
SDLImage(std::string filename, bool compressed=false);
SDLImage(std::string filename);
SDLImage(const JsonNode & conf);
//Create using existing surface, extraRef will increase refcount on SDL_Surface
@ -107,65 +106,6 @@ public:
friend class SDLImageLoader;
};
/*
* RLE-compressed image data for 8-bit images with alpha-channel, currently far from finished
* primary purpose is not high compression ratio but fast drawing.
* Consist of repeatable segments with format similar to H3 def compression:
* 1st byte:
* if (byte == 0xff)
* raw data, opaque and semi-transparent data always in separate blocks
* else
* RLE-compressed image data with this color
* 2nd byte = size of segment
* raw data (if any)
*/
class CompImage : public IImage
{
//x,y - margins, w,h - sprite size
Rect sprite;
//total size including borders
Point fullSize;
//RLE-d data
ui8 * surf;
//array of offsets for each line
ui32 * line;
//palette
SDL_Color *palette;
//Used internally to blit one block of data
template<int bpp, int dir>
void BlitBlock(ui8 type, ui8 size, ui8 *&data, ui8 *&dest, ui8 alpha) const;
void BlitBlockWithBpp(ui8 bpp, ui8 type, ui8 size, ui8 *&data, ui8 *&dest, ui8 alpha, bool rotated) const;
public:
//Load image from def file
CompImage(const CDefFile *data, size_t frame, size_t group=0);
//TODO: load image from SDL_Surface
CompImage(SDL_Surface * surf);
~CompImage();
void draw(SDL_Surface *where, int posX=0, int posY=0, Rect *src=nullptr, ui8 alpha=255) const override;
void draw(SDL_Surface * where, SDL_Rect * dest, SDL_Rect * src, ui8 alpha=255) const override;
std::shared_ptr<IImage> scaleFast(float scale) const override;
void exportBitmap(const boost::filesystem::path & path) const override;
void playerColored(PlayerColor player) override;
void setFlagColor(PlayerColor player) override;
int width() const override;
int height() const override;
void horizontalFlip() override;
void verticalFlip() override;
void shiftPalette(int from, int howMany) override;
void setBorderPallete(const BorderPallete & borderPallete) override;
friend class CompImageLoader;
};
class SDLImageLoader
{
SDLImage * image;
@ -184,30 +124,6 @@ public:
~SDLImageLoader();
};
class CompImageLoader
{
CompImage * image;
ui8 *position;
ui8 *entry;
ui32 currentLine;
inline ui8 typeOf(ui8 color);
inline void NewEntry(ui8 color, size_t size);
inline void NewEntry(const ui8 * &data, size_t size);
public:
//load size raw pixels from data
inline void Load(size_t size, const ui8 * data);
//set size pixels to color
inline void Load(size_t size, ui8 color=0);
inline void EndLine();
//init image with these sizes and palette
inline void init(Point SpriteSize, Point Margins, Point FullSize, SDL_Color *pal);
CompImageLoader(CompImage * Img);
~CompImageLoader();
};
// Extremely simple file cache. TODO: smarter, more general solution
class CFileCache
{
@ -576,7 +492,7 @@ SDLImageLoader::SDLImageLoader(SDLImage * Img):
void SDLImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_Color *pal)
{
//Init image
image->surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SpriteSize.x, SpriteSize.y, 8, 0, 0, 0, 0);
image->surf = SDL_CreateRGBSurface(0, SpriteSize.x, SpriteSize.y, 8, 0, 0, 0, 0);
image->margins = Margins;
image->fullSize = FullSize;
@ -621,182 +537,6 @@ SDLImageLoader::~SDLImageLoader()
//TODO: RLE if compressed and bpp>1
}
////////////////////////////////////////////////////////////////////////////////
CompImageLoader::CompImageLoader(CompImage * Img):
image(Img),
position(nullptr),
entry(nullptr),
currentLine(0)
{
}
void CompImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_Color *pal)
{
image->sprite = Rect(Margins, SpriteSize);
image->fullSize = FullSize;
if (SpriteSize.x && SpriteSize.y)
{
image->palette = new SDL_Color[256];
memcpy((void*)image->palette, (void*)pal, 256*sizeof(SDL_Color));
//Allocate enought space for worst possible case, c-style malloc used due to resizing after load
image->surf = (ui8*)malloc(SpriteSize.x*SpriteSize.y*3);
image->line = new ui32[SpriteSize.y+1];
image->line[0] = 0;
position = image->surf;
}
}
inline void CompImageLoader::NewEntry(ui8 color, size_t size)
{
assert(color != 0xff);
assert(size && size<256);
entry = position;
entry[0] = color;
entry[1] = size;
position +=2;
}
inline void CompImageLoader::NewEntry(const ui8 * &data, size_t size)
{
assert(size && size<256);
entry = position;
entry[0] = 0xff;
entry[1] = size;
position +=2;
memcpy(position, data, size);
position+=size;
data+=size;
}
inline ui8 CompImageLoader::typeOf(ui8 color)
{
if (color == 0)
return 0;
if (image->palette[color].a != 255)
return 1;
return 2;
}
inline void CompImageLoader::Load(size_t size, const ui8 * data)
{
while (size)
{
//Try to compress data
while(true)
{
ui8 color = data[0];
if (color != 0xff)
{
size_t runLength = 1;
while (runLength < size && color == data[runLength])
runLength++;
if (runLength > 1 && runLength < 255)//Row of one color found - use RLE
{
Load(runLength, color);
data += runLength;
size -= runLength;
if (!size)
return;
}
else
break;
}
else
break;
}
//Select length for new raw entry
size_t runLength = 1;
ui8 color = data[0];
ui8 type = typeOf(color);
ui8 color2;
ui8 type2;
if (size > 1)
{
do
{
color2 = data[runLength];
type2 = typeOf(color2);
runLength++;
}
//While we have data of this type and different colors
while ((runLength < size) && (type == type2) && ( (color2 != 0xff) || (color2 != color)));
}
size -= runLength;
//add data to last entry
if (entry && entry[0] == 0xff && type == typeOf(entry[2]))
{
size_t toCopy = std::min<size_t>(runLength, 255 - entry[1]);
runLength -= toCopy;
entry[1] += toCopy;
memcpy(position, data, toCopy);
data+=toCopy;
position+=toCopy;
}
//Create new entries
while (runLength > 255)
{
NewEntry(data, 255);
runLength -= 255;
}
if (runLength)
NewEntry(data, runLength);
}
}
inline void CompImageLoader::Load(size_t size, ui8 color)
{
if (!size)
return;
if (color==0xff)
{
auto tmpbuf = new ui8[size];
memset((void*)tmpbuf, color, size);
Load(size, tmpbuf);
delete [] tmpbuf;
return;
}
//Current entry is RLE with same color as new block
if (entry && entry[0] == color)
{
size_t toCopy = std::min<size_t>(size, 255 - entry[1]);
size -= toCopy;
entry[1] += toCopy;
}
//Create new entries
while (size > 255)
{
NewEntry(color, 255);
size -= 255;
}
if (size)
NewEntry(color, size);
}
void CompImageLoader::EndLine()
{
currentLine++;
image->line[currentLine] = position - image->surf;
entry = nullptr;
}
CompImageLoader::~CompImageLoader()
{
if (!image->surf)
return;
ui8* newPtr = (ui8*)realloc((void*)image->surf, position - image->surf);
if (newPtr)
image->surf = newPtr;
}
/*************************************************************************
* Classes for images, support loading from file and drawing on surface *
*************************************************************************/
@ -805,7 +545,7 @@ IImage::IImage() = default;
IImage::~IImage() = default;
SDLImage::SDLImage(CDefFile * data, size_t frame, size_t group, bool compressed)
SDLImage::SDLImage(CDefFile * data, size_t frame, size_t group)
: surf(nullptr),
margins(0, 0),
fullSize(0, 0)
@ -857,15 +597,14 @@ SDLImage::SDLImage(const JsonNode & conf)
}
}
SDLImage::SDLImage(std::string filename, bool compressed)
SDLImage::SDLImage(std::string filename)
: surf(nullptr),
margins(0, 0),
fullSize(0, 0)
{
surf = BitmapHandler::loadBitmap(filename);
if (surf == nullptr)
if(surf == nullptr)
{
logGlobal->error("Error: failed to load image %s", filename);
return;
@ -875,23 +614,8 @@ SDLImage::SDLImage(std::string filename, bool compressed)
fullSize.x = surf->w;
fullSize.y = surf->h;
}
if (compressed)
{
SDL_Surface *temp = surf;
// add RLE flag
if (surf->format->palette)
{
CSDL_Ext::setColorKey(temp,temp->format->palette->colors[0]);
}
SDL_SetSurfaceRLE(temp, SDL_RLEACCEL);
// convert surface to enable RLE
surf = SDL_ConvertSurface(temp, temp->format, temp->flags);
SDL_FreeSurface(temp);
}
}
void SDLImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 alpha) const
{
if(!surf)
@ -1042,282 +766,6 @@ SDLImage::~SDLImage()
SDL_FreeSurface(surf);
}
CompImage::CompImage(const CDefFile *data, size_t frame, size_t group):
surf(nullptr),
line(nullptr),
palette(nullptr)
{
CompImageLoader loader(this);
data->loadFrame(frame, group, loader);
}
CompImage::CompImage(SDL_Surface * surf)
{
//TODO
assert(0);
}
void CompImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 alpha) const
{
Rect dest(posX,posY, width(), height());
draw(where, &dest, src, alpha);
}
void CompImage::draw(SDL_Surface* where, SDL_Rect* dest, SDL_Rect* src, ui8 alpha) const
{
int rotation = 0; //TODO
//rotation & 2 = horizontal rotation
//rotation & 4 = vertical rotation
if (!surf)
return;
Rect sourceRect(sprite);
//TODO: rotation and scaling
if (src)
sourceRect = sourceRect & *src;
//Limit source rect to sizes of surface
sourceRect = sourceRect & Rect(0, 0, where->w, where->h);
//Starting point on SDL surface
Point dst(sourceRect.x,sourceRect.y);
if (dest)
{
dst.x += dest->x;
dst.y += dest->y;
}
if (rotation & 2)
dst.y += sourceRect.h;
if (rotation & 4)
dst.x += sourceRect.w;
sourceRect -= sprite.topLeft();
for (int currY = 0; currY <sourceRect.h; currY++)
{
ui8* data = surf + line[currY+sourceRect.y];
ui8 type = *(data++);
ui8 size = *(data++);
int currX = sourceRect.x;
//Skip blocks until starting position reached
while ( currX > size )
{
currX -= size;
if (type == 0xff)
data += size;
type = *(data++);
size = *(data++);
}
//This block will be shown partially - calculate size\position
size -= currX;
if (type == 0xff)
data += currX;
currX = 0;
ui8 bpp = where->format->BytesPerPixel;
//Calculate position for blitting: pixels + Y + X
ui8* blitPos = (ui8*) where->pixels;
if (rotation & 4)
blitPos += (dst.y - currY) * where->pitch;
else
blitPos += (dst.y + currY) * where->pitch;
blitPos += dst.x * bpp;
//Blit blocks that must be fully visible
while (currX + size < sourceRect.w)
{
//blit block, pointers will be modified if needed
BlitBlockWithBpp(bpp, type, size, data, blitPos, alpha, rotation & 2);
currX += size;
type = *(data++);
size = *(data++);
}
//Blit last, semi-visible block
size = sourceRect.w - currX;
BlitBlockWithBpp(bpp, type, size, data, blitPos, alpha, rotation & 2);
}
}
std::shared_ptr<IImage> CompImage::scaleFast(float scale) const
{
//todo: CompImage::scaleFast
logAnim->error("CompImage::scaleFast is not implemented");
return nullptr;
}
#define CASEBPP(x,y) case x: BlitBlock<x,y>(type, size, data, dest, alpha); break
//FIXME: better way to get blitter
void CompImage::BlitBlockWithBpp(ui8 bpp, ui8 type, ui8 size, ui8 *&data, ui8 *&dest, ui8 alpha, bool rotated) const
{
assert(bpp>1 && bpp<5);
if (rotated)
switch (bpp)
{
CASEBPP(2,1);
CASEBPP(3,1);
CASEBPP(4,1);
}
else
switch (bpp)
{
CASEBPP(2,1);
CASEBPP(3,1);
CASEBPP(4,1);
}
}
#undef CASEBPP
//Blit one block from RLE-d surface
template<int bpp, int dir>
void CompImage::BlitBlock(ui8 type, ui8 size, ui8 *&data, ui8 *&dest, ui8 alpha) const
{
//Raw data
if (type == 0xff)
{
ui8 color = *data;
if (alpha != 255)//Per-surface alpha is set
{
for (size_t i=0; i<size; i++)
{
SDL_Color col = palette[*(data++)];
col.a = (ui32)col.a*alpha/255;
ColorPutter<bpp, 1>::PutColorAlpha(dest, col);
}
return;
}
if (palette[color].a == 255)
{
//Put row of RGB data
for (size_t i=0; i<size; i++)
ColorPutter<bpp, 1>::PutColor(dest, palette[*(data++)]);
}
else
{
//Put row of RGBA data
for (size_t i=0; i<size; i++)
ColorPutter<bpp, 1>::PutColorAlpha(dest, palette[*(data++)]);
}
}
//RLE-d sequence
else
{
if (alpha != 255 && palette[type].a !=0)//Per-surface alpha is set
{
SDL_Color col = palette[type];
col.a = (int)col.a*(255-alpha)/255;
for (size_t i=0; i<size; i++)
ColorPutter<bpp, 1>::PutColorAlpha(dest, col);
return;
}
switch (palette[type].a)
{
case 0:
{
//Skip row
dest += size*bpp;
break;
}
case 255:
{
//Put RGB row
ColorPutter<bpp, 1>::PutColorRow(dest, palette[type], size);
break;
}
default:
{
//Put RGBA row
for (size_t i=0; i<size; i++)
ColorPutter<bpp, 1>::PutColorAlpha(dest, palette[type]);
break;
}
}
}
}
void CompImage::playerColored(PlayerColor player)
{
SDL_Color *pal = nullptr;
if(player < PlayerColor::PLAYER_LIMIT)
{
pal = graphics->playerColorPalette + 32*player.getNum();
}
else if(player == PlayerColor::NEUTRAL)
{
pal = graphics->neutralColorPalette;
}
else
assert(0);
for(int i=0; i<32; ++i)
{
CSDL_Ext::colorAssign(palette[224+i],pal[i]);
}
}
void CompImage::setFlagColor(PlayerColor player)
{
logAnim->error("CompImage::setFlagColor is not implemented");
}
int CompImage::width() const
{
return fullSize.x;
}
int CompImage::height() const
{
return fullSize.y;
}
CompImage::~CompImage()
{
free(surf);
delete [] line;
delete [] palette;
}
void CompImage::horizontalFlip()
{
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
}
void CompImage::verticalFlip()
{
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
}
void CompImage::shiftPalette(int from, int howMany)
{
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
}
void CompImage::setBorderPallete(const IImage::BorderPallete & borderPallete)
{
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
}
void CompImage::exportBitmap(const boost::filesystem::path & path) const
{
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
}
/*************************************************************************
* CAnimation for animations handling, can load part of file if needed *
*************************************************************************/
std::shared_ptr<IImage> CAnimation::getFromExtraDef(std::string filename)
{
size_t pos = filename.find(':');
@ -1363,17 +811,14 @@ bool CAnimation::loadFrame(size_t frame, size_t group)
if(vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present
{
if(compressed)
images[group][frame] = std::make_shared<CompImage>(defFile, frame, group);
else
images[group][frame] = std::make_shared<SDLImage>(defFile, frame, group);
images[group][frame] = std::make_shared<SDLImage>(defFile.get(), frame, group);
return true;
}
}
// still here? image is missing
printError(frame, group, "LoadFrame");
images[group][frame] = std::make_shared<SDLImage>("DEFAULT", compressed);
images[group][frame] = std::make_shared<SDLImage>("DEFAULT");
}
else //load from separate file
{
@ -1507,11 +952,10 @@ void CAnimation::printError(size_t frame, size_t group, std::string type) const
logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name, group, frame);
}
CAnimation::CAnimation(std::string Name, bool Compressed):
CAnimation::CAnimation(std::string Name):
name(Name),
compressed(Compressed),
preloaded(false),
defFile(nullptr)
defFile()
{
size_t dotPos = name.find_last_of('.');
if ( dotPos!=-1 )
@ -1521,7 +965,7 @@ CAnimation::CAnimation(std::string Name, bool Compressed):
ResourceID resource(std::string("SPRITES/") + name, EResType::ANIMATION);
if(CResourceHandler::get()->existsResource(resource))
defFile = new CDefFile(name);
defFile = std::make_shared<CDefFile>(name);
init();
@ -1531,21 +975,28 @@ CAnimation::CAnimation(std::string Name, bool Compressed):
CAnimation::CAnimation():
name(""),
compressed(false),
preloaded(false),
defFile(nullptr)
defFile()
{
init();
}
CAnimation::~CAnimation()
{
if(defFile)
delete defFile;
}
CAnimation::~CAnimation() = default;
void CAnimation::duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup)
{
if(!source.count(sourceGroup))
{
logAnim->error("Group %d missing in %s", sourceGroup, name);
return;
}
if(source[sourceGroup].size() <= sourceFrame)
{
logAnim->error("Frame [%d %d] missing in %s", sourceGroup, sourceFrame, name);
return;
}
//todo: clone actual loaded Image object
JsonNode clone(source[sourceGroup][sourceFrame]);

View File

@ -68,12 +68,9 @@ private:
//animation file name
std::string name;
//if true all frames will be stored in compressed (RLE) state
const bool compressed;
bool preloaded;
CDefFile * defFile;
std::shared_ptr<CDefFile> defFile;
//loader, will be called by load(), require opened def file for loading from it. Returns true if image is loaded
bool loadFrame(size_t frame, size_t group);
@ -93,7 +90,7 @@ private:
std::shared_ptr<IImage> getFromExtraDef(std::string filename);
public:
CAnimation(std::string Name, bool Compressed = false);
CAnimation(std::string Name);
CAnimation();
~CAnimation();

View File

@ -18,8 +18,31 @@
#include "../CMT.h"
void CCursorHandler::clearBuffer()
{
Uint32 fillColor = SDL_MapRGBA(buffer->format, 0, 0, 0, 0);
CSDL_Ext::fillRect(buffer, nullptr, fillColor);
}
void CCursorHandler::updateBuffer(CIntObject * payload)
{
payload->moveTo(Point(0,0));
payload->showAll(buffer);
needUpdate = true;
}
void CCursorHandler::replaceBuffer(CIntObject * payload)
{
clearBuffer();
updateBuffer(payload);
}
void CCursorHandler::initCursor()
{
cursorLayer = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 40, 40);
SDL_SetTextureBlendMode(cursorLayer, SDL_BLENDMODE_BLEND);
xpos = ypos = 0;
type = ECursor::DEFAULT;
dndObject = nullptr;
@ -34,9 +57,9 @@ void CCursorHandler::initCursor()
currentCursor = cursors.at(int(ECursor::DEFAULT)).get();
help = CSDL_Ext::newSurface(40,40);
//No blending. Ensure, that we are copying pixels during "screen restore draw"
SDL_SetSurfaceBlendMode(help,SDL_BLENDMODE_NONE);
buffer = CSDL_Ext::newSurface(40,40);
SDL_SetSurfaceBlendMode(buffer, SDL_BLENDMODE_NONE);
SDL_ShowCursor(SDL_DISABLE);
changeGraphic(ECursor::ADVENTURE, 0);
@ -56,11 +79,17 @@ void CCursorHandler::changeGraphic(ECursor::ECursorTypes type, int index)
this->frame = index;
currentCursor->setFrame(index);
}
replaceBuffer(currentCursor);
}
void CCursorHandler::dragAndDropCursor(std::unique_ptr<CAnimImage> object)
{
dndObject = std::move(object);
if(dndObject)
replaceBuffer(dndObject.get());
else
replaceBuffer(currentCursor);
}
void CCursorHandler::cursorMove(const int & x, const int & y)
@ -69,40 +98,6 @@ void CCursorHandler::cursorMove(const int & x, const int & y)
ypos = y;
}
void CCursorHandler::drawWithScreenRestore()
{
if(!showing) return;
int x = xpos, y = ypos;
shiftPos(x, y);
SDL_Rect temp_rect1 = genRect(40,40,x,y);
SDL_Rect temp_rect2 = genRect(40,40,0,0);
SDL_BlitSurface(screen, &temp_rect1, help, &temp_rect2);
if (dndObject)
{
dndObject->moveTo(Point(x - dndObject->pos.w/2, y - dndObject->pos.h/2));
dndObject->showAll(screen);
}
else
{
currentCursor->moveTo(Point(x,y));
currentCursor->showAll(screen);
}
}
void CCursorHandler::drawRestored()
{
if(!showing)
return;
int x = xpos, y = ypos;
shiftPos(x, y);
SDL_Rect temp_rect = genRect(40, 40, x, y);
SDL_BlitSurface(help, nullptr, screen, &temp_rect);
}
void CCursorHandler::shiftPos( int &x, int &y )
{
if(( type == ECursor::COMBAT && frame != ECursor::COMBAT_POINTER) || type == ECursor::SPELLBOOK)
@ -221,15 +216,54 @@ void CCursorHandler::centerCursor()
void CCursorHandler::render()
{
drawWithScreenRestore();
CSDL_Ext::update(screen);
drawRestored();
if(!showing)
return;
//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);
if(dndObject)
{
x -= dndObject->pos.w/2;
y -= dndObject->pos.h/2;
}
SDL_Rect destRect;
destRect.x = x;
destRect.y = y;
destRect.w = 40;
destRect.h = 40;
SDL_RenderCopy(mainRenderer, cursorLayer, nullptr, &destRect);
}
CCursorHandler::CCursorHandler() = default;
void CCursorHandler::updateTexture()
{
if(needUpdate)
{
SDL_UpdateTexture(cursorLayer, nullptr, buffer->pixels, buffer->pitch);
needUpdate = false;
}
}
CCursorHandler::CCursorHandler()
: needUpdate(true),
buffer(nullptr),
cursorLayer(nullptr),
showing(false)
{
}
CCursorHandler::~CCursorHandler()
{
if(help)
SDL_FreeSurface(help);
if(buffer)
SDL_FreeSurface(buffer);
if(cursorLayer)
SDL_DestroyTexture(cursorLayer);
}

View File

@ -8,9 +8,10 @@
*
*/
#pragma once
class CIntObject;
class CAnimImage;
struct SDL_Surface;
struct SDL_Texture;
namespace ECursor
{
@ -26,7 +27,10 @@ namespace ECursor
/// handles mouse cursor
class CCursorHandler final
{
SDL_Surface * help;
bool needUpdate;
SDL_Texture * cursorLayer;
SDL_Surface * buffer;
CAnimImage * currentCursor;
std::unique_ptr<CAnimImage> dndObject; //if set, overrides currentCursor
@ -35,10 +39,12 @@ class CCursorHandler final
bool showing;
/// Draw cursor preserving original image below cursor
void drawWithScreenRestore();
/// Restore original image below cursor
void drawRestored();
void clearBuffer();
void updateBuffer(CIntObject * payload);
void replaceBuffer(CIntObject * payload);
void shiftPos( int &x, int &y );
void updateTexture();
public:
/// position of cursor
int xpos, ypos;
@ -63,9 +69,8 @@ public:
void render();
void shiftPos( int &x, int &y );
void hide() { showing=0; };
void show() { showing=1; };
void hide() { showing=false; };
void show() { showing=true; };
/// change cursor's positions to (x, y)
void cursorMove(const int & x, const int & y);

View File

@ -96,10 +96,11 @@ void CGuiHandler::handleElementDeActivate(CIntObject * elem, ui16 activityFlag)
elem->active_m &= ~activityFlag;
}
void CGuiHandler::popInt(IShowActivatable *top)
void CGuiHandler::popInt(std::shared_ptr<IShowActivatable> top)
{
assert(listInt.front() == top);
top->deactivate();
disposed.push_back(top);
listInt.pop_front();
objsToBlit -= top;
if(!listInt.empty())
@ -109,18 +110,10 @@ void CGuiHandler::popInt(IShowActivatable *top)
pushSDLEvent(SDL_USEREVENT, EUserEvent::INTERFACE_CHANGED);
}
void CGuiHandler::popIntTotally(IShowActivatable *top)
{
assert(listInt.front() == top);
popInt(top);
delete top;
fakeMouseMove();
}
void CGuiHandler::pushInt(IShowActivatable *newInt)
void CGuiHandler::pushInt(std::shared_ptr<IShowActivatable> newInt)
{
assert(newInt);
assert(boost::range::find(listInt, newInt) == listInt.end()); // do not add same object twice
assert(!vstd::contains(listInt, newInt)); // do not add same object twice
//a new interface will be present, we'll need to use buffer surface (unless it's advmapint that will alter screenBuf on activate anyway)
screenBuf = screen2;
@ -128,6 +121,7 @@ void CGuiHandler::pushInt(IShowActivatable *newInt)
if(!listInt.empty())
listInt.front()->deactivate();
listInt.push_front(newInt);
CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
newInt->activate();
objsToBlit.push_back(newInt);
totalRedraw();
@ -144,7 +138,7 @@ void CGuiHandler::popInts(int howMany)
for(int i=0; i < howMany; i++)
{
objsToBlit -= listInt.front();
delete listInt.front();
disposed.push_back(listInt.front());
listInt.pop_front();
}
@ -158,10 +152,10 @@ void CGuiHandler::popInts(int howMany)
pushSDLEvent(SDL_USEREVENT, EUserEvent::INTERFACE_CHANGED);
}
IShowActivatable * CGuiHandler::topInt()
std::shared_ptr<IShowActivatable> CGuiHandler::topInt()
{
if(listInt.empty())
return nullptr;
return std::shared_ptr<IShowActivatable>();
else
return listInt.front();
}
@ -234,11 +228,11 @@ void CGuiHandler::handleCurrentEvent()
case SDLK_F9:
//not working yet since CClient::run remain locked after CBattleInterface removal
if(LOCPLINT->battleInt)
{
GH.popIntTotally(GH.topInt());
vstd::clear_pointer(LOCPLINT->battleInt);
}
// if(LOCPLINT->battleInt)
// {
// GH.popInts(1);
// vstd::clear_pointer(LOCPLINT->battleInt);
// }
break;
default:
@ -271,7 +265,6 @@ void CGuiHandler::handleCurrentEvent()
}
else if(current->type == SDL_MOUSEMOTION)
{
CCS->curh->cursorMove(current->motion.x, current->motion.y);
handleMouseMotion();
}
else if(current->type == SDL_MOUSEBUTTONDOWN)
@ -457,15 +450,18 @@ void CGuiHandler::renderFrame()
if(nullptr != curInt)
curInt->update();
if (settings["general"]["showfps"].Bool())
if(settings["general"]["showfps"].Bool())
drawFPSCounter();
// draw the mouse cursor and update the screen
CCS->curh->render();
SDL_UpdateTexture(screenTexture, nullptr, screen->pixels, screen->pitch);
SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr);
CCS->curh->render();
SDL_RenderPresent(mainRenderer);
disposed.clear();
}
mainFPSmng->framerateDelay(); // holds a constant FPS

View File

@ -58,10 +58,12 @@ class CGuiHandler
{
public:
CFramerateManager * mainFPSmng; //to keep const framerate
std::list<IShowActivatable *> listInt; //list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on)
std::list<std::shared_ptr<IShowActivatable>> listInt; //list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on)
CGStatusBar * statusbar;
private:
std::vector<std::shared_ptr<IShowActivatable>> disposed;
std::atomic<bool> continueEventHandling;
typedef std::list<CIntObject*> CIntObjectList;
@ -86,7 +88,7 @@ public:
public:
//objs to blit
std::vector<IShowable*> objsToBlit;
std::vector<std::shared_ptr<IShowActivatable>> objsToBlit;
SDL_Event * current; //current event - can be set to nullptr to stop handling event
IUpdateable *curInt;
@ -106,11 +108,19 @@ public:
void totalRedraw(); //forces total redraw (using showAll), sets a flag, method gets called at the end of the rendering
void simpleRedraw(); //update only top interface and draw background from buffer, sets a flag, method gets called at the end of the rendering
void popInt(IShowActivatable *top); //removes given interface from the top and activates next
void popIntTotally(IShowActivatable *top); //deactivates, deletes, removes given interface from the top and activates next
void pushInt(IShowActivatable *newInt); //deactivate old top interface, activates this one and pushes to the top
void pushInt(std::shared_ptr<IShowActivatable> newInt); //deactivate old top interface, activates this one and pushes to the top
template <typename T, typename ... Args>
void pushIntT(Args && ... args)
{
auto newInt = std::make_shared<T>(std::forward<Args>(args)...);
pushInt(newInt);
}
void popInts(int howMany); //pops one or more interfaces - deactivates top, deletes and removes given number of interfaces, activates new front
IShowActivatable *topInt(); //returns top interface
void popInt(std::shared_ptr<IShowActivatable> top); //removes given interface from the top and activates next
std::shared_ptr<IShowActivatable> topInt(); //returns top interface
void updateTime(); //handles timeInterested
void handleEvents(); //takes events from queue and calls interested objects

View File

@ -164,11 +164,6 @@ void CIntObject::printAtLoc(const std::string & text, int x, int y, EFonts font,
graphics->fonts[font]->renderTextLeft(dst, text, kolor, Point(pos.x + x, pos.y + y));
}
void CIntObject::printAtRightLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst)
{
graphics->fonts[font]->renderTextRight(dst, text, kolor, Point(pos.x + x, pos.y + y));
}
void CIntObject::printAtMiddleLoc(const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst)
{
printAtMiddleLoc(text, Point(x,y), font, kolor, dst);
@ -194,11 +189,6 @@ void CIntObject::printAtMiddleWBLoc( const std::string & text, int x, int y, EFo
graphics->fonts[font]->renderTextLinesCenter(dst, CMessage::breakText(text, charpr, font), kolor, Point(pos.x + x, pos.y + y));
}
void CIntObject::printToLoc( const std::string & text, int x, int y, EFonts font, SDL_Color kolor, SDL_Surface * dst )
{
graphics->fonts[font]->renderTextRight(dst, text, kolor, Point(pos.x + x, pos.y + y));
}
void CIntObject::addUsedEvents(ui16 newActions)
{
if (active_m)
@ -223,7 +213,7 @@ void CIntObject::disable()
void CIntObject::enable()
{
if(!active_m && parent_m->active)
if(!active_m && (!parent_m || parent_m->active))
activate();
recActions = 255;
@ -302,11 +292,6 @@ void CIntObject::removeChild(CIntObject * child, bool adjustPosition)
child->pos -= pos;
}
void CIntObject::drawBorderLoc(SDL_Surface * sur, const Rect &r, const int3 &color)
{
CSDL_Ext::drawBorder(sur, r + pos, color);
}
void CIntObject::redraw()
{
//currently most of calls come from active objects so this check won't affect them
@ -375,3 +360,16 @@ void CKeyShortcut::keyPressed(const SDL_KeyboardEvent & key)
}
}
WindowBase::WindowBase(int used_, Point pos_)
: CIntObject(used_, pos_)
{
}
void WindowBase::close()
{
if(GH.topInt().get() != this)
logGlobal->error("Only top interface must be closed");
GH.popInts(1);
}

View File

@ -183,12 +183,9 @@ public:
/*
* Functions that should be used only by specific GUI elements. Don't use them unless you really know why they are here
*/
//wrappers for CSDL_Ext methods. This versions use coordinates relative to pos
void drawBorderLoc(SDL_Surface * sur, const Rect &r, const int3 &color);
//functions for printing text. Use CLabel where possible instead
void printAtLoc(const std::string & text, int x, int y, EFonts font, SDL_Color color, SDL_Surface * dst);
void printToLoc(const std::string & text, int x, int y, EFonts font, SDL_Color color, SDL_Surface * dst);
void printAtRightLoc(const std::string & text, int x, int y, EFonts font, SDL_Color color, SDL_Surface * dst);
void printAtMiddleLoc(const std::string & text, int x, int y, EFonts font, SDL_Color color, SDL_Surface * dst);
void printAtMiddleLoc(const std::string & text, const Point &p, EFonts font, SDL_Color color, SDL_Surface * dst);
void printAtMiddleWBLoc(const std::string & text, int x, int y, EFonts font, int charsPerLine, SDL_Color color, SDL_Surface * dst);
@ -211,3 +208,11 @@ public:
CKeyShortcut(std::set<int> Keys);
virtual void keyPressed(const SDL_KeyboardEvent & key) override; //call-in
};
class WindowBase : public CIntObject
{
public:
WindowBase(int used_ = 0, Point pos_ = Point());
protected:
void close();
};

View File

@ -39,7 +39,7 @@ void SDL_UpdateRect(SDL_Surface *surface, int x, int y, int w, int h)
SDL_Surface * CSDL_Ext::newSurface(int w, int h, SDL_Surface * mod) //creates new surface, with flags/format same as in surface given
{
SDL_Surface * ret = SDL_CreateRGBSurface(mod->flags,w,h,mod->format->BitsPerPixel,mod->format->Rmask,mod->format->Gmask,mod->format->Bmask,mod->format->Amask);
SDL_Surface * ret = SDL_CreateRGBSurface(0,w,h,mod->format->BitsPerPixel,mod->format->Rmask,mod->format->Gmask,mod->format->Bmask,mod->format->Amask);
if (mod->format->palette)
{
assert(ret->format->palette);
@ -65,7 +65,7 @@ SDL_Surface * CSDL_Ext::createSurfaceWithBpp(int width, int height)
Channels::px<bpp>::b.set((Uint8*)&bMask, 255);
Channels::px<bpp>::a.set((Uint8*)&aMask, 255);
return SDL_CreateRGBSurface( SDL_SWSURFACE, width, height, bpp * 8, rMask, gMask, bMask, aMask);
return SDL_CreateRGBSurface(0, width, height, bpp * 8, rMask, gMask, bMask, aMask);
}
bool isItIn(const SDL_Rect * rect, int x, int y)

View File

@ -394,14 +394,14 @@ void CBonusSelection::goBack()
}
else
{
GH.popIntTotally(this);
close();
}
// TODO: we can actually only pop bonus selection interface for custom campaigns
// Though this would require clearing CLobbyScreen::bonusSel pointer when poping this interface
/*
else
{
GH.popIntTotally(this);
close();
CSH->state = EClientState::LOBBY;
}
*/
@ -420,7 +420,7 @@ void CBonusSelection::startMap()
const CCampaignScenario & scenario = getCampaign()->camp->scenarios[CSH->campaignMap];
if(scenario.prolog.hasPrologEpilog)
{
GH.pushInt(new CPrologEpilogVideo(scenario.prolog, exitCb));
GH.pushIntT<CPrologEpilogVideo>(scenario.prolog, exitCb);
}
else
{
@ -430,7 +430,7 @@ void CBonusSelection::startMap()
if(LOCPLINT) // we're currently ingame, so ask for starting new map and end game
{
GH.popInt(this);
close();
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [=]()
{
showPrologVideo();
@ -444,7 +444,7 @@ void CBonusSelection::startMap()
void CBonusSelection::restartMap()
{
GH.popInt(this);
close();
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[67], [=]()
{
CSH->startCampaignScenario();

View File

@ -88,7 +88,11 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
buttonStart->assignedKeys.insert(SDLK_RETURN);
buttonBack = std::make_shared<CButton>(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [&](){CSH->sendClientDisconnecting(); GH.popIntTotally(this);}, SDLK_ESCAPE);
buttonBack = std::make_shared<CButton>(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [&]()
{
CSH->sendClientDisconnecting();
close();
}, SDLK_ESCAPE);
}
CLobbyScreen::~CLobbyScreen()
@ -138,7 +142,7 @@ void CLobbyScreen::startScenario(bool allowOnlyAI)
}
catch(ExceptionNoTemplate & e)
{
GH.pushInt(CInfoWindow::create(CGI->generaltexth->allTexts[751]));
CInfoWindow::showInfoDialog(std::ref(CGI->generaltexth->allTexts[751]), CInfoWindow::TCompsInfo(), PlayerColor(1));
}
catch(...)
{

View File

@ -31,5 +31,5 @@ public:
const CMapInfo * getMapInfo() override;
const StartInfo * getStartInfo() override;
CBonusSelection * bonusSel;
std::shared_ptr<CBonusSelection> bonusSel;
};

View File

@ -80,7 +80,7 @@ void CSavingScreen::saveGame()
Settings lastSave = settings.write["general"]["lastSave"];
lastSave->String() = path;
LOCPLINT->cb->save(path);
GH.popIntTotally(this);
close();
};
if(CResourceHandler::get("local")->existsResource(ResourceID(path, EResType::CLIENT_SAVEGAME)))

View File

@ -39,7 +39,7 @@ CScenarioInfoScreen::CScenarioInfoScreen()
card->changeSelection();
card->iconDifficulty->setSelected(getCurrentDifficulty());
buttonBack = std::make_shared<CButton>(Point(584, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], std::bind(&CGuiHandler::popIntTotally, &GH, this), SDLK_ESCAPE);
buttonBack = std::make_shared<CButton>(Point(584, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [=](){ close();}, SDLK_ESCAPE);
}
CScenarioInfoScreen::~CScenarioInfoScreen()

View File

@ -12,7 +12,7 @@
#include "CSelectionBase.h"
/// Scenario information screen shown during the game
class CScenarioInfoScreen : public CIntObject, public ISelectionScreenInfo
class CScenarioInfoScreen : public WindowBase, public ISelectionScreenInfo
{
public:
std::shared_ptr<CButton> buttonBack;

View File

@ -86,7 +86,7 @@ CSelectionBase::CSelectionBase(ESelectionScreen type)
}
pos = background->center();
card = std::make_shared<InfoCard>();
buttonBack = std::make_shared<CButton>(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], std::bind(&CGuiHandler::popIntTotally, &GH, this), SDLK_ESCAPE);
buttonBack = std::make_shared<CButton>(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [=](){ close();}, SDLK_ESCAPE);
}
void CSelectionBase::toggleTab(std::shared_ptr<CIntObject> tab)
@ -367,7 +367,7 @@ void CFlagBox::recreate()
void CFlagBox::clickRight(tribool down, bool previousState)
{
if(down && SEL->getMapInfo())
GH.pushInt(new CFlagBoxTooltipBox(iconsTeamFlags));
GH.pushIntT<CFlagBoxTooltipBox>(iconsTeamFlags);
}
CFlagBox::CFlagBoxTooltipBox::CFlagBoxTooltipBox(std::shared_ptr<CAnimation> icons)

View File

@ -422,7 +422,7 @@ void OptionsTab::SelectedBox::clickRight(tribool down, bool previousState)
if(settings.hero == -2 && !SEL->getPlayerInfo(settings.color.getNum()).hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)
return;
GH.pushInt(new CPlayerOptionTooltipBox(*this));
GH.pushIntT<CPlayerOptionTooltipBox>(*this);
}
}

View File

@ -79,8 +79,7 @@ std::shared_ptr<CButton> CCampaignScreen::createExitButton(const JsonNode & butt
if(!button["help"].isNull() && button["help"].Float() > 0)
help = CGI->generaltexth->zelp[button["help"].Float()];
std::function<void()> close = std::bind(&CGuiHandler::popIntTotally, &GH, this);
return std::make_shared<CButton>(Point(button["x"].Float(), button["y"].Float()), button["name"].String(), help, close, button["hotkey"].Float());
return std::make_shared<CButton>(Point(button["x"].Float(), button["y"].Float()), button["name"].String(), help, [=](){ close();}, button["hotkey"].Float());
}
CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode & config)

View File

@ -60,7 +60,7 @@
namespace fs = boost::filesystem;
CMainMenu * CMM = nullptr;
std::shared_ptr<CMainMenu> CMM;
ISelectionScreenInfo * SEL;
static void do_quit()
@ -176,7 +176,7 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
case 0:
return std::bind(CMainMenu::openLobby, ESelectionScreen::newGame, true, nullptr, ELoadMode::NONE);
case 1:
return []() { GH.pushInt(new CMultiMode(ESelectionScreen::newGame)); };
return []() { GH.pushIntT<CMultiMode>(ESelectionScreen::newGame); };
case 2:
return std::bind(CMainMenu::openLobby, ESelectionScreen::campaignList, true, nullptr, ELoadMode::NONE);
case 3:
@ -191,7 +191,7 @@ static std::function<void()> genCommand(CMenuScreen * menu, std::vector<std::str
case 0:
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::SINGLE);
case 1:
return []() { GH.pushInt(new CMultiMode(ESelectionScreen::loadGame)); };
return []() { GH.pushIntT<CMultiMode>(ESelectionScreen::loadGame); };
case 2:
return std::bind(CMainMenu::openLobby, ESelectionScreen::loadGame, true, nullptr, ELoadMode::CAMPAIGN);
case 3:
@ -279,8 +279,7 @@ CMainMenu::CMainMenu()
pos.h = screen->h;
GH.defActionsDef = 63;
CMM = this;
menu = new CMenuScreen(CMainMenuConfig::get().getConfig()["window"]);
menu = std::make_shared<CMenuScreen>(CMainMenuConfig::get().getConfig()["window"]);
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
backgroundAroundMenu = std::make_shared<CFilledTexture>("DIBOXBCK", pos);
}
@ -288,8 +287,6 @@ CMainMenu::CMainMenu()
CMainMenu::~CMainMenu()
{
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
if(CMM == this)
CMM = nullptr;
if(GH.curInt == this)
GH.curInt = nullptr;
@ -297,12 +294,12 @@ CMainMenu::~CMainMenu()
void CMainMenu::update()
{
if(CMM != this) //don't update if you are not a main interface
if(CMM != this->shared_from_this()) //don't update if you are not a main interface
return;
if(GH.listInt.empty())
{
GH.pushInt(this);
GH.pushInt(CMM);
GH.pushInt(menu);
menu->switchToTab(0);
}
@ -313,7 +310,7 @@ void CMainMenu::update()
// check for null othervice crash on finishing a campaign
// /FIXME: find out why GH.listInt is empty to begin with
if(GH.topInt() != nullptr)
if(GH.topInt())
GH.topInt()->show(screen);
}
@ -323,7 +320,7 @@ void CMainMenu::openLobby(ESelectionScreen screenType, bool host, const std::vec
CSH->screenType = screenType;
CSH->loadMode = loadMode;
GH.pushInt(new CSimpleJoinScreen(host));
GH.pushIntT<CSimpleJoinScreen>(host);
}
void CMainMenu::openCampaignLobby(const std::string & campaignFileName)
@ -337,23 +334,23 @@ void CMainMenu::openCampaignLobby(std::shared_ptr<CCampaignState> campaign)
CSH->resetStateForLobby(StartInfo::CAMPAIGN);
CSH->screenType = ESelectionScreen::campaignList;
CSH->campaignStateToSend = campaign;
GH.pushInt(new CSimpleJoinScreen());
GH.pushIntT<CSimpleJoinScreen>();
}
void CMainMenu::openCampaignScreen(std::string name)
{
if(vstd::contains(CMainMenuConfig::get().getCampaigns().Struct(), name))
{
GH.pushInt(new CCampaignScreen(CMainMenuConfig::get().getCampaigns()[name]));
GH.pushIntT<CCampaignScreen>(CMainMenuConfig::get().getCampaigns()[name]);
return;
}
logGlobal->error("Unknown campaign set: %s", name);
}
CMainMenu * CMainMenu::create()
std::shared_ptr<CMainMenu> CMainMenu::create()
{
if(!CMM)
CMM = new CMainMenu();
CMM = std::shared_ptr<CMainMenu>(new CMainMenu());
GH.terminate_cond->set(false);
return CMM;
@ -381,19 +378,21 @@ CMultiMode::CMultiMode(ESelectionScreen ScreenType)
buttonHotseat = std::make_shared<CButton>(Point(373, 78), "MUBHOT.DEF", CGI->generaltexth->zelp[266], std::bind(&CMultiMode::hostTCP, this));
buttonHost = std::make_shared<CButton>(Point(373, 78 + 57 * 1), "MUBHOST.DEF", CButton::tooltip("Host TCP/IP game", ""), std::bind(&CMultiMode::hostTCP, this));
buttonJoin = std::make_shared<CButton>(Point(373, 78 + 57 * 2), "MUBJOIN.DEF", CButton::tooltip("Join TCP/IP game", ""), std::bind(&CMultiMode::joinTCP, this));
buttonCancel = std::make_shared<CButton>(Point(373, 424), "MUBCANC.DEF", CGI->generaltexth->zelp[288], [&]() { GH.popIntTotally(this);}, SDLK_ESCAPE);
buttonCancel = std::make_shared<CButton>(Point(373, 424), "MUBCANC.DEF", CGI->generaltexth->zelp[288], [=](){ close();}, SDLK_ESCAPE);
}
void CMultiMode::hostTCP()
{
GH.popIntTotally(this);
GH.pushInt(new CMultiPlayers(settings["general"]["playerName"].String(), screenType, true, ELoadMode::MULTI));
auto savedScreenType = screenType;
close();
GH.pushIntT<CMultiPlayers>(settings["general"]["playerName"].String(), savedScreenType, true, ELoadMode::MULTI);
}
void CMultiMode::joinTCP()
{
GH.popIntTotally(this);
GH.pushInt(new CMultiPlayers(settings["general"]["playerName"].String(), screenType, false, ELoadMode::MULTI));
auto savedScreenType = screenType;
close();
GH.pushIntT<CMultiPlayers>(settings["general"]["playerName"].String(), savedScreenType, false, ELoadMode::MULTI);
}
void CMultiMode::onNameChange(std::string newText)
@ -420,7 +419,7 @@ CMultiPlayers::CMultiPlayers(const std::string & firstPlayer, ESelectionScreen S
}
buttonOk = std::make_shared<CButton>(Point(95, 338), "MUBCHCK.DEF", CGI->generaltexth->zelp[560], std::bind(&CMultiPlayers::enterSelectionScreen, this), SDLK_RETURN);
buttonCancel = std::make_shared<CButton>(Point(205, 338), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CGuiHandler::popIntTotally, std::ref(GH), this), SDLK_ESCAPE);
buttonCancel = std::make_shared<CButton>(Point(205, 338), "MUBCANC.DEF", CGI->generaltexth->zelp[561], [=](){ close();}, SDLK_ESCAPE);
statusBar = std::make_shared<CGStatusBar>(std::make_shared<CPicture>(Rect(7, 381, 348, 18), 0)); //226, 472
inputNames[0]->setText(firstPlayer, true);
@ -497,9 +496,9 @@ void CSimpleJoinScreen::leaveScreen()
textTitle->setText("Closing...");
CSH->state = EClientState::CONNECTION_CANCELLED;
}
else if(GH.listInt.size() && GH.listInt.front() == this)
else if(GH.listInt.size() && GH.listInt.front().get() == this)
{
GH.popIntTotally(this);
close();
}
}
@ -516,9 +515,9 @@ void CSimpleJoinScreen::connectThread(const std::string addr, const ui16 port)
else
CSH->justConnectToServer(addr, port);
if(GH.listInt.size() && GH.listInt.front() == this)
if(GH.listInt.size() && GH.listInt.front().get() == this)
{
GH.popIntTotally(this);
close();
}
}

View File

@ -69,7 +69,7 @@ public:
};
/// Multiplayer mode
class CMultiMode : public CIntObject
class CMultiMode : public WindowBase
{
public:
ESelectionScreen screenType;
@ -89,7 +89,7 @@ public:
};
/// Hot seat player window
class CMultiPlayers : public CIntObject
class CMultiPlayers : public WindowBase
{
bool host;
ELoadMode loadMode;
@ -124,14 +124,14 @@ private:
};
/// Handles background screen, loads graphics for victory/loss condition and random town or hero selection
class CMainMenu : public CIntObject, public IUpdateable
class CMainMenu : public CIntObject, public IUpdateable, public std::enable_shared_from_this<CMainMenu>
{
std::shared_ptr<CFilledTexture> backgroundAroundMenu;
CMainMenu(); //Use CMainMenu::create
public:
CMenuScreen * menu;
std::shared_ptr<CMenuScreen> menu;
~CMainMenu();
void update() override;
@ -140,14 +140,14 @@ public:
static void openCampaignLobby(std::shared_ptr<CCampaignState> campaign);
void openCampaignScreen(std::string name);
static CMainMenu * create();
static std::shared_ptr<CMainMenu> create();
static std::shared_ptr<CPicture> createPicture(const JsonNode & config);
};
/// Simple window to enter the server's address.
class CSimpleJoinScreen : public CIntObject
class CSimpleJoinScreen : public WindowBase
{
std::shared_ptr<CPicture> background;
std::shared_ptr<CTextBox> textTitle;
@ -179,4 +179,4 @@ public:
void showAll(SDL_Surface * to) override;
};
extern CMainMenu * CMM;
extern std::shared_ptr<CMainMenu> CMM;

View File

@ -58,7 +58,7 @@ void CPrologEpilogVideo::show(SDL_Surface * to)
void CPrologEpilogVideo::clickLeft(tribool down, bool previousState)
{
GH.popInt(this);
close();
CCS->soundh->stopSound(voiceSoundHandle);
exitCb();
}

View File

@ -1250,21 +1250,16 @@ void CAdvMapPanel::addChildToPanel(std::shared_ptr<CIntObject> obj, ui8 actions)
CAdvMapWorldViewPanel::CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, SDL_Surface * bg, Point position, int spaceBottom, const PlayerColor &color)
: CAdvMapPanel(bg, position), icons(_icons)
{
fillerHeight = bg ? spaceBottom - pos.y - pos.h : 0;
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
int fillerHeight = bg ? spaceBottom - pos.y - pos.h : 0;
if (fillerHeight > 0)
if(fillerHeight > 0)
{
tmpBackgroundFiller = CMessage::drawDialogBox(pos.w, fillerHeight, color);
backgroundFiller = std::make_shared<CFilledTexture>("DIBOXBCK", Rect(0, pos.h, pos.w, fillerHeight));
}
else
tmpBackgroundFiller = nullptr;
}
CAdvMapWorldViewPanel::~CAdvMapWorldViewPanel()
{
if (tmpBackgroundFiller)
SDL_FreeSurface(tmpBackgroundFiller);
}
CAdvMapWorldViewPanel::~CAdvMapWorldViewPanel() = default;
void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor & color, int indexOffset)
{
@ -1275,13 +1270,6 @@ void CAdvMapWorldViewPanel::recolorIcons(const PlayerColor & color, int indexOff
const auto & data = iconsData.at(idx);
currentIcons[idx]->setFrame(data.first + indexOffset);
}
if(fillerHeight > 0)
{
if(tmpBackgroundFiller)
SDL_FreeSurface(tmpBackgroundFiller);
tmpBackgroundFiller = CMessage::drawDialogBox(pos.w, fillerHeight, color);
}
}
void CAdvMapWorldViewPanel::addChildIcon(std::pair<int, Point> data, int indexOffset)
@ -1290,13 +1278,3 @@ void CAdvMapWorldViewPanel::addChildIcon(std::pair<int, Point> data, int indexOf
iconsData.push_back(data);
currentIcons.push_back(std::make_shared<CAnimImage>(icons, data.first + indexOffset, 0, data.second.x, data.second.y));
}
void CAdvMapWorldViewPanel::showAll(SDL_Surface * to)
{
if (tmpBackgroundFiller)
{
blitAt(tmpBackgroundFiller, pos.x, pos.y + pos.h, to);
}
CAdvMapPanel::showAll(to);
}

View File

@ -16,6 +16,7 @@ class CArmedInstance;
class CAnimation;
class CAnimImage;
class CShowableAnim;
class CFilledTexture;
class CGGarrison;
class CGObjectInstance;
class CGHeroInstance;
@ -384,9 +385,8 @@ class CAdvMapWorldViewPanel : public CAdvMapPanel
std::vector<std::pair<int, Point>> iconsData;
/// ptrs to child-pictures constructed from iconsData
std::vector<std::shared_ptr<CAnimImage>> currentIcons;
/// temporary surface drawn below world view panel on higher resolutions (won't be needed when world view panel is configured for extraResolutions mod)
SDL_Surface * tmpBackgroundFiller;
int fillerHeight;
/// surface drawn below world view panel on higher resolutions (won't be needed when world view panel is configured for extraResolutions mod)
std::shared_ptr<CFilledTexture> backgroundFiller;
std::shared_ptr<CAnimation> icons;
public:
CAdvMapWorldViewPanel(std::shared_ptr<CAnimation> _icons, SDL_Surface * bg, Point position, int spaceBottom, const PlayerColor &color);
@ -395,7 +395,6 @@ public:
void addChildIcon(std::pair<int, Point> data, int indexOffset);
/// recreates all pictures from given def to recolor them according to current player color
void recolorIcons(const PlayerColor & color, int indexOffset);
void showAll(SDL_Surface * to) override;
};
class CInGameConsole : public CIntObject

View File

@ -126,7 +126,7 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH)
{
if(ourArt->artType->id == ArtifactID::SPELLBOOK)
GH.pushInt(new CSpellWindow(ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt));
GH.pushIntT<CSpellWindow>(ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt);
}
if (!down && previousState)
@ -678,14 +678,14 @@ CArtifactsOfHero::~CArtifactsOfHero()
void CArtifactsOfHero::updateParentWindow()
{
if (CHeroWindow* chw = dynamic_cast<CHeroWindow*>(GH.topInt()))
if (CHeroWindow* chw = dynamic_cast<CHeroWindow*>(GH.topInt().get()))
{
if(updateState)
chw->curHero = curHero;
else
chw->update(curHero, true);
}
else if(CExchangeWindow* cew = dynamic_cast<CExchangeWindow*>(GH.topInt()))
else if(CExchangeWindow* cew = dynamic_cast<CExchangeWindow*>(GH.topInt().get()))
{
//use our copy of hero to draw window
if(cew->heroInst[0]->id == curHero->id)

View File

@ -161,7 +161,7 @@ bool CGarrisonSlot::viewInfo()
elem->block(true);
redraw();
GH.pushInt(new CStackWindow(myStack, dism, pom, upgr));
GH.pushIntT<CStackWindow>(myStack, dism, pom, upgr);
return true;
}
@ -170,7 +170,7 @@ bool CGarrisonSlot::viewInfo()
bool CGarrisonSlot::highlightOrDropArtifact()
{
bool artSelected = false;
if (CWindowWithArtifacts* chw = dynamic_cast<CWindowWithArtifacts*>(GH.topInt())) //dirty solution
if (CWindowWithArtifacts* chw = dynamic_cast<CWindowWithArtifacts*>(GH.topInt().get())) //dirty solution
{
const std::shared_ptr<CArtifactsOfHero::SCommonPart> commonInfo = chw->getCommonPart();
const CArtifactInstance * art = nullptr;
@ -241,8 +241,8 @@ bool CGarrisonSlot::split()
int countLeft = selection->myStack ? selection->myStack->count : 0;
int countRight = myStack ? myStack->count : 0;
GH.pushInt(new CSplitWindow(selection->creature, std::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
minLeft, minRight, countLeft, countRight));
GH.pushIntT<CSplitWindow>(selection->creature, std::bind(&CGarrisonInt::splitStacks, owner, _1, _2),
minLeft, minRight, countLeft, countRight);
return true;
}
@ -278,7 +278,7 @@ void CGarrisonSlot::clickRight(tribool down, bool previousState)
{
if(creature && down)
{
GH.pushInt(new CStackWindow(myStack, true));
GH.pushIntT<CStackWindow>(myStack, true);
}
}

View File

@ -173,7 +173,7 @@ void CPicture::createSimpleRect(const Rect &r, bool screenFormat, ui32 color)
if(screenFormat)
bg = CSDL_Ext::newSurface(r.w, r.h);
else
bg = SDL_CreateRGBSurface(SDL_SWSURFACE, r.w, r.h, 8, 0, 0, 0, 0);
bg = SDL_CreateRGBSurface(0, r.w, r.h, 8, 0, 0, 0, 0);
SDL_FillRect(bg, nullptr, color);
freeSurf = true;
@ -296,7 +296,7 @@ void CAnimImage::playerColored(PlayerColor currPlayer)
}
CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Delay, size_t Group):
anim(new CAnimation(name, Flags & USE_RLE)),
anim(std::make_shared<CAnimation>(name)),
group(Group),
frame(0),
first(0),
@ -319,7 +319,6 @@ CShowableAnim::CShowableAnim(int x, int y, std::string name, ui8 Flags, ui32 Del
CShowableAnim::~CShowableAnim()
{
anim->unloadGroup(group);
delete anim;
}
void CShowableAnim::setAlpha(ui32 alphaValue)
@ -410,8 +409,8 @@ void CShowableAnim::blitImage(size_t frame, size_t group, SDL_Surface *to)
assert(to);
Rect src( xOffset, yOffset, pos.w, pos.h);
auto img = anim->getImage(frame, group);
if (img)
img->draw(to, pos.x-xOffset, pos.y-yOffset, &src, alpha);
if(img)
img->draw(to, pos.x, pos.y, &src, alpha);
}
void CShowableAnim::rotate(bool on, bool vertical)
@ -423,15 +422,11 @@ void CShowableAnim::rotate(bool on, bool vertical)
flags &= ~flag;
}
CCreatureAnim::CCreatureAnim(int x, int y, std::string name, Rect picPos, ui8 flags, EAnimType type):
CCreatureAnim::CCreatureAnim(int x, int y, std::string name, ui8 flags, EAnimType type):
CShowableAnim(x,y,name,flags,4,type)
{
xOffset = picPos.x;
yOffset = picPos.y;
if (picPos.w)
pos.w = picPos.w;
if (picPos.h)
pos.h = picPos.h;
xOffset = 0;
yOffset = 0;
}
void CCreatureAnim::loopPreview(bool warMachine)

View File

@ -105,12 +105,11 @@ public:
BASE=1, //base frame will be blitted before current one
HORIZONTAL_FLIP=2, //TODO: will be displayed rotated
VERTICAL_FLIP=4, //TODO: will be displayed rotated
USE_RLE=8, //RLE-d version, support full alpha-channel for 8-bit images
PLAYER_COLORED=16, //TODO: all loaded images will be player-colored
PLAY_ONCE=32 //play animation only once and stop at last frame
};
protected:
CAnimation * anim;
std::shared_ptr<CAnimation> anim;
size_t group, frame;//current frame
@ -227,7 +226,6 @@ public:
//clear queue and set animation to this sequence
void clearAndSet(EAnimType type);
CCreatureAnim(int x, int y, std::string name, Rect picPos,
ui8 flags= USE_RLE, EAnimType = HOLDING );
CCreatureAnim(int x, int y, std::string name, ui8 flags = 0, EAnimType = HOLDING);
};

View File

@ -457,7 +457,7 @@ CCreaturePic::CCreaturePic(int x, int y, const CCreature * cre, bool Big, bool A
bg = std::make_shared<CPicture>(CGI->townh->factions[faction]->creatureBg130);
else
bg = std::make_shared<CPicture>(CGI->townh->factions[faction]->creatureBg120);
anim = std::make_shared<CCreatureAnim>(0, 0, cre->animDefName, Rect());
anim = std::make_shared<CCreatureAnim>(0, 0, cre->animDefName);
anim->clipRect(cre->isDoubleWide()?170:150, 155, bg->pos.w, bg->pos.h);
anim->startPreview(cre->hasBonusOfType(Bonus::SIEGE_WEAPON));

View File

@ -59,7 +59,7 @@
#define ADVOPT (conf.go()->ac)
using namespace CSDL_Ext;
CAdvMapInt *adventureInt;
std::shared_ptr<CAdvMapInt> adventureInt;
static void setScrollingCursor(ui8 direction)
{
@ -565,7 +565,6 @@ CAdvMapInt::CAdvMapInt():
swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
swipeTargetPosition(int3(-1, -1, -1))
{
adventureInt = this;
pos.x = pos.y = 0;
pos.w = screen->w;
pos.h = screen->h;
@ -723,7 +722,7 @@ CAdvMapInt::~CAdvMapInt()
void CAdvMapInt::fshowOverview()
{
GH.pushInt(new CKingdomInterface());
GH.pushIntT<CKingdomInterface>();
}
void CAdvMapInt::fworldViewBack()
@ -811,17 +810,17 @@ void CAdvMapInt::fshowSpellbok()
centerOn(selection);
GH.pushInt(new CSpellWindow(curHero(), LOCPLINT, false));
GH.pushIntT<CSpellWindow>(curHero(), LOCPLINT, false);
}
void CAdvMapInt::fadventureOPtions()
{
GH.pushInt(new CAdventureOptions());
GH.pushIntT<CAdventureOptions>();
}
void CAdvMapInt::fsystemOptions()
{
GH.pushInt(new CSystemOptionsWindow());
GH.pushIntT<CSystemOptionsWindow>();
}
void CAdvMapInt::fnextHero()
@ -1088,7 +1087,7 @@ void CAdvMapInt::handleMapScrollingUpdate()
int scrollSpeed = settings["adventure"]["scrollSpeed"].Float();
//if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
if((animValHitCount % (4 / scrollSpeed)) == 0
&& ((GH.topInt() == this) || isCtrlKeyDown()))
&& ((GH.topInt().get() == this) || isCtrlKeyDown()))
{
if((scrollingDir & LEFT) && (position.x > -CGI->mh->frameW))
position.x--;
@ -1224,7 +1223,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
return;
case SDLK_s:
if(isActive() && key.type == SDL_KEYUP)
GH.pushInt(new CSavingScreen());
GH.pushIntT<CSavingScreen>();
return;
case SDLK_d:
{
@ -1274,7 +1273,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
}
case SDLK_ESCAPE:
{
if(isActive() || GH.topInt() != this || !spellBeingCasted || key.state != SDL_PRESSED)
if(isActive() || GH.topInt().get() != this || !spellBeingCasted || key.state != SDL_PRESSED)
return;
leaveCastingMode();
@ -1300,7 +1299,7 @@ void CAdvMapInt::keyPressed(const SDL_KeyboardEvent & key)
}
if(townWithMarket) //if any town has marketplace, open window
GH.pushInt(new CMarketplaceWindow(townWithMarket));
GH.pushIntT<CMarketplaceWindow>(townWithMarket);
else //if not - complain
LOCPLINT->showInfoDialog("No available marketplace!");
}
@ -1957,11 +1956,11 @@ void CAdventureOptions::showScenarioInfo()
{
if(LOCPLINT->cb->getStartInfo()->campState)
{
GH.pushInt(new CBonusSelection());
GH.pushIntT<CBonusSelection>();
}
else
{
GH.pushInt(new CScenarioInfoScreen());
GH.pushIntT<CScenarioInfoScreen>();
}
}

View File

@ -266,4 +266,4 @@ public:
};
extern CAdvMapInt *adventureInt;
extern std::shared_ptr<CAdvMapInt> adventureInt;

View File

@ -41,13 +41,12 @@
#include "../../lib/mapObjects/CGTownInstance.h"
CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town, const CStructure * Str)
: CShowableAnim(0, 0, Str->defName, CShowableAnim::BASE | CShowableAnim::USE_RLE),
: CShowableAnim(0, 0, Str->defName, CShowableAnim::BASE),
parent(Par),
town(Town),
str(Str),
stateCounter(80)
{
recActions = ACTIVATE | DEACTIVATE | DISPOSE | SHARE_POS;
addUsedEvents(LCLICK | RCLICK | HOVER);
pos.x += str->pos.x;
pos.y += str->pos.y;
@ -131,7 +130,7 @@ void CBuildingRect::clickRight(tribool down, bool previousState)
else
{
int level = ( bid - BuildingID::DWELL_FIRST ) % GameConstants::CREATURES_PER_TOWN;
GH.pushInt(new CDwellingInfoBox(parent->pos.x+parent->pos.w/2, parent->pos.y+parent->pos.h/2, town, level));
GH.pushIntT<CDwellingInfoBox>(parent->pos.x+parent->pos.w/2, parent->pos.y+parent->pos.h/2, town, level);
}
}
}
@ -421,7 +420,7 @@ void CHeroGSlot::clickLeft(tribool down, bool previousState)
{
setHighlight(true);
owner->garr->selectSlot(nullptr);
showAll(screen2);
redraw();
}
//refresh statusbar
@ -434,7 +433,7 @@ void CHeroGSlot::clickRight(tribool down, bool previousState)
{
if(hero && down)
{
GH.pushInt(new CInfoBoxPopup(Point(pos.x + 175, pos.y + 100), hero));
GH.pushIntT<CInfoBoxPopup>(Point(pos.x + 175, pos.y + 100), hero);
}
}
@ -522,17 +521,26 @@ void HeroSlots::swapArmies()
}
template <class T>
class SORTHELP
{
public:
bool operator() (const std::shared_ptr<T> a, const std::shared_ptr<T> b)
bool operator() (const CIntObject * a, const CIntObject * b)
{
return (*a)<(*b);
auto b1 = dynamic_cast<const CBuildingRect *>(a);
auto b2 = dynamic_cast<const CBuildingRect *>(b);
if(!b1 && !b2)
return intptr_t(a) < intptr_t(b);
if(b1 && !b2)
return false;
if(!b1 && b2)
return true;
return (*b1)<(*b2);
}
};
SORTHELP<CBuildingRect> buildSorter;
SORTHELP buildSorter;
CCastleBuildings::CCastleBuildings(const CGTownInstance* Town):
town(Town),
@ -552,8 +560,7 @@ CCastleBuildings::~CCastleBuildings() = default;
void CCastleBuildings::recreate()
{
selectedBuilding = nullptr;
//TODO: remove show[all] method and try UPDATE+SHOWALL
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(ACTIVATE+SHARE_POS);
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
buildings.clear();
groups.clear();
@ -600,7 +607,8 @@ void CCastleBuildings::recreate()
buildings.push_back(std::make_shared<CBuildingRect>(this, town, toAdd));
}
boost::sort(buildings, buildSorter);
boost::sort(children, buildSorter); //TODO: create building in blit order
}
void CCastleBuildings::addBuilding(BuildingID building)
@ -632,20 +640,6 @@ void CCastleBuildings::removeBuilding(BuildingID building)
recreate();
}
void CCastleBuildings::show(SDL_Surface * to)
{
CIntObject::show(to);
for(auto str : buildings)
str->show(to);
}
void CCastleBuildings::showAll(SDL_Surface * to)
{
CIntObject::showAll(to);
for(auto str : buildings)
str->showAll(to);
}
const CGHeroInstance * CCastleBuildings::getHero()
{
if(town->visitingHero)
@ -689,7 +683,7 @@ void CCastleBuildings::buildingClicked(BuildingID building)
case BuildingID::FORT:
case BuildingID::CITADEL:
case BuildingID::CASTLE:
GH.pushInt(new CFortScreen(town));
GH.pushIntT<CFortScreen>(town);
break;
case BuildingID::VILLAGE_HALL:
@ -700,7 +694,7 @@ void CCastleBuildings::buildingClicked(BuildingID building)
break;
case BuildingID::MARKETPLACE:
GH.pushInt(new CMarketplaceWindow(town, town->visitingHero));
GH.pushIntT<CMarketplaceWindow>(town, town->visitingHero);
break;
case BuildingID::BLACKSMITH:
@ -718,7 +712,7 @@ void CCastleBuildings::buildingClicked(BuildingID building)
case ETownType::DUNGEON://Artifact Merchant
case ETownType::CONFLUX:
if(town->visitingHero)
GH.pushInt(new CMarketplaceWindow(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT));
GH.pushIntT<CMarketplaceWindow>(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT);
else
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
break;
@ -742,14 +736,14 @@ void CCastleBuildings::buildingClicked(BuildingID building)
case ETownType::STRONGHOLD: //Freelancer's Guild
if(getHero())
GH.pushInt(new CMarketplaceWindow(town, getHero(), EMarketMode::CREATURE_RESOURCE));
GH.pushIntT<CMarketplaceWindow>(town, getHero(), EMarketMode::CREATURE_RESOURCE);
else
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
break;
case ETownType::CONFLUX: //Magic University
if (getHero())
GH.pushInt(new CUniversityWindow(getHero(), town));
GH.pushIntT<CUniversityWindow>(getHero(), town);
else
enterBuilding(building);
break;
@ -772,7 +766,7 @@ void CCastleBuildings::buildingClicked(BuildingID building)
break;
case ETownType::NECROPOLIS: //Skeleton Transformer
GH.pushInt( new CTransformerWindow(getHero(), town) );
GH.pushIntT<CTransformerWindow>(getHero(), town);
break;
case ETownType::DUNGEON: //Portal of Summoning
@ -810,7 +804,7 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
int price = CGI->arth->artifacts[artifactID]->price;
bool possible = LOCPLINT->cb->getResourceAmount(Res::GOLD) >= price && !hero->hasArt(artifactID);
CreatureID cre = artifactID.toArtifact()->warMachine;
GH.pushInt(new CBlacksmithDialog(possible, cre, artifactID, hero->id));
GH.pushIntT<CBlacksmithDialog>(possible, cre, artifactID, hero->id);
}
void CCastleBuildings::enterBuilding(BuildingID building)
@ -838,20 +832,20 @@ void CCastleBuildings::enterCastleGate()
}
}
auto gateIcon = std::make_shared<CAnimImage>(town->town->clientInfo.buildingsIcons, BuildingID::CASTLE_GATE);//will be deleted by selection window
GH.pushInt(new CObjectListWindow(availableTowns, gateIcon, CGI->generaltexth->jktexts[40],
CGI->generaltexth->jktexts[41], std::bind (&CCastleInterface::castleTeleport, LOCPLINT->castleInt, _1)));
GH.pushIntT<CObjectListWindow>(availableTowns, gateIcon, CGI->generaltexth->jktexts[40],
CGI->generaltexth->jktexts[41], std::bind (&CCastleInterface::castleTeleport, LOCPLINT->castleInt, _1));
}
void CCastleBuildings::enterDwelling(int level)
{
assert(level >= 0 && level < town->creatures.size());
auto recruitCb = [=](CreatureID id, int count){ LOCPLINT->cb->recruitCreatures(town, town->getUpperArmy(), id, count, level); };
GH.pushInt(new CRecruitmentWindow(town, level, town, recruitCb, -87));
GH.pushIntT<CRecruitmentWindow>(town, level, town, recruitCb, -87);
}
void CCastleBuildings::enterToTheQuickRecruitmentWindow()
{
GH.pushInt(new QuickRecruitmentWindow(town, pos));
GH.pushIntT<QuickRecruitmentWindow>(town, pos);
}
void CCastleBuildings::enterFountain(BuildingID building)
@ -915,7 +909,7 @@ void CCastleBuildings::enterTownHall()
else
{
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[673]);
dynamic_cast<CInfoWindow*>(GH.topInt())->buttons[0]->addCallback(std::bind(&CCastleBuildings::openTownHall, this));
dynamic_cast<CInfoWindow*>(GH.topInt().get())->buttons[0]->addCallback(std::bind(&CCastleBuildings::openTownHall, this));
}
}
else
@ -928,12 +922,12 @@ void CCastleBuildings::openMagesGuild()
{
std::string mageGuildBackground;
mageGuildBackground = LOCPLINT->castleInt->town->town->clientInfo.guildBackground;
GH.pushInt(new CMageGuildScreen(LOCPLINT->castleInt,mageGuildBackground));
GH.pushIntT<CMageGuildScreen>(LOCPLINT->castleInt,mageGuildBackground);
}
void CCastleBuildings::openTownHall()
{
GH.pushInt(new CHallInterface(town));
GH.pushIntT<CHallInterface>(town);
}
CCreaInfo::CCreaInfo(Point position, const CGTownInstance * Town, int Level, bool compact, bool ShowAvailable):
@ -1016,7 +1010,7 @@ void CCreaInfo::clickLeft(tribool down, bool previousState)
{
LOCPLINT->cb->recruitCreatures(town, town->getUpperArmy(), id, count, level);
};
GH.pushInt(new CRecruitmentWindow(town, level, town, recruitCb, offset));
GH.pushIntT<CRecruitmentWindow>(town, level, town, recruitCb, offset);
}
}
@ -1036,7 +1030,7 @@ void CCreaInfo::clickRight(tribool down, bool previousState)
if(down)
{
if (showAvailable)
GH.pushInt(new CDwellingInfoBox(screen->w/2, screen->h/2, town, level));
GH.pushIntT<CDwellingInfoBox>(screen->w/2, screen->h/2, town, level);
else
CRClickPopup::createAndPush(genGrowthText(), std::make_shared<CComponent>(CComponent::creature, creature->idNumber));
}
@ -1141,7 +1135,8 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
CCastleInterface::~CCastleInterface()
{
LOCPLINT->castleInt = nullptr;
if(LOCPLINT->castleInt == this)
LOCPLINT->castleInt = nullptr;
}
void CCastleInterface::updateGarrisons()
@ -1170,12 +1165,13 @@ void CCastleInterface::castleTeleport(int where)
void CCastleInterface::townChange()
{
//TODO: do not recreate window
const CGTownInstance * dest = LOCPLINT->towns[townlist->getSelectedIndex()];
const CGTownInstance * town = this->town;// "this" is going to be deleted
if ( dest == town )
return;
close();
GH.pushInt(new CCastleInterface(dest, town));
GH.pushIntT<CCastleInterface>(dest, town);
}
void CCastleInterface::addBuilding(BuildingID bid)
@ -1299,13 +1295,13 @@ void CHallInterface::CBuildingBox::hover(bool on)
void CHallInterface::CBuildingBox::clickLeft(tribool down, bool previousState)
{
if(previousState && (!down))
GH.pushInt(new CBuildWindow(town,building,state,0));
GH.pushIntT<CBuildWindow>(town,building,state,0);
}
void CHallInterface::CBuildingBox::clickRight(tribool down, bool previousState)
{
if(down)
GH.pushInt(new CBuildWindow(town,building,state,1));
GH.pushIntT<CBuildWindow>(town,building,state,1);
}
CHallInterface::CHallInterface(const CGTownInstance * Town):
@ -1756,7 +1752,7 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
animBG->needRefresh = true;
const CCreature * creature = CGI->creh->creatures[creMachineID];
anim = std::make_shared<CCreatureAnim>(64, 50, creature->animDefName, Rect());
anim = std::make_shared<CCreatureAnim>(64, 50, creature->animDefName);
anim->clipRect(113,125,200,150);
title = std::make_shared<CLabel>(165, 28, FONT_BIG, CENTER, Colors::YELLOW,

View File

@ -156,9 +156,6 @@ public:
void buildingClicked(BuildingID building);
void addBuilding(BuildingID building);
void removeBuilding(BuildingID building);//FIXME: not tested!!!
void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override;
};
/// Creature info window

View File

@ -78,8 +78,8 @@ void CHeroSwitcher::clickLeft(tribool down, bool previousState)
owner->update(hero, true);
#else
const CGHeroInstance * buf = hero;
GH.popIntTotally(parent);
GH.pushInt(new CHeroWindow(buf));
GH.popInts(1);
GH.pushIntT<CHeroWindow>(buf);
#endif // 0
}
}
@ -297,16 +297,16 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
//if we have exchange window with this curHero open
bool noDismiss=false;
for(IShowActivatable * isa : GH.listInt)
for(auto isa : GH.listInt)
{
if(CExchangeWindow * cew = dynamic_cast<CExchangeWindow*>(isa))
if(CExchangeWindow * cew = dynamic_cast<CExchangeWindow*>(isa.get()))
{
for(int g=0; g < cew->heroInst.size(); ++g)
if(cew->heroInst[g] == curHero)
noDismiss = true;
}
if(dynamic_cast<CKingdomInterface*>(isa))
if(dynamic_cast<CKingdomInterface*>(isa.get()))
noDismiss = true;
}
//if player only have one hero and no towns
@ -374,7 +374,7 @@ void CHeroWindow::commanderWindow()
}
else
{
GH.pushInt(new CStackWindow(curHero->commander, false));
GH.pushIntT<CStackWindow>(curHero->commander, false);
}
}

View File

@ -40,8 +40,7 @@ void CQuestLabel::clickLeft(tribool down, bool previousState)
void CQuestLabel::showAll(SDL_Surface * to)
{
if (active)
CMultiLineLabel::showAll (to);
CMultiLineLabel::showAll (to);
}
CQuestIcon::CQuestIcon (const std::string &defname, int index, int x, int y) :
@ -204,8 +203,9 @@ void CQuestLog::recreateLabelList()
void CQuestLog::showAll(SDL_Surface * to)
{
CWindowObject::showAll(to);
if(labels.size() && labels[questIndex]->active)
if(questIndex >= 0 && questIndex < labels.size())
{
//TODO: use child object to selection rect
Rect rect = Rect::around(labels[questIndex]->pos);
rect.x -= 2; // Adjustment needed as we want selection box on top of border in graphics
rect.w += 2;

View File

@ -255,7 +255,7 @@ void CSpellWindow::fexitb()
(myInt->battleInt ? myInt->spellbookSettings.spellbookLastTabBattle : myInt->spellbookSettings.spellbookLastTabAdvmap) = selectedTab;
(myInt->battleInt ? myInt->spellbookSettings.spellbookLastPageBattle : myInt->spellbookSettings.spellbokLastPageAdvmap) = currentPage;
GH.popIntTotally(this);
close();
}
void CSpellWindow::fadvSpellsb()
@ -414,13 +414,13 @@ void CSpellWindow::setCurrentPage(int value)
void CSpellWindow::turnPageLeft()
{
if(settings["video"]["spellbookAnimation"].Bool())
CCS->videoh->openAndPlayVideo("PGTRNLFT.SMK", pos.x+13, pos.y+15, screen);
CCS->videoh->openAndPlayVideo("PGTRNLFT.SMK", pos.x+13, pos.y+15);
}
void CSpellWindow::turnPageRight()
{
if(settings["video"]["spellbookAnimation"].Bool())
CCS->videoh->openAndPlayVideo("PGTRNRGH.SMK", pos.x+13, pos.y+15, screen);
CCS->videoh->openAndPlayVideo("PGTRNRGH.SMK", pos.x+13, pos.y+15);
}
void CSpellWindow::keyPressed(const SDL_KeyboardEvent & key)
@ -568,7 +568,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
else //adventure spell
{
const CGHeroInstance * h = owner->myHero;
GH.popInt(owner);
GH.popInts(1);
auto guard = vstd::makeScopeGuard([this]()
{

View File

@ -605,22 +605,19 @@ void CTradeWindow::setMode(EMarketMode::EMarketMode Mode)
{
const IMarket *m = market;
const CGHeroInstance *h = hero;
CTradeWindow *nwindow = nullptr;
GH.popIntTotally(this);
close();
switch(Mode)
{
case EMarketMode::CREATURE_EXP:
case EMarketMode::ARTIFACT_EXP:
nwindow = new CAltarWindow(m, h, Mode);
GH.pushIntT<CAltarWindow>(m, h, Mode);
break;
default:
nwindow = new CMarketplaceWindow(m, h, Mode);
GH.pushIntT<CMarketplaceWindow>(m, h, Mode);
break;
}
GH.pushInt(nwindow);
}
void CTradeWindow::artifactSelected(CHeroArtPlace *slot)

View File

@ -34,8 +34,7 @@
#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
CWindowObject::CWindowObject(int options_, std::string imageName, Point centerAt):
CIntObject(getUsedEvents(options_), Point()),
shadow(nullptr),
WindowBase(getUsedEvents(options_), Point()),
options(options_),
background(createBg(imageName, options & PLAYER_COLORED))
{
@ -56,7 +55,7 @@ CWindowObject::CWindowObject(int options_, std::string imageName, Point centerAt
}
CWindowObject::CWindowObject(int options_, std::string imageName):
CIntObject(getUsedEvents(options_), Point()),
WindowBase(getUsedEvents(options_), Point()),
options(options_),
background(createBg(imageName, options_ & PLAYER_COLORED))
{
@ -122,10 +121,10 @@ void CWindowObject::setShadow(bool on)
//size of shadow
static const int size = 8;
if(on == bool(shadow))
if(on == !shadowParts.empty())
return;
shadow.reset();
shadowParts.clear();
//object too small to cast shadow
if(pos.h <= size || pos.w <= size)
@ -214,15 +213,11 @@ void CWindowObject::setShadow(bool on)
//generate "shadow" object with these 3 pieces in it
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
shadow = std::make_shared<CIntObject>();
}
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255);
shadowParts.push_back(std::make_shared<CPicture>(shadowCorner, shadowPos.x, shadowPos.y));
shadowParts.push_back(std::make_shared<CPicture>(shadowRight, shadowPos.x, shadowStart.y));
shadowParts.push_back(std::make_shared<CPicture>(shadowBottom, shadowStart.x, shadowPos.y));
shadow->addChild(new CPicture(shadowCorner, shadowPos.x, shadowPos.y));
shadow->addChild(new CPicture(shadowRight, shadowPos.x, shadowStart.y));
shadow->addChild(new CPicture(shadowBottom, shadowStart.x, shadowPos.y));
}
}
}
@ -238,11 +233,6 @@ void CWindowObject::showAll(SDL_Surface *to)
CMessage::drawBorder(color, to, pos.w+28, pos.h+29, pos.x-14, pos.y-15);
}
void CWindowObject::close()
{
GH.popIntTotally(this);
}
void CWindowObject::clickRight(tribool down, bool previousState)
{
close();

View File

@ -11,13 +11,13 @@
#include "../gui/CIntObject.h"
/// Basic class for windows
class CWindowObject : public CIntObject
class CWindowObject : public WindowBase
{
std::shared_ptr<CPicture> createBg(std::string imageName, bool playerColored);
int getUsedEvents(int options);
std::shared_ptr<CIntObject> shadow;
std::vector<std::shared_ptr<CPicture>> shadowParts;
void setShadow(bool on);
int options;
@ -25,8 +25,6 @@ class CWindowObject : public CIntObject
protected:
std::shared_ptr<CPicture> background;
//Simple function with call to GH.popInt
void close();
//Used only if RCLICK_POPUP was set
void clickRight(tribool down, bool previousState) override;
//To display border

View File

@ -96,7 +96,7 @@ void CRecruitmentWindow::CCreatureCard::clickLeft(tribool down, bool previousSta
void CRecruitmentWindow::CCreatureCard::clickRight(tribool down, bool previousState)
{
if(down)
GH.pushInt(new CStackWindow(creature, true));
GH.pushIntT<CStackWindow>(creature, true);
}
void CRecruitmentWindow::CCreatureCard::showAll(SDL_Surface * to)
@ -569,8 +569,7 @@ void CSystemOptionsWindow::selectGameRes()
items.push_back(resX + 'x' + resY);
}
GH.pushInt(new CObjectListWindow(items, nullptr, texts["label"].String(), texts["help"].String(),
std::bind(&CSystemOptionsWindow::setGameRes, this, _1)));
GH.pushIntT<CObjectListWindow>(items, nullptr, texts["label"].String(), texts["help"].String(), std::bind(&CSystemOptionsWindow::setGameRes, this, _1));
}
void CSystemOptionsWindow::setGameRes(int index)
@ -599,7 +598,7 @@ void CSystemOptionsWindow::bquitf()
void CSystemOptionsWindow::breturnf()
{
GH.popIntTotally(this);
close();
}
void CSystemOptionsWindow::bmainmenuf()
@ -609,14 +608,14 @@ void CSystemOptionsWindow::bmainmenuf()
void CSystemOptionsWindow::bloadf()
{
GH.popIntTotally(this);
close();
LOCPLINT->proposeLoadingGame();
}
void CSystemOptionsWindow::bsavef()
{
GH.popIntTotally(this);
GH.pushInt(new CSavingScreen());
close();
GH.pushIntT<CSavingScreen>();
}
void CSystemOptionsWindow::brestartf()
@ -626,7 +625,7 @@ void CSystemOptionsWindow::brestartf()
void CSystemOptionsWindow::closeAndPushEvent(int eventType, int code)
{
GH.popIntTotally(this);
close();
GH.pushSDLEvent(eventType, code);
}
@ -705,7 +704,7 @@ void CTavernWindow::recruitb()
void CTavernWindow::thievesguildb()
{
GH.pushInt( new CThievesGuildWindow(tavernObj) );
GH.pushIntT<CThievesGuildWindow>(tavernObj);
}
CTavernWindow::~CTavernWindow()
@ -745,9 +744,7 @@ void CTavernWindow::HeroPortrait::clickLeft(tribool down, bool previousState)
void CTavernWindow::HeroPortrait::clickRight(tribool down, bool previousState)
{
if(h && down)
{
GH.pushInt(new CRClickPopupInt(new CHeroWindow(h), true));
}
GH.pushIntT<CRClickPopupInt>(std::make_shared<CHeroWindow>(h));
}
CTavernWindow::HeroPortrait::HeroPortrait(int & sel, int id, int x, int y, const CGHeroInstance * H)
@ -990,7 +987,7 @@ CShipyardWindow::CShipyardWindow(const std::vector<si32> & cost, int state, int
std::string boatFilenames[3] = {"AB01_", "AB02_", "AB03_"};
Point waterCenter = Point(bgWater->pos.x+bgWater->pos.w/2, bgWater->pos.y+bgWater->pos.h/2);
bgShip = std::make_shared<CAnimImage>(boatFilenames[boatType], 0, 7, 120, 96, CShowableAnim::USE_RLE);
bgShip = std::make_shared<CAnimImage>(boatFilenames[boatType], 0, 7, 120, 96, 0);
bgShip->center(waterCenter);
// Create resource icons and costs.
@ -1110,7 +1107,7 @@ void CTransformerWindow::CItem::clickLeft(tribool down, bool previousState)
if(previousState && (!down))
{
move();
parent->showAll(screen2);
parent->redraw();
}
}
@ -1152,7 +1149,7 @@ void CTransformerWindow::addAll()
if(elem->left)
elem->move();
}
showAll(screen2);
redraw();
}
void CTransformerWindow::updateGarrisons()
@ -1215,10 +1212,7 @@ void CUniversityWindow::CItem::clickLeft(tribool down, bool previousState)
if(previousState && (!down))
{
if(state() == 2)
{
auto win = new CUnivConfirmWindow(parent, ID, LOCPLINT->cb->getResourceAmount(Res::GOLD) >= 2000);
GH.pushInt(win);
}
GH.pushIntT<CUnivConfirmWindow>(parent, ID, LOCPLINT->cb->getResourceAmount(Res::GOLD) >= 2000);
}
}
@ -1813,14 +1807,14 @@ void CObjectListWindow::elementSelected()
{
std::function<void(int)> toCall = onSelect;//save
int where = items[selected].first; //required variables
GH.popIntTotally(this);//then destroy window
close();//then destroy window
toCall(where);//and send selected object
}
void CObjectListWindow::exitPressed()
{
std::function<void()> toCall = onExit;//save
GH.popIntTotally(this);//then destroy window
close();//then destroy window
if(toCall)
toCall();
}

View File

@ -161,7 +161,8 @@ CInfoWindow::CInfoWindow()
void CInfoWindow::close()
{
GH.popIntTotally(this);
WindowBase::close();
if(LOCPLINT)
LOCPLINT->showingDialog->setn(false);
}
@ -181,8 +182,7 @@ void CInfoWindow::showAll(SDL_Surface * to)
void CInfoWindow::showInfoDialog(const std::string &text, const TCompsInfo & components, PlayerColor player)
{
CInfoWindow * window = CInfoWindow::create(text, player, components);
GH.pushInt(window);
GH.pushInt(CInfoWindow::create(text, player, components));
}
void CInfoWindow::showYesNoDialog(const std::string & text, const TCompsInfo & components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, PlayerColor player)
@ -191,7 +191,7 @@ void CInfoWindow::showYesNoDialog(const std::string & text, const TCompsInfo & c
std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
pom.push_back(std::pair<std::string,CFunctionList<void()> >("ICANCEL.DEF",0));
CInfoWindow * temp = new CInfoWindow(text, player, components, pom);
std::shared_ptr<CInfoWindow> temp = std::make_shared<CInfoWindow>(text, player, components, pom);
temp->buttons[0]->addCallback( onYes );
temp->buttons[1]->addCallback( onNo );
@ -199,22 +199,11 @@ void CInfoWindow::showYesNoDialog(const std::string & text, const TCompsInfo & c
GH.pushInt(temp);
}
void CInfoWindow::showOkDialog(const std::string & text, const TCompsInfo & components, const std::function<void()> & onOk, PlayerColor player)
std::shared_ptr<CInfoWindow> CInfoWindow::create(const std::string &text, PlayerColor playerID, const TCompsInfo & components)
{
std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
CInfoWindow * temp = new CInfoWindow(text, player, components, pom);
temp->buttons[0]->addCallback(onOk);
GH.pushInt(temp);
}
CInfoWindow * CInfoWindow::create(const std::string &text, PlayerColor playerID, const TCompsInfo & components)
{
std::vector<std::pair<std::string,CFunctionList<void()> > > pom;
pom.push_back(std::pair<std::string,CFunctionList<void()> >("IOKAY.DEF",0));
CInfoWindow * ret = new CInfoWindow(text, playerID, components, pom);
return ret;
return std::make_shared<CInfoWindow>(text, playerID, components, pom);
}
std::string CInfoWindow::genText(std::string title, std::string description)
@ -268,7 +257,7 @@ void CInfoPopup::close()
{
if(free)
SDL_FreeSurface(bitmap);
GH.popIntTotally(this);
WindowBase::close();
}
void CInfoPopup::show(SDL_Surface * to)
@ -307,7 +296,7 @@ void CRClickPopup::clickRight(tribool down, bool previousState)
void CRClickPopup::close()
{
GH.popIntTotally(this);
WindowBase::close();
}
void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCompsInfo &comps)
@ -316,11 +305,11 @@ void CRClickPopup::createAndPush(const std::string &txt, const CInfoWindow::TCom
if(settings["session"]["spectate"].Bool())//TODO: there must be better way to implement this
player = PlayerColor(1);
CSimpleWindow * temp = new CInfoWindow(txt, player, comps);
auto temp = std::make_shared<CInfoWindow>(txt, player, comps);
temp->center(Point(GH.current->motion)); //center on mouse
temp->fitToScreen(10);
auto rcpi = new CRClickPopupInt(temp,true);
GH.pushInt(rcpi);
GH.pushIntT<CRClickPopupInt>(temp);
}
void CRClickPopup::createAndPush(const std::string & txt, std::shared_ptr<CComponent> component)
@ -331,14 +320,16 @@ void CRClickPopup::createAndPush(const std::string & txt, std::shared_ptr<CCompo
createAndPush(txt, intComps);
}
void CRClickPopup::createAndPush(const CGObjectInstance *obj, const Point &p, EAlignment alignment)
void CRClickPopup::createAndPush(const CGObjectInstance * obj, const Point & p, EAlignment alignment)
{
CIntObject *iWin = createInfoWin(p, obj); //try get custom infowindow for this obj
auto iWin = createInfoWin(p, obj); //try get custom infowindow for this obj
if(iWin)
{
GH.pushInt(iWin);
}
else
{
if (adventureInt->curHero())
if(adventureInt->curHero())
CRClickPopup::createAndPush(obj->getHoverText(adventureInt->curHero()));
else
CRClickPopup::createAndPush(obj->getHoverText(LOCPLINT->playerID));
@ -354,31 +345,20 @@ CRClickPopup::~CRClickPopup()
{
}
void CRClickPopupInt::show(SDL_Surface * to)
{
inner->show(to);
}
CRClickPopupInt::CRClickPopupInt( IShowActivatable *our, bool deleteInt )
CRClickPopupInt::CRClickPopupInt(std::shared_ptr<CIntObject> our)
{
CCS->curh->hide();
defActions = SHOWALL | UPDATE;
our->recActions = defActions;
inner = our;
delInner = deleteInt;
addChild(our.get(), false);
}
CRClickPopupInt::~CRClickPopupInt()
{
if(delInner)
delete inner;
CCS->curh->show();
}
void CRClickPopupInt::showAll(SDL_Surface * to)
{
inner->showAll(to);
}
Point CInfoBoxPopup::toScreen(Point p)
{
vstd::abetween(p.x, adventureInt->terrain.pos.x + 100, adventureInt->terrain.pos.x + adventureInt->terrain.pos.w - 100);
@ -417,7 +397,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr)
tooltip = std::make_shared<CArmyTooltip>(Point(9, 10), iah);
}
CIntObject * CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
std::shared_ptr<WindowBase> CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
{
if(nullptr == specific)
specific = adventureInt->selection;
@ -431,13 +411,13 @@ CIntObject * CRClickPopup::createInfoWin(Point position, const CGObjectInstance
switch(specific->ID)
{
case Obj::HERO:
return new CInfoBoxPopup(position, dynamic_cast<const CGHeroInstance *>(specific));
return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGHeroInstance *>(specific));
case Obj::TOWN:
return new CInfoBoxPopup(position, dynamic_cast<const CGTownInstance *>(specific));
return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGTownInstance *>(specific));
case Obj::GARRISON:
case Obj::GARRISON2:
return new CInfoBoxPopup(position, dynamic_cast<const CGGarrison *>(specific));
return std::make_shared<CInfoBoxPopup>(position, dynamic_cast<const CGGarrison *>(specific));
default:
return nullptr;
return std::shared_ptr<WindowBase>();
}
}

View File

@ -26,11 +26,11 @@ class CSlider;
class CArmyTooltip;
// Window GUI class
class CSimpleWindow : public CIntObject
class CSimpleWindow : public WindowBase
{
public:
SDL_Surface * bitmap; //background
virtual void show(SDL_Surface * to) override;
void show(SDL_Surface * to) override;
CSimpleWindow():bitmap(nullptr){};
virtual ~CSimpleWindow();
};
@ -58,16 +58,15 @@ public:
//use only before the game starts! (showYesNoDialog in LOCPLINT must be used then)
static void showInfoDialog( const std::string & text, const TCompsInfo & components, PlayerColor player = PlayerColor(1));
static void showOkDialog(const std::string & text, const TCompsInfo & components, const std::function<void()> & onOk, PlayerColor player = PlayerColor(1));
static void showYesNoDialog( const std::string & text, const TCompsInfo & components, const CFunctionList<void()> & onYes, const CFunctionList<void()> & onNo, PlayerColor player = PlayerColor(1));
static CInfoWindow * create(const std::string & text, PlayerColor playerID = PlayerColor(1), const TCompsInfo & components = TCompsInfo());
static std::shared_ptr<CInfoWindow> create(const std::string & text, PlayerColor playerID = PlayerColor(1), const TCompsInfo & components = TCompsInfo());
/// create text from title and description: {title}\n\n description
static std::string genText(std::string title, std::string description);
};
/// popup displayed on R-click
class CRClickPopup : public CIntObject
class CRClickPopup : public WindowBase
{
public:
virtual void close();
@ -76,7 +75,7 @@ public:
CRClickPopup();
virtual ~CRClickPopup();
static CIntObject* createInfoWin(Point position, const CGObjectInstance * specific);
static std::shared_ptr<WindowBase> createInfoWin(Point position, const CGObjectInstance * specific);
static void createAndPush(const std::string & txt, const CInfoWindow::TCompsInfo &comps = CInfoWindow::TCompsInfo());
static void createAndPush(const std::string & txt, std::shared_ptr<CComponent> component);
static void createAndPush(const CGObjectInstance * obj, const Point & p, EAlignment alignment = BOTTOMRIGHT);
@ -85,13 +84,9 @@ public:
/// popup displayed on R-click
class CRClickPopupInt : public CRClickPopup
{
std::shared_ptr<CIntObject> inner;
public:
IShowActivatable *inner;
bool delInner;
void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override;
CRClickPopupInt(IShowActivatable *our, bool deleteInt);
CRClickPopupInt(std::shared_ptr<CIntObject> our);
virtual ~CRClickPopupInt();
};
@ -123,11 +118,11 @@ public:
/// component selection window
class CSelWindow : public CInfoWindow
{ //warning - this window deletes its components by closing!
{
public:
void selectionChange(unsigned to);
void madeChoice(); //looks for selected component and calls callback
CSelWindow(const std::string & text, PlayerColor player, int charperline ,const std::vector<std::shared_ptr<CSelectableComponent>> & comps, const std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, QueryID askID);
CSelWindow(){};
CSelWindow(const std::string & text, PlayerColor player, int charperline, const std::vector<std::shared_ptr<CSelectableComponent>> & comps, const std::vector<std::pair<std::string,CFunctionList<void()> > > &Buttons, QueryID askID);
//notification - this class inherits important destructor from CInfoWindow
};

View File

@ -50,102 +50,102 @@ protected:
public:
//various
int getDate(Date::EDateType mode=Date::DAY)const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
const StartInfo * getStartInfo(bool beforeRandomization = false)const;
bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact; 2 - secondary skill
virtual int getDate(Date::EDateType mode=Date::DAY)const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
virtual const StartInfo * getStartInfo(bool beforeRandomization = false)const;
virtual bool isAllowed(int type, int id); //type: 0 - spell; 1- artifact; 2 - secondary skill
//player
const PlayerState * getPlayer(PlayerColor color, bool verbose = true) const;
int getResource(PlayerColor Player, Res::ERes which) const;
bool isVisible(int3 pos) const;
PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const;
void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object
EPlayerStatus::EStatus getPlayerStatus(PlayerColor player, bool verbose = true) const; //-1 if no such player
PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns
virtual const PlayerState * getPlayer(PlayerColor color, bool verbose = true) const;
virtual int getResource(PlayerColor Player, Res::ERes which) const;
virtual bool isVisible(int3 pos) const;
virtual PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const;
virtual void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object
virtual EPlayerStatus::EStatus getPlayerStatus(PlayerColor player, bool verbose = true) const; //-1 if no such player
virtual PlayerColor getCurrentPlayer() const; //player that currently makes move // TODO synchronous turns
virtual PlayerColor getLocalPlayer() const; //player that is currently owning given client (if not a client, then returns current player)
const PlayerSettings * getPlayerSettings(PlayerColor color) const;
virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const;
//armed object
void getUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out)const;
virtual void getUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out)const;
//hero
const CGHeroInstance* getHero(ObjectInstanceID objid) const;
const CGHeroInstance* getHeroWithSubid(int subid) const;
int getHeroCount(PlayerColor player, bool includeGarrisoned) const;
bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const;
int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const;
const CGObjectInstance * getArmyInstance(ObjectInstanceID oid) const;
virtual const CGHeroInstance* getHero(ObjectInstanceID objid) const;
virtual const CGHeroInstance* getHeroWithSubid(int subid) const;
virtual int getHeroCount(PlayerColor player, bool includeGarrisoned) const;
virtual bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const;
virtual int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
virtual int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
virtual const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
virtual const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const;
//virtual const CGObjectInstance * getArmyInstance(ObjectInstanceID oid) const;
//objects
const CGObjectInstance* getObj(ObjectInstanceID objid, bool verbose = true) const;
std::vector <const CGObjectInstance * > getBlockingObjs(int3 pos)const;
std::vector <const CGObjectInstance * > getVisitableObjs(int3 pos, bool verbose = true)const;
std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
const CGObjectInstance * getTopObj (int3 pos) const;
PlayerColor getOwner(ObjectInstanceID heroID) const;
const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed)
virtual const CGObjectInstance* getObj(ObjectInstanceID objid, bool verbose = true) const;
virtual std::vector <const CGObjectInstance * > getBlockingObjs(int3 pos)const;
virtual std::vector <const CGObjectInstance * > getVisitableObjs(int3 pos, bool verbose = true)const;
virtual std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
virtual const CGObjectInstance * getTopObj (int3 pos) const;
virtual PlayerColor getOwner(ObjectInstanceID heroID) const;
virtual const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed)
//map
int3 guardingCreaturePosition (int3 pos) const;
std::vector<const CGObjectInstance*> getGuardingCreatures (int3 pos) const;
const CMapHeader * getMapHeader()const;
int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map
const TerrainTile * getTile(int3 tile, bool verbose = true) const;
std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
bool isInTheMap(const int3 &pos) const;
void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
virtual int3 guardingCreaturePosition (int3 pos) const;
virtual std::vector<const CGObjectInstance*> getGuardingCreatures (int3 pos) const;
virtual const CMapHeader * getMapHeader()const;
virtual int3 getMapSize() const; //returns size of map - z is 1 for one - level map and 2 for two level map
virtual const TerrainTile * getTile(int3 tile, bool verbose = true) const;
virtual std::shared_ptr<boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const;
virtual bool isInTheMap(const int3 &pos) const;
virtual void getVisibleTilesInRange(std::unordered_set<int3, ShashInt3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const;
//town
const CGTownInstance* getTown(ObjectInstanceID objid) const;
int howManyTowns(PlayerColor Player) const;
const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial)
std::vector<const CGHeroInstance *> getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited
std::string getTavernRumor(const CGObjectInstance * townOrTavern) const;
EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
virtual const CGTownInstance* getTown(ObjectInstanceID objid) const;
virtual int howManyTowns(PlayerColor Player) const;
//virtual const CGTownInstance * getTownInfo(int val, bool mode)const; //mode = 0 -> val = player town serial; mode = 1 -> val = object id (serial)
virtual std::vector<const CGHeroInstance *> getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited
virtual std::string getTavernRumor(const CGObjectInstance * townOrTavern) const;
virtual EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const;
const CTown *getNativeTown(PlayerColor color) const;
virtual const CTown *getNativeTown(PlayerColor color) const;
//from gs
const TeamState *getTeam(TeamID teamID) const;
const TeamState *getPlayerTeam(PlayerColor color) const;
EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID) const;// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
virtual const TeamState *getTeam(TeamID teamID) const;
virtual const TeamState *getPlayerTeam(PlayerColor color) const;
//virtual EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID) const;// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
//teleport
std::vector<ObjectInstanceID> getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const;
std::vector<ObjectInstanceID> getTeleportChannelEntraces(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
std::vector<ObjectInstanceID> getTeleportChannelExits(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
ETeleportChannelType getTeleportChannelType(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
bool isTeleportChannelImpassable(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
bool isTeleportChannelBidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const;
virtual std::vector<ObjectInstanceID> getVisibleTeleportObjects(std::vector<ObjectInstanceID> ids, PlayerColor player) const;
virtual std::vector<ObjectInstanceID> getTeleportChannelEntraces(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
virtual std::vector<ObjectInstanceID> getTeleportChannelExits(TeleportChannelID id, PlayerColor Player = PlayerColor::UNFLAGGABLE) const;
virtual ETeleportChannelType getTeleportChannelType(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
virtual bool isTeleportChannelImpassable(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
virtual bool isTeleportChannelBidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
virtual bool isTeleportChannelUnidirectional(TeleportChannelID id, PlayerColor player = PlayerColor::UNFLAGGABLE) const;
virtual bool isTeleportEntrancePassable(const CGTeleport * obj, PlayerColor player) const;
};
class DLL_LINKAGE CPlayerSpecificInfoCallback : public CGameInfoCallback
{
public:
int howManyTowns() const;
int howManyHeroes(bool includeGarrisoned = true) const;
int3 getGrailPos(double *outKnownRatio);
boost::optional<PlayerColor> getMyColor() const;
virtual int howManyTowns() const;
virtual int howManyHeroes(bool includeGarrisoned = true) const;
virtual int3 getGrailPos(double *outKnownRatio);
virtual boost::optional<PlayerColor> getMyColor() const;
std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
int getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned=true) const;
const CGTownInstance* getTownBySerial(int serialId) const; // serial id is [0, number of towns)
const CGHeroInstance* getHeroBySerial(int serialId, bool includeGarrisoned=true) const; // serial id is [0, number of heroes)
std::vector <const CGHeroInstance *> getHeroesInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
std::vector <const CGDwelling *> getMyDwellings() const; //returns all dwellings that belong to player
std::vector <const CGObjectInstance * > getMyObjects() const; //returns all objects flagged by belonging player
std::vector <QuestInfo> getMyQuests() const;
virtual std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
virtual int getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned=true) const;
virtual const CGTownInstance* getTownBySerial(int serialId) const; // serial id is [0, number of towns)
virtual const CGHeroInstance* getHeroBySerial(int serialId, bool includeGarrisoned=true) const; // serial id is [0, number of heroes)
virtual std::vector <const CGHeroInstance *> getHeroesInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
virtual std::vector <const CGDwelling *> getMyDwellings() const; //returns all dwellings that belong to player
virtual std::vector <const CGObjectInstance * > getMyObjects() const; //returns all objects flagged by belonging player
virtual std::vector <QuestInfo> getMyQuests() const;
int getResourceAmount(Res::ERes type) const;
TResources getResourceAmount() const;
const std::vector< std::vector< std::vector<ui8> > > & getVisibilityMap()const; //returns visibility map
const PlayerSettings * getPlayerSettings(PlayerColor color) const;
virtual int getResourceAmount(Res::ERes type) const;
virtual TResources getResourceAmount() const;
virtual const std::vector< std::vector< std::vector<ui8> > > & getVisibilityMap()const; //returns visibility map
//virtual const PlayerSettings * getPlayerSettings(PlayerColor color) const;
};
class DLL_LINKAGE IGameEventRealizer

View File

@ -28,6 +28,21 @@ Res::ResourceSet::ResourceSet(const JsonNode & node)
push_back(node[name].Float());
}
Res::ResourceSet::ResourceSet(TResource wood, TResource mercury, TResource ore, TResource sulfur, TResource crystal,
TResource gems, TResource gold, TResource mithril)
{
resize(GameConstants::RESOURCE_QUANTITY);
auto d = data();
d[Res::WOOD] = wood;
d[Res::MERCURY] = mercury;
d[Res::ORE] = ore;
d[Res::SULFUR] = sulfur;
d[Res::CRYSTAL] = crystal;
d[Res::GEMS] = gems;
d[Res::GOLD] = gold;
d[Res::MITHRIL] = mithril;
}
void Res::ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
{
if(!handler.saving)

View File

@ -25,7 +25,8 @@ namespace Res
{
WOOD = 0, MERCURY, ORE, SULFUR, CRYSTAL, GEMS, GOLD, MITHRIL,
WOOD_AND_ORE = 127 // special case for town bonus resource
WOOD_AND_ORE = 127, // special case for town bonus resource
INVALID = -1
};
//class to be representing a vector of resource
@ -35,6 +36,8 @@ namespace Res
DLL_LINKAGE ResourceSet();
// read resources set from json. Format example: { "gold": 500, "wood":5 }
DLL_LINKAGE ResourceSet(const JsonNode & node);
DLL_LINKAGE ResourceSet(TResource wood, TResource mercury, TResource ore, TResource sulfur, TResource crystal,
TResource gems, TResource gold, TResource mithril = 0);
#define scalarOperator(OPSIGN) \

View File

@ -1262,6 +1262,18 @@ bool CGTownInstance::hasBuilt(BuildingID buildingID, int townID) const
return false;
}
TResources CGTownInstance::getBuildingCost(BuildingID buildingID) const
{
if (vstd::contains(town->buildings, buildingID))
return town->buildings.at(buildingID)->resources;
else
{
logGlobal->error("Town %s at %s has no possible building %d!", name, pos.toString(), buildingID.toEnum());
return TResources();
}
}
bool CGTownInstance::hasBuilt(BuildingID buildingID) const
{
return vstd::contains(builtBuildings, buildingID);

View File

@ -265,6 +265,7 @@ public:
//checks if building is constructed and town has same subID
bool hasBuilt(BuildingID buildingID) const;
bool hasBuilt(BuildingID buildingID, int townID) const;
TResources getBuildingCost(BuildingID buildingID) const;
TResources dailyIncome() const; //calculates daily income of this town
int spellsAtLevel(int level, bool checkGuild) const; //levels are counted from 1 (1 - 5)
bool armedGarrison() const; //true if town has creatures in garrison or garrisoned hero

View File

@ -137,7 +137,7 @@ EMonsterStrength::EMonsterStrength CMapGenOptions::getMonsterStrength() const
void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value)
{
monsterStrength = value;
monsterStrength = value;
}
void CMapGenOptions::resetPlayersMap()

View File

@ -60,9 +60,14 @@ set(test_SRCS
spells/targetConditions/SpellEffectConditionTest.cpp
spells/targetConditions/TargetConditionItemFixture.cpp
vcai/mock_ResourceManager.cpp
vcai/mock_VCAI.cpp
vcai/ResurceManagerTest.cpp
mock/mock_IGameCallback.cpp
mock/mock_MapService.cpp
mock/mock_BonusBearer.cpp
mock/mock_CPSICallback.cpp
)
set(test_HEADERS
@ -77,10 +82,13 @@ set(test_HEADERS
spells/targetConditions/TargetConditionItemFixture.h
vcai/ResourceManagerTest.h
mock/mock_BonusBearer.h
mock/mock_IGameCallback.h
mock/mock_MapService.h
mock/mock_BonusBearer.h
)
assign_source_group(${test_SRCS} ${test_HEADERS})
@ -94,6 +102,11 @@ set(mock_HEADERS
mock/mock_spells_Problem.h
mock/mock_spells_Spell.h
mock/mock_vstd_RNG.h
mock/mock_CPSICallback.h
vcai/mock_ResourceManager.h
vcai/mock_VCAI.h
vcai/mock_VCAI_CGoal.h
)
add_subdirectory_with_folder("3rdparty" googletest EXCLUDE_FROM_ALL)

View File

@ -66,6 +66,8 @@
<Add option="-lVCMI_lib" />
<Add option="-lboost_filesystem$(#boost.libsuffix)" />
<Add option="-lboost_system$(#boost.libsuffix)" />
<Add option="-lboost_thread$(#boost.libsuffix)" />
<Add library="../AI/VCAI.dll" />
<Add directory="../" />
</Linker>
<Unit filename="CMakeLists.txt" />
@ -99,6 +101,8 @@
<Unit filename="map/MapComparer.h" />
<Unit filename="mock/mock_BonusBearer.cpp" />
<Unit filename="mock/mock_BonusBearer.h" />
<Unit filename="mock/mock_CPSICallback.cpp" />
<Unit filename="mock/mock_CPSICallback.h" />
<Unit filename="mock/mock_IGameCallback.cpp" />
<Unit filename="mock/mock_IGameCallback.h" />
<Unit filename="mock/mock_MapService.cpp" />
@ -139,6 +143,13 @@
<Unit filename="spells/targetConditions/TargetConditionItemFixture.cpp" />
<Unit filename="spells/targetConditions/TargetConditionItemFixture.h" />
<Unit filename="testdata/rmg/1.json" />
<Unit filename="vcai/ResourceManagerTest.h" />
<Unit filename="vcai/ResurceManagerTest.cpp" />
<Unit filename="vcai/mock_ResourceManager.cpp" />
<Unit filename="vcai/mock_ResourceManager.h" />
<Unit filename="vcai/mock_VCAI.cpp" />
<Unit filename="vcai/mock_VCAI.h" />
<Unit filename="vcai/mock_VCAI_CGoal.h" />
<Extensions>
<code_completion />
<envvars />

View File

@ -0,0 +1,13 @@
/*
* mock_CPLayerSpecificInfoCallback.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 "mock_CPSICallback.h"

View File

@ -0,0 +1,28 @@
/*
* mock_CPLayerSpecificInfoCallback.h, 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
*
*/
#pragma once
#include "gtest/gtest.h"
#include "../lib/CGameInfoCallback.h"
#include "../lib/ResourceSet.h"
class CCallback;
class CPSICallbackMock : public CPlayerSpecificInfoCallback
{
public:
using CPlayerSpecificInfoCallback::CPlayerSpecificInfoCallback;
MOCK_CONST_METHOD0(getResourceAmount, TResources());
//std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const;
MOCK_CONST_METHOD0(getTownsInfo, std::vector <const CGTownInstance *>());
MOCK_CONST_METHOD1(getTownsInfo, std::vector <const CGTownInstance *>(bool));
};

View File

@ -1,5 +1,5 @@
/*
* {file}.h, part of VCMI engine
* mock_IGameCallback.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*

View File

@ -0,0 +1,13 @@
/*
* ResourceManagerTest.h, 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
*
*/
#pragma once
extern boost::thread_specific_ptr<CCallback> cb;

View File

@ -0,0 +1,254 @@
/*
* ResourceManagerTest.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 "gtest/gtest.h"
#include "../AI/VCAI/VCAI.h"
#include "ResourceManagerTest.h"
#include "../AI/VCAI/Goals.h"
#include "mock_VCAI_CGoal.h"
#include "mock_VCAI.h"
#include "mock_ResourceManager.h"
#include "../mock/mock_CPSICallback.h"
#include "../lib/CGameInfoCallback.h"
using namespace Goals;
using namespace ::testing;
struct ResourceManagerTest : public Test//, public IResourceManager
{
std::unique_ptr<ResourceManagerMock> rm;
NiceMock<CPSICallbackMock> gcm;
NiceMock<VCAIMock> aim;
TSubgoal invalidGoal, gatherArmy, buildThis, buildAny, recruitHero;
ResourceManagerTest()
{
rm = make_unique<NiceMock<ResourceManagerMock>>(&gcm, &aim);
//note: construct new goal for modfications
invalidGoal = sptr(StrictMock<InvalidGoalMock>());
gatherArmy = sptr(StrictMock<GatherArmyGoalMock>());
buildThis = sptr(StrictMock<BuildThis>());
buildAny = sptr(StrictMock<Build>());
recruitHero = sptr(StrictMock<RecruitHero>());
//auto AI = CDynLibHandler::getNewAI("VCAI.dll");
//SET_GLOBAL_STATE(AI);
//gtest couldn't deduce default return value;
ON_CALL(gcm, getTownsInfo(_))
.WillByDefault(Return(std::vector < const CGTownInstance *>()));
ON_CALL(gcm, getTownsInfo())
.WillByDefault(Return(std::vector < const CGTownInstance *>()));
ON_CALL(aim, getFlaggedObjects())
.WillByDefault(Return(std::vector < const CGObjectInstance *>()));
//enable if get unexpected exceptions
//ON_CALL(gcm, getResourceAmount())
// .WillByDefault(Return(TResources()));
}
};
TEST_F(ResourceManagerTest, canAffordMaths)
{
//mocking cb calls inside canAfford()
ON_CALL(gcm, getResourceAmount())
.WillByDefault(Return(TResources(10, 0, 11, 0, 0, 0, 12345)));
TResources buildingCost(10, 0, 10, 0, 0, 0, 5000);
//EXPECT_CALL(gcm, getResourceAmount()).RetiresOnSaturation();
//EXPECT_CALL(gcm, getTownsInfo(_)).RetiresOnSaturation();
EXPECT_NO_THROW(rm->canAfford(buildingCost));
EXPECT_TRUE(rm->canAfford(buildingCost));
TResources armyCost(0, 0, 0, 0, 0, 0, 54321);
EXPECT_FALSE(rm->canAfford(armyCost));
rm->reserveResoures(armyCost, gatherArmy);
EXPECT_FALSE(rm->canAfford(buildingCost)) << "Reserved value should be substracted from free resources";
}
TEST_F(ResourceManagerTest, notifyGoalImplemented)
{
ASSERT_FALSE(rm->hasTasksLeft());
EXPECT_FALSE(rm->notifyGoalCompleted(invalidGoal));
EXPECT_FALSE(rm->hasTasksLeft());
TResources res(0,0,0,0,0,0,12345);;
rm->reserveResoures(res, invalidGoal);
ASSERT_FALSE(rm->hasTasksLeft()) << "Can't push Invalid goal";
EXPECT_FALSE(rm->notifyGoalCompleted(invalidGoal));
EXPECT_FALSE(rm->notifyGoalCompleted(gatherArmy)) << "Queue should be empty";
rm->reserveResoures(res, gatherArmy);
EXPECT_TRUE(rm->notifyGoalCompleted(gatherArmy)) << "Not implemented"; //TODO: try it with not a copy
EXPECT_FALSE(rm->notifyGoalCompleted(gatherArmy)); //already completed
}
TEST_F(ResourceManagerTest, notifyFulfillsAll)
{
TResources res;
ASSERT_TRUE(buildAny->fulfillsMe(buildThis)) << "Goal dependency implemented incorrectly"; //TODO: goal mock?
rm->reserveResoures(res, buildAny);
rm->reserveResoures(res, buildAny);
rm->reserveResoures(res, buildAny);
ASSERT_TRUE(rm->hasTasksLeft()); //regardless if duplicates are allowed or not
rm->notifyGoalCompleted(buildThis);
ASSERT_FALSE(rm->hasTasksLeft()) << "BuildThis didn't remove Build Any!";
}
TEST_F(ResourceManagerTest, queueOrder)
{
ASSERT_FALSE(rm->hasTasksLeft());
TSubgoal buildLow = sptr(StrictMock<BuildThis>()),
buildBit = sptr(StrictMock<BuildThis>()),
buildMed = sptr(StrictMock<BuildThis>()),
buildHigh = sptr(StrictMock<BuildThis>()),
buildVeryHigh = sptr(StrictMock<BuildThis>()),
buildExtra = sptr(StrictMock<BuildThis>()),
buildNotSoExtra = sptr(StrictMock<BuildThis>());
buildLow->setpriority(0).setbid(1);
buildLow->setpriority(1).setbid(2);
buildMed->setpriority(2).setbid(3);
buildHigh->setpriority(5).setbid(4);
buildVeryHigh->setpriority(10).setbid(5);
TResources price(0, 0, 0, 0, 0, 0, 1000);
rm->reserveResoures(price, buildLow);
rm->reserveResoures(price, buildHigh);
rm->reserveResoures(price, buildMed);
ON_CALL(gcm, getResourceAmount())
.WillByDefault(Return(TResources(0,0,0,0,0,0,4000,0))); //we can afford 4 top goals
auto goal = rm->whatToDo();
EXPECT_EQ(goal->goalType, Goals::BUILD_STRUCTURE);
ASSERT_EQ(rm->whatToDo()->bid, 4);
rm->reserveResoures(price, buildBit);
rm->reserveResoures(price, buildVeryHigh);
goal = rm->whatToDo();
EXPECT_EQ(goal->goalType, Goals::BUILD_STRUCTURE);
ASSERT_EQ(goal->bid, 5);
buildExtra->setpriority(3).setbid(100);
EXPECT_EQ(rm->whatToDo(price, buildExtra)->bid, 100);
buildNotSoExtra->setpriority(0.7f).setbid(7);
goal = rm->whatToDo(price, buildNotSoExtra);
EXPECT_NE(goal->bid, 7);
EXPECT_EQ(goal->goalType, Goals::COLLECT_RES) << "We can't afford this goal, need to collect resources";
EXPECT_EQ(goal->resID, Res::GOLD) << "We need to collect gold";
goal = rm->whatToDo();
EXPECT_NE(goal->goalType, Goals::COLLECT_RES);
EXPECT_EQ(goal->bid, 5) << "Should return highest-priority goal";
}
TEST_F(ResourceManagerTest, updateGoalImplemented)
{
ASSERT_FALSE(rm->hasTasksLeft());
TResources res;
res[Res::GOLD] = 12345;
buildThis->setpriority(1);
buildThis->bid = 666;
EXPECT_FALSE(rm->updateGoal(buildThis)); //try update with no objectives -> fail
rm->reserveResoures(res, buildThis);
ASSERT_TRUE(rm->hasTasksLeft());
buildThis->setpriority(4.444f);
auto buildThis2 = sptr(StrictMock<BuildThis>());
buildThis2->bid = 777;
buildThis2->setpriority(3.33f);
EXPECT_FALSE(rm->updateGoal(buildThis2)) << "Shouldn't update with wrong goal";
EXPECT_TRUE(rm->updateGoal(buildThis)) << "Not implemented"; //try update with copy of reserved goal -> true
EXPECT_FALSE(rm->updateGoal(invalidGoal)) << "Can't update Invalid goal";
}
TEST_F(ResourceManagerTest, complexGoalUpdates)
{
//TODO
ASSERT_FALSE(rm->hasTasksLeft());
}
TEST_F(ResourceManagerTest, tasksLeft)
{
ASSERT_FALSE(rm->hasTasksLeft());
}
TEST_F(ResourceManagerTest, reservedResources)
{
//TOOO, not worth it now
}
TEST_F(ResourceManagerTest, freeResources)
{
ON_CALL(gcm, getResourceAmount()) //in case callback or gs gets crazy
.WillByDefault(Return(TResources(-1, 0, -13.0f, -38763, -93764, -464, -12, -98765)));
auto res = rm->freeResources();
ASSERT_GE(res[Res::WOOD], 0);
ASSERT_GE(res[Res::MERCURY], 0);
ASSERT_GE(res[Res::ORE], 0);
ASSERT_GE(res[Res::SULFUR], 0);
ASSERT_GE(res[Res::CRYSTAL], 0);
ASSERT_GE(res[Res::GEMS], 0);
ASSERT_GE(res[Res::GOLD], 0);
ASSERT_GE(res[Res::MITHRIL], 0);
}
TEST_F(ResourceManagerTest, freeResourcesWithManyGoals)
{
ON_CALL(gcm, getResourceAmount())
.WillByDefault(Return(TResources(20, 10, 20, 10, 10, 10, 20000, 0)));
ASSERT_EQ(rm->freeResources(), TResources(20, 10, 20, 10, 10, 10, 20000, 0));
rm->reserveResoures(TResources(0, 4, 0, 0, 0, 0, 13000), gatherArmy);
ASSERT_EQ(rm->freeResources(), TResources(20, 6, 20, 10, 10, 10, 7000, 0));
rm->reserveResoures(TResources(5, 4, 5, 4, 4, 4, 5000), buildThis);
ASSERT_EQ(rm->freeResources(), TResources(15, 2, 15, 6, 6, 6, 2000, 0));
rm->reserveResoures(TResources(0, 0, 0, 0, 0, 0, 2500), recruitHero);
auto res = rm->freeResources();
EXPECT_EQ(res[Res::GOLD], 0) << "We should have 0 gold left";
ON_CALL(gcm, getResourceAmount())
.WillByDefault(Return(TResources(20, 10, 20, 10, 10, 10, 30000, 0))); //+10000 gold
res = rm->freeResources();
EXPECT_EQ(res[Res::GOLD], 9500) << "We should have extra savings now";
}
TEST_F(ResourceManagerTest, freeGold)
{
ON_CALL(gcm, getResourceAmount())
.WillByDefault(Return(TResources(0, 0, 0, 0, 0, 0, 666)));
ASSERT_EQ(rm->freeGold(), 666);
ON_CALL(gcm, getResourceAmount())
.WillByDefault(Return(TResources(0, 0, 0, 0, 0, 0, -3762363)));
ASSERT_GE(rm->freeGold(), 0) << "We should never see negative savings";
}

View File

@ -0,0 +1,18 @@
#include "StdInc.h"
#include "mock_ResourceManager.h"
void ResourceManagerMock::reserveResoures(const TResources &res, Goals::TSubgoal goal)
{
ResourceManager::reserveResoures(res, goal);
}
bool ResourceManagerMock::updateGoal(Goals::TSubgoal goal)
{
return ResourceManager::updateGoal(goal);
}
bool ResourceManagerMock::notifyGoalCompleted(Goals::TSubgoal goal)
{
return ResourceManager::notifyGoalCompleted(goal);
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "gtest/gtest.h"
#include "../AI/VCAI/ResourceManager.h"
class ResourceManager;
class CPlayerSpecificInfoCallback;
class VCAI;
class ResourceManagerMock : public ResourceManager
{
friend class ResourceManagerTest; //everything is public
public:
using ResourceManager::ResourceManager;
//access protected members, TODO: consider other architecture?
void reserveResoures(const TResources &res, Goals::TSubgoal goal = Goals::TSubgoal()) override;
bool updateGoal(Goals::TSubgoal goal) override;
bool notifyGoalCompleted(Goals::TSubgoal goal) override;
};

23
test/vcai/mock_VCAI.cpp Normal file
View File

@ -0,0 +1,23 @@
/*
* ResourceManagerTest.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 "mock_VCAI.h"
VCAIMock::VCAIMock()
: VCAI()
{
makingTurn = nullptr;
}
VCAIMock::~VCAIMock()
{
}

96
test/vcai/mock_VCAI.h Normal file
View File

@ -0,0 +1,96 @@
/*
* mock_VCAI.h, 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
*
*/
#pragma once
#include "gtest/gtest.h"
#include "../AI/VCAI/VCAI.h"
//dependency hell
#include "../../lib/NetPacks.h" //for Component, TryMoveHero
#include "../../lib/serializer/BinarySerializer.h"
#include "../../lib/serializer/BinaryDeserializer.h"
class VCAIMock : public VCAI
{
public:
VCAIMock();
~VCAIMock() override;
//overloading all "override" methods from VCAI. AI should never call them, anyway
MOCK_CONST_METHOD0(getBattleAIName, std::string());
MOCK_METHOD1(init, void(std::shared_ptr<CCallback> CB));
MOCK_METHOD0(yourTurn, void());
MOCK_METHOD4(heroGotLevel, void(const CGHeroInstance * hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID));
MOCK_METHOD3(commanderGotLevel, void(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID));
MOCK_METHOD6(showBlockingDialog, void(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel));
MOCK_METHOD4(showGarrisonDialog, void(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID));
MOCK_METHOD4(showTeleportDialog, void(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID));
MOCK_METHOD5(showMapObjectSelectDialog, void(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects));
MOCK_METHOD2(saveGame, void(BinarySerializer & h, const int version));
MOCK_METHOD2(loadGame, void(BinaryDeserializer & h, const int version));
MOCK_METHOD0(finish, void());
MOCK_METHOD1(availableCreaturesChanged, void(const CGDwelling * town));
MOCK_METHOD1(heroMoved, void(const TryMoveHero & details));
MOCK_METHOD1(heroInGarrisonChange, void(const CGTownInstance * town));
MOCK_METHOD2(centerView, void(int3 pos, int focusTime));
MOCK_METHOD1(tileHidden, void(const std::unordered_set<int3, ShashInt3> & pos));
MOCK_METHOD2(artifactMoved, void(const ArtifactLocation & src, const ArtifactLocation & dst));
MOCK_METHOD1(artifactAssembled, void(const ArtifactLocation & al));
MOCK_METHOD1(showTavernWindow, void(const CGObjectInstance * townOrTavern));
MOCK_METHOD1(showThievesGuildWindow, void(const CGObjectInstance * obj));
MOCK_METHOD2(playerBlocked, void(int reason, bool start));
MOCK_METHOD0(showPuzzleMap, void());
MOCK_METHOD1(showShipyardDialog, void(const IShipyard * obj));
MOCK_METHOD2(gameOver, void(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult));
MOCK_METHOD1(artifactPut, void(const ArtifactLocation & al));
MOCK_METHOD1(artifactRemoved, void(const ArtifactLocation & al));
MOCK_METHOD1(artifactDisassembled, void(const ArtifactLocation & al));
MOCK_METHOD3(heroVisit, void(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start));
MOCK_METHOD1(availableArtifactsChanged, void(const CGBlackMarket * bm));
MOCK_METHOD2(heroVisitsTown, void(const CGHeroInstance * hero, const CGTownInstance * town));
MOCK_METHOD1(tileRevealed, void(const std::unordered_set<int3, ShashInt3> & pos));
MOCK_METHOD3(heroExchangeStarted, void(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query));
MOCK_METHOD3(heroPrimarySkillChanged, void(const CGHeroInstance * hero, int which, si64 val));
MOCK_METHOD3(showRecruitmentDialog, void(const CGDwelling * dwelling, const CArmedInstance * dst, int level));
MOCK_METHOD1(heroMovePointsChanged, void(const CGHeroInstance * hero));
MOCK_METHOD2(garrisonsChanged, void(ObjectInstanceID id1, ObjectInstanceID id2));
MOCK_METHOD1(newObject, void(const CGObjectInstance * obj));
MOCK_METHOD2(showHillFortWindow, void(const CGObjectInstance * object, const CGHeroInstance * visitor));
MOCK_METHOD2(playerBonusChanged, void(const Bonus & bonus, bool gain));
MOCK_METHOD1(heroCreated, void(const CGHeroInstance *));
MOCK_METHOD2(advmapSpellCast, void(const CGHeroInstance * caster, int spellID));
MOCK_METHOD3(showInfoDialog, void(const std::string & text, const std::vector<Component> & components, int soundID));
MOCK_METHOD1(requestRealized, void(PackageApplied * pa));
MOCK_METHOD0(receivedResource, void());
MOCK_METHOD1(objectRemoved, void(const CGObjectInstance * obj));
MOCK_METHOD2(showUniversityWindow, void(const IMarket * market, const CGHeroInstance * visitor));
MOCK_METHOD1(heroManaPointsChanged, void(const CGHeroInstance * hero));
MOCK_METHOD3(heroSecondarySkillChanged, void(const CGHeroInstance * hero, int which, int val));
MOCK_METHOD0(battleResultsApplied, void());
MOCK_METHOD1(objectPropertyChanged, void(const SetObjectProperty * sop));
MOCK_METHOD3(buildChanged, void(const CGTownInstance * town, BuildingID buildingID, int what));
MOCK_METHOD3(heroBonusChanged, void(const CGHeroInstance * hero, const Bonus & bonus, bool gain));
MOCK_METHOD2(showMarketWindow, void(const IMarket * market, const CGHeroInstance * visitor));
MOCK_METHOD1(showWorldViewEx, void(const std::vector<ObjectPosInfo> & objectPositions));
MOCK_METHOD6(battleStart, void(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side));
MOCK_METHOD1(battleEnd, void(const BattleResult * br));
MOCK_METHOD2(requestSent, void(const CPackForServer * pack, int requestID));
//interetsing stuff
MOCK_CONST_METHOD0(getFlaggedObjects, std::vector<const CGObjectInstance *>());
};

View File

@ -0,0 +1,35 @@
/*
* mock_VCAI_CGoal.h, 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
*
*/
#pragma once
#include "../AI/VCAI/Goals.h"
namespace Goals
{
class InvalidGoalMock : public Invalid
{
public:
//MOCK_METHOD1(accept, void(VCAI *));
//MOCK_METHOD1(accept, float(FuzzyHelper *));
//MOCK_METHOD1(fulfillsMe, bool(TSubgoal));
//bool operator==(AbstractGoal & g) override
//{
// return false;
//};
//MOCK_METHOD0(getAllPossibleSubgoals, TGoalVec());
//MOCK_METHOD0(whatToDoToAchieve, TSubgoal());
};
class GatherArmyGoalMock : public GatherArmy
{
};
}