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:
commit
2288e9b8aa
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
@ -173,4 +186,4 @@ public:
|
||||
{
|
||||
}
|
||||
bool operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs);
|
||||
};
|
||||
};
|
89
AI/VCAI/AIhelper.cpp
Normal file
89
AI/VCAI/AIhelper.cpp
Normal 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
53
AI/VCAI/AIhelper.h
Normal 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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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())
|
||||
{
|
||||
|
111
AI/VCAI/Goals.h
111
AI/VCAI/Goals.h
@ -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
324
AI/VCAI/ResourceManager.cpp
Normal 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
111
AI/VCAI/ResourceManager.h
Normal 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;
|
||||
}
|
||||
};
|
@ -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" />
|
||||
|
402
AI/VCAI/VCAI.cpp
402
AI/VCAI/VCAI.cpp
@ -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;
|
||||
}
|
||||
|
154
AI/VCAI/VCAI.h
154
AI/VCAI/VCAI.h
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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++)
|
||||
|
190
client/CMT.cpp
190
client/CMT.cpp
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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(...)
|
||||
{
|
||||
|
@ -31,5 +31,5 @@ public:
|
||||
const CMapInfo * getMapInfo() override;
|
||||
const StartInfo * getStartInfo() override;
|
||||
|
||||
CBonusSelection * bonusSel;
|
||||
std::shared_ptr<CBonusSelection> bonusSel;
|
||||
};
|
||||
|
@ -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)))
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
};
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,4 +266,4 @@ public:
|
||||
|
||||
};
|
||||
|
||||
extern CAdvMapInt *adventureInt;
|
||||
extern std::shared_ptr<CAdvMapInt> adventureInt;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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]()
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -137,7 +137,7 @@ EMonsterStrength::EMonsterStrength CMapGenOptions::getMonsterStrength() const
|
||||
|
||||
void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value)
|
||||
{
|
||||
monsterStrength = value;
|
||||
monsterStrength = value;
|
||||
}
|
||||
|
||||
void CMapGenOptions::resetPlayersMap()
|
||||
|
@ -59,10 +59,15 @@ set(test_SRCS
|
||||
spells/targetConditions/ReceptiveFeatureConditionTest.cpp
|
||||
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
|
||||
@ -76,11 +81,14 @@ set(test_HEADERS
|
||||
spells/effects/EffectFixture.h
|
||||
|
||||
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)
|
||||
|
@ -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 />
|
||||
|
13
test/mock/mock_CPSICallback.cpp
Normal file
13
test/mock/mock_CPSICallback.cpp
Normal 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"
|
28
test/mock/mock_CPSICallback.h
Normal file
28
test/mock/mock_CPSICallback.h
Normal 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));
|
||||
};
|
@ -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
|
||||
*
|
||||
|
13
test/vcai/ResourceManagerTest.h
Normal file
13
test/vcai/ResourceManagerTest.h
Normal 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;
|
254
test/vcai/ResurceManagerTest.cpp
Normal file
254
test/vcai/ResurceManagerTest.cpp
Normal 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";
|
||||
}
|
18
test/vcai/mock_ResourceManager.cpp
Normal file
18
test/vcai/mock_ResourceManager.cpp
Normal 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);
|
||||
}
|
19
test/vcai/mock_ResourceManager.h
Normal file
19
test/vcai/mock_ResourceManager.h
Normal 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
23
test/vcai/mock_VCAI.cpp
Normal 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
96
test/vcai/mock_VCAI.h
Normal 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 *>());
|
||||
|
||||
};
|
35
test/vcai/mock_VCAI_CGoal.h
Normal file
35
test/vcai/mock_VCAI_CGoal.h
Normal 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
|
||||
{
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user