mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
* New file: lib/HeroBonus.cpp - updated project files for MSVC
* Updated changelog * Support for Lighthosues and Obelisks * Bonus system extended on players * Army speed won't affect movement points when sailing * Picking grail position (digging not implemented though, puzzle map only partially) * Minor improvements
This commit is contained in:
parent
d0bf334394
commit
0fdbe787dc
@ -317,7 +317,7 @@ std::vector< std::vector< std::vector<unsigned char> > > & CCallback::getVisibil
|
||||
bool CCallback::isVisible(int3 pos, int Player) const
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
|
||||
return gs->isVisible(pos, Player);
|
||||
return gs->map->isInTheMap(pos) && gs->isVisible(pos, Player);
|
||||
}
|
||||
|
||||
std::vector < const CGTownInstance *> CCallback::getTownsInfo(bool onlyOur) const
|
||||
@ -905,6 +905,12 @@ void CCallback::calculatePaths( const CGHeroInstance *hero, CPathsInfo &out, int
|
||||
gs->calculatePaths(hero, out, src, movement);
|
||||
}
|
||||
|
||||
int3 CCallback::getGrailPos( float &outKnownRatio )
|
||||
{
|
||||
outKnownRatio = (float)CGObelisk::visited[player] / CGObelisk::obeliskCount;
|
||||
return gs->map->grailPos;
|
||||
}
|
||||
|
||||
InfoAboutTown::InfoAboutTown()
|
||||
{
|
||||
tType = NULL;
|
||||
|
@ -114,6 +114,7 @@ public:
|
||||
virtual int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const =0; //when called during battle, takes into account creatures' spell cost reduction
|
||||
virtual int estimateSpellDamage(const CSpell * sp) const =0; //estimates damage of given spell; returns 0 if spell causes no dmg
|
||||
virtual void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj)=0; //get thieves' guild info obtainable while visiting given object
|
||||
virtual int3 getGrailPos(float &outKnownRatio)=0;
|
||||
|
||||
//hero
|
||||
virtual int howManyHeroes(bool includeGarrisoned = true)const =0;
|
||||
@ -247,6 +248,7 @@ public:
|
||||
int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
|
||||
int estimateSpellDamage(const CSpell * sp) const; //estimates damage of given spell; returns 0 if spell causes no dmg
|
||||
void getThievesGuildInfo(SThievesGuildInfo & thi, const CGObjectInstance * obj); //get thieves' guild info obtainable while visiting given object
|
||||
int3 getGrailPos(float &outKnownRatio); //returns pos and (via arg) percent of discovered obelisks; TODO: relies on fairness of GUI/AI... :/
|
||||
|
||||
std::vector < const CGObjectInstance * > getBlockingObjs(int3 pos) const;
|
||||
std::vector < const CGObjectInstance * > getVisitableObjs(int3 pos) const;
|
||||
|
@ -21,7 +21,7 @@ using namespace boost::logic;
|
||||
class CCallback;
|
||||
class ICallback;
|
||||
class CGlobalAI;
|
||||
class Component;
|
||||
struct Component;
|
||||
class CSelectableComponent;
|
||||
struct TryMoveHero;
|
||||
class CGHeroInstance;
|
||||
@ -91,6 +91,7 @@ public:
|
||||
virtual void showShipyardDialog(const IShipyard *obj){} //obj may be town or shipyard; state: 0 - can buid, 1 - lack of resources, 2 - dest tile is blocked, 3 - no water
|
||||
virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, const int soundID, bool selection, bool cancel) = 0; //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, boost::function<void()> &onEnd) = 0; //all stacks operations between these objects become allowed, interface has to call onEnd when done
|
||||
virtual void showPuzzleMap(){};
|
||||
virtual void tileHidden(const std::set<int3> &pos){};
|
||||
virtual void tileRevealed(const std::set<int3> &pos){};
|
||||
virtual void newObject(const CGObjectInstance * obj){}; //eg. ship built in shipyard
|
||||
@ -98,6 +99,7 @@ public:
|
||||
virtual void centerView (int3 pos, int focusTime){};
|
||||
virtual void availableCreaturesChanged(const CGDwelling *town){};
|
||||
virtual void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
|
||||
virtual void playerBonusChanged(const HeroBonus &bonus, bool gain){};//if gain hero received bonus, else he lost it
|
||||
virtual void requestRealized(PackageApplied *pa){};
|
||||
virtual void heroExchangeStarted(si32 hero1, si32 hero2){};
|
||||
virtual void objectPropertyChanged(const SetObjectProperty * sop){}; //eg. mine has been flagged
|
||||
|
39
ChangeLog
39
ChangeLog
@ -1,3 +1,42 @@
|
||||
0.75 -> 0.8
|
||||
GENERAL:
|
||||
* Victory and loss conditions are supported. It's now possible to win or lose the game.
|
||||
* Kingdom Overview screen is now available.
|
||||
* Replaced TTF fonts with original ones.
|
||||
|
||||
ADVENTURE MAP:
|
||||
* Implemented rivers animations (thx to GrayFace).
|
||||
|
||||
HERO:
|
||||
* Partial implementation of scholar skill (spell exchange works, but no message).
|
||||
|
||||
TOWN:
|
||||
* New left-bottom info panel functionalities.
|
||||
TOWNS:
|
||||
* new town structures supported:
|
||||
- Ballista Yard
|
||||
- Blood Obelisk
|
||||
- Dwarven Treasury
|
||||
- Glyphs of Fear
|
||||
- Mystic Pond
|
||||
- Thieves Guild
|
||||
|
||||
OBJECTS:
|
||||
New objects supported:
|
||||
- Border gate
|
||||
- Den of Thieves
|
||||
- Lighthouse
|
||||
- Obelisk
|
||||
- Quest Guard
|
||||
- Seer hut
|
||||
|
||||
|
||||
|
||||
A lot of of various bugfixes and improvements:
|
||||
http://vcmi.antypika.aplus.pl/bugs/view_all_bug_page.php?filter=699
|
||||
|
||||
|
||||
|
||||
0.74 -> 0.75 (Dec 01 2009)
|
||||
GENERAL:
|
||||
* Implemented "main menu" in-game option.
|
||||
|
@ -2060,7 +2060,7 @@ CAdventureOptions::CAdventureOptions()
|
||||
//viewWorld = new AdventureMapButton("","",boost::bind(&CGuiHandler::popIntTotally, &GH, this), 204, 313, "IOK6432.DEF",SDLK_RETURN);
|
||||
|
||||
puzzle = new AdventureMapButton("","", boost::bind(&CGuiHandler::popIntTotally, &GH, this), 24, 81, "ADVPUZ.DEF");;
|
||||
puzzle->callback += CAdventureOptions::showPuzzleMap;
|
||||
puzzle->callback += boost::bind(&CPlayerInterface::showPuzzleMap, LOCPLINT);
|
||||
}
|
||||
|
||||
CAdventureOptions::~CAdventureOptions()
|
||||
@ -2070,9 +2070,4 @@ CAdventureOptions::~CAdventureOptions()
|
||||
void CAdventureOptions::showScenarioInfo()
|
||||
{
|
||||
GH.pushInt(new CScenarioInfo(LOCPLINT->cb->getMapHeader(), LOCPLINT->cb->getStartInfo()));
|
||||
}
|
||||
|
||||
void CAdventureOptions::showPuzzleMap()
|
||||
{
|
||||
GH.pushInt(new CPuzzleWindow());
|
||||
}
|
||||
}
|
@ -34,7 +34,6 @@ public:
|
||||
CAdventureOptions();
|
||||
~CAdventureOptions();
|
||||
static void showScenarioInfo();
|
||||
static void showPuzzleMap();
|
||||
};
|
||||
|
||||
|
||||
|
@ -1169,6 +1169,8 @@ void CCastleInterface::CCreaInfo::clickRight(tribool down, bool previousState)
|
||||
ch = ci->town->visitingHero;
|
||||
};
|
||||
|
||||
//TODO player bonuses
|
||||
|
||||
if(bld.find(26)!=bld.end()) //grail - +50% to ALL growth
|
||||
summ+=AddToString(CGI->buildh->buildings[ci->town->subID][26]->Name()+" %+d",descr,summ/2);
|
||||
|
||||
|
@ -230,7 +230,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
|
||||
|
||||
if(makingTurn && ho->tempOwner == playerID) //we are moving our hero - we may need to update assigned path
|
||||
{
|
||||
if(details.result == TryMoveHero::TELEPORTATION || details.start == details.end)
|
||||
if(details.result == TryMoveHero::TELEPORTATION/* || details.start == details.end*/)
|
||||
{
|
||||
adventureInt->eraseCurrentPathOf(ho);
|
||||
return; //teleport - no fancy moving animation
|
||||
@ -1629,6 +1629,22 @@ void CPlayerInterface::gameOver(ui8 player, bool victory )
|
||||
}
|
||||
}
|
||||
|
||||
void CPlayerInterface::playerBonusChanged( const HeroBonus &bonus, bool gain )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CPlayerInterface::showPuzzleMap()
|
||||
{
|
||||
waitWhileDialog();
|
||||
|
||||
//TODO: interface should not know the real position of Grail...
|
||||
float ratio = 0;
|
||||
int3 grailPos = cb->getGrailPos(ratio);
|
||||
|
||||
GH.pushInt(new CPuzzleWindow(grailPos, ratio));
|
||||
}
|
||||
|
||||
void SystemOptions::setMusicVolume( int newVolume )
|
||||
{
|
||||
musicVolume = newVolume;
|
||||
|
@ -159,12 +159,14 @@ public:
|
||||
void showShipyardDialog(const IShipyard *obj); //obj may be town or shipyard;
|
||||
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, int soundID, bool selection, bool cancel); //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, boost::function<void()> &onEnd);
|
||||
void showPuzzleMap();
|
||||
void tileHidden(const std::set<int3> &pos); //called when given tiles become hidden under fog of war
|
||||
void tileRevealed(const std::set<int3> &pos); //called when fog of war disappears from given tiles
|
||||
void newObject(const CGObjectInstance * obj);
|
||||
void yourTurn();
|
||||
void availableCreaturesChanged(const CGDwelling *town);
|
||||
void heroBonusChanged(const CGHeroInstance *hero, const HeroBonus &bonus, bool gain);//if gain hero received bonus, else he lost it
|
||||
void playerBonusChanged(const HeroBonus &bonus, bool gain);
|
||||
void requestRealized(PackageApplied *pa);
|
||||
void heroExchangeStarted(si32 hero1, si32 hero2);
|
||||
void centerView (int3 pos, int focusTime);
|
||||
|
@ -4710,7 +4710,7 @@ CShipyardWindow::CShipyardWindow(const std::vector<si32> &cost, int state, const
|
||||
printAtMiddle(CGI->generaltexth->jktexts[14], 164, 220, FONT_MEDIUM, zwykly, bg); //Resource cost:
|
||||
}
|
||||
|
||||
CPuzzleWindow::CPuzzleWindow()
|
||||
CPuzzleWindow::CPuzzleWindow(const int3 &grailPos, float discoveredRatio)
|
||||
:animCount(0)
|
||||
{
|
||||
SDL_Surface * back = BitmapHandler::loadBitmap("PUZZLE.BMP", false);
|
||||
@ -4728,19 +4728,17 @@ CPuzzleWindow::CPuzzleWindow()
|
||||
//printing necessary thinks to background
|
||||
|
||||
CGI->mh->terrainRect
|
||||
(int3(14, 15, 0), LOCPLINT->adventureInt->anim,
|
||||
(grailPos, LOCPLINT->adventureInt->anim,
|
||||
&LOCPLINT->cb->getVisibilityMap(), true, LOCPLINT->adventureInt->heroAnim,
|
||||
background, &genRect(544, 591, 8, 8), 0, 0, true);
|
||||
|
||||
|
||||
float discoveryRatio = 0.5f;
|
||||
int faction = LOCPLINT->cb->getStartInfo()->playerInfos[LOCPLINT->serialID].castle;
|
||||
|
||||
std::vector<SPuzzleInfo> puzzlesToPrint;
|
||||
|
||||
for(int g=0; g<PUZZLES_PER_FACTION; ++g)
|
||||
{
|
||||
if(CGI->heroh->puzzleInfo[faction][g].whenUncovered >= PUZZLES_PER_FACTION * discoveryRatio)
|
||||
if(CGI->heroh->puzzleInfo[faction][g].whenUncovered > PUZZLES_PER_FACTION * discoveredRatio)
|
||||
{
|
||||
puzzlesToPrint.push_back(CGI->heroh->puzzleInfo[faction][g]);
|
||||
}
|
||||
|
@ -825,7 +825,7 @@ public:
|
||||
void deactivate();
|
||||
void show(SDL_Surface * to);
|
||||
|
||||
CPuzzleWindow();
|
||||
CPuzzleWindow(const int3 &grailPos, float discoveredRatio);
|
||||
~CPuzzleWindow();
|
||||
};
|
||||
|
||||
|
@ -109,9 +109,21 @@ void SetAvailableHeroes::applyCl( CClient *cl )
|
||||
|
||||
void GiveBonus::applyCl( CClient *cl )
|
||||
{
|
||||
CGHeroInstance *h = GS(cl)->getHero(hid);
|
||||
if(vstd::contains(cl->playerint,h->tempOwner))
|
||||
cl->playerint[h->tempOwner]->heroBonusChanged(h,h->bonuses.back(),true);
|
||||
switch(who)
|
||||
{
|
||||
case HERO:
|
||||
{
|
||||
const CGHeroInstance *h = GS(cl)->getHero(id);
|
||||
INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroBonusChanged, h, h->bonuses.back(),true);
|
||||
}
|
||||
break;
|
||||
case PLAYER:
|
||||
{
|
||||
const PlayerState *p = GS(cl)->getPlayer(id);
|
||||
INTERFACE_CALL_IF_PRESENT(id, playerBonusChanged, p->bonuses.back(), true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeObjPos::applyFirstCl( CClient *cl )
|
||||
@ -133,6 +145,25 @@ void PlayerEndsGame::applyCl( CClient *cl )
|
||||
i->second->gameOver(player, victory);
|
||||
}
|
||||
|
||||
void RemoveBonus::applyCl( CClient *cl )
|
||||
{
|
||||
switch(who)
|
||||
{
|
||||
case HERO:
|
||||
{
|
||||
const CGHeroInstance *h = GS(cl)->getHero(id);
|
||||
INTERFACE_CALL_IF_PRESENT(h->tempOwner, heroBonusChanged, h, bonus,false);
|
||||
}
|
||||
break;
|
||||
case PLAYER:
|
||||
{
|
||||
const PlayerState *p = GS(cl)->getPlayer(id);
|
||||
INTERFACE_CALL_IF_PRESENT(id, playerBonusChanged, bonus, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveObject::applyFirstCl( CClient *cl )
|
||||
{
|
||||
const CGObjectInstance *o = cl->getObj(id);
|
||||
@ -635,6 +666,11 @@ void OpenWindow::applyCl(CClient *cl)
|
||||
GH.pushInt( new CThievesGuildWindow(obj) );
|
||||
}
|
||||
break;
|
||||
case PUZZLE_MAP:
|
||||
{
|
||||
INTERFACE_CALL_IF_PRESENT(id1, showPuzzleMap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -253,4 +253,4 @@ GUISettings
|
||||
ButtonEndTurn: x=1559 y=491 graphic=IAM001.DEF playerColoured=1;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
17
global.h
17
global.h
@ -75,9 +75,10 @@ enum ECombatInfo{ALIVE = 180, SUMMONED, CLONED, HAD_MORALE, WAITING, MOVED, DEFE
|
||||
class CGameInfo;
|
||||
extern CGameInfo* CGI;
|
||||
|
||||
#define HEROI_TYPE (34)
|
||||
#define TOWNI_TYPE (98)
|
||||
#define CREI_TYPE (54)
|
||||
const int HEROI_TYPE = 34,
|
||||
TOWNI_TYPE = 98,
|
||||
CREI_TYPE = 54,
|
||||
EVENTI_TYPE = 26;
|
||||
|
||||
const int F_NUMBER = 9; //factions (town types) quantity
|
||||
const int PLAYER_LIMIT = 8; //player limit per map
|
||||
@ -265,6 +266,7 @@ t1 & amax(t1 &a, const t2 &b) //assigns greater of (a, b) to a and returns maxim
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename t1, typename t2>
|
||||
t1 & amin(t1 &a, const t2 &b) //assigns smaller of (a, b) to a and returns minimum of (a, b)
|
||||
{
|
||||
@ -276,6 +278,15 @@ t1 & amin(t1 &a, const t2 &b) //assigns smaller of (a, b) to a and returns minim
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename t1, typename t2, typename t3>
|
||||
t1 & abetw(t1 &a, const t2 &b, const t3 &c) //makes a to fit the range <b, c>
|
||||
{
|
||||
amax(a,b);
|
||||
amin(a,c);
|
||||
return a;
|
||||
}
|
||||
|
||||
#include "CConsoleHandler.h"
|
||||
extern DLL_EXPORT std::ostream *logfile;
|
||||
extern DLL_EXPORT CConsoleHandler *console;
|
||||
|
@ -44,6 +44,9 @@ extern boost::rand48 ran;
|
||||
std::map <ui8, std::set <ui8> > CGKeys::playerKeyMap;
|
||||
std::map <si32, std::vector<si32> > CGMagi::eyelist;
|
||||
BankConfig CGPyramid::pyramidConfig;
|
||||
ui8 CGObelisk::obeliskCount; //how many obelisks are on map
|
||||
std::map<ui8, ui8> CGObelisk::visited; //map: color_id => how many obelisks has been visited
|
||||
|
||||
|
||||
void IObjectInterface::onHeroVisit(const CGHeroInstance * h) const
|
||||
{};
|
||||
@ -429,7 +432,7 @@ void CGObjectInstance::giveDummyBonus(int heroID, ui8 duration) const
|
||||
{
|
||||
GiveBonus gbonus;
|
||||
gbonus.bonus.type = HeroBonus::NONE;
|
||||
gbonus.hid = heroID;
|
||||
gbonus.id = heroID;
|
||||
gbonus.bonus.duration = duration;
|
||||
gbonus.bonus.source = HeroBonus::OBJECT;
|
||||
gbonus.bonus.id = ID;
|
||||
@ -563,10 +566,8 @@ bool CGHeroInstance::canWalkOnSea() const
|
||||
int CGHeroInstance::getPrimSkillLevel(int id) const
|
||||
{
|
||||
int ret = primSkills[id];
|
||||
for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
|
||||
if(i->type == HeroBonus::PRIMARY_SKILL && i->subtype==id)
|
||||
ret += i->val;
|
||||
amax(ret, id/2);//minimum value for attack and defense is 0 and for spell power and knowledge - 1
|
||||
ret += valOfBonuses(HeroBonus::PRIMARY_SKILL, id);
|
||||
amax(ret, id/2); //minimal value is 0 for attack and defense and 1 for spell power and knowledge
|
||||
return ret;
|
||||
}
|
||||
ui8 CGHeroInstance::getSecSkillLevel(const int & ID) const
|
||||
@ -578,13 +579,21 @@ ui8 CGHeroInstance::getSecSkillLevel(const int & ID) const
|
||||
}
|
||||
int CGHeroInstance::maxMovePoints(bool onLand) const
|
||||
{
|
||||
static const int moveForSpeed[] = { 1500, 1560, 1630, 1700, 1760, 1830, 1900, 1960, 2000 }; //first element for 3 and lower; last for 11 and more
|
||||
int index = lowestSpeed(this) - 3;
|
||||
amin(index, ARRAY_COUNT(moveForSpeed)-1);
|
||||
amax(index, 0);
|
||||
|
||||
int ret = moveForSpeed[index],
|
||||
bonus = valOfBonuses(HeroBonus::MOVEMENT) + (onLand ? valOfBonuses(HeroBonus::LAND_MOVEMENT) : valOfBonuses(HeroBonus::SEA_MOVEMENT));
|
||||
int base = -1;
|
||||
if(onLand)
|
||||
{
|
||||
static const int moveForSpeed[] = { 1500, 1560, 1630, 1700, 1760, 1830, 1900, 1960, 2000 }; //first element for 3 and lower; last for 11 and more
|
||||
int index = lowestSpeed(this) - 3;
|
||||
amin(index, ARRAY_COUNT(moveForSpeed)-1);
|
||||
amax(index, 0);
|
||||
base = moveForSpeed[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
base = 1500; //on water base movement is always 1500 (speed of army doesn't matter)
|
||||
}
|
||||
|
||||
int bonus = valOfBonuses(HeroBonus::MOVEMENT) + (onLand ? valOfBonuses(HeroBonus::LAND_MOVEMENT) : valOfBonuses(HeroBonus::SEA_MOVEMENT));
|
||||
|
||||
double modifier = 0;
|
||||
if(onLand)
|
||||
@ -619,7 +628,7 @@ int CGHeroInstance::maxMovePoints(bool onLand) const
|
||||
break;
|
||||
}
|
||||
}
|
||||
return int(ret + ret*modifier) + bonus;
|
||||
return int(base + base*modifier) + bonus;
|
||||
}
|
||||
|
||||
ui32 CGHeroInstance::getArtAtPos(ui16 pos) const
|
||||
@ -879,15 +888,7 @@ std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentMoraleModifie
|
||||
{
|
||||
//TODO: check if stack is undead/mechanic/elemental => always neutrl morale
|
||||
std::vector<std::pair<int,std::string> > ret;
|
||||
|
||||
//various morale bonuses (from buildings, artifacts, etc)
|
||||
for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
|
||||
{
|
||||
if(i->type == HeroBonus::MORALE || i->type == HeroBonus::MORALE_AND_LUCK)
|
||||
{
|
||||
ret.push_back(std::make_pair(i->val, i->description));
|
||||
}
|
||||
}
|
||||
getModifiersWDescr(ret, MORALE_AFFECTING);
|
||||
|
||||
//leadership
|
||||
if(getSecSkillLevel(6))
|
||||
@ -961,42 +962,29 @@ int CGHeroInstance::getCurrentLuck( int stack/*=-1*/, bool town/*=false*/ ) cons
|
||||
std::vector<std::pair<int,std::string> > mods = getCurrentLuckModifiers(stack,town);
|
||||
for(int i=0; i < mods.size(); i++)
|
||||
ret += mods[i].first;
|
||||
if(ret > 3)
|
||||
return 3;
|
||||
if(ret < -3)
|
||||
return -3;
|
||||
|
||||
abetw(ret, -3, 3);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int,std::string> > CGHeroInstance::getCurrentLuckModifiers( int stack/*=-1*/, bool town/*=false*/ ) const
|
||||
{
|
||||
std::vector<std::pair<int,std::string> > ret;
|
||||
|
||||
//various morale bonuses (from buildings, artifacts, etc)
|
||||
for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
|
||||
if(i->type == HeroBonus::LUCK || i->type == HeroBonus::MORALE_AND_LUCK)
|
||||
ret.push_back(std::make_pair(i->val, i->description));
|
||||
getModifiersWDescr(ret, LUCK_AFFECTING);
|
||||
|
||||
//luck skill
|
||||
if(getSecSkillLevel(9))
|
||||
ret.push_back(std::make_pair(getSecSkillLevel(9),VLC->generaltexth->arraytxt[73+getSecSkillLevel(9)]));
|
||||
|
||||
if(visitedTown)
|
||||
{
|
||||
if(visitedTown->subID == 1 && vstd::contains(visitedTown->builtBuildings,21)) //castle, brotherhood of sword built
|
||||
ret.push_back(std::pair<int,std::string>(2,VLC->generaltexth->buildings[1][21].first + " +2"));
|
||||
}
|
||||
|
||||
|
||||
if(visitedTown && visitedTown->subID == 1 && vstd::contains(visitedTown->builtBuildings,21)) //rampart, fountain of fortune
|
||||
ret.push_back(std::pair<int,std::string>(2,VLC->generaltexth->buildings[1][21].first + " +2"));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const HeroBonus * CGHeroInstance::getBonus( int from, int id ) const
|
||||
{
|
||||
for (std::list<HeroBonus>::const_iterator i=bonuses.begin(); i!=bonuses.end(); i++)
|
||||
if(i->source == from && i->id == id)
|
||||
return &*i;
|
||||
return NULL;
|
||||
return bonuses.getBonus(from, id);
|
||||
}
|
||||
|
||||
void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
|
||||
@ -1048,26 +1036,16 @@ bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
|
||||
if(!getArt(17)) //if hero has no spellbook
|
||||
return false;
|
||||
|
||||
if(vstd::contains(spells, spell->id) //hero does not have this spell in spellbook
|
||||
if(vstd::contains(spells, spell->id) //hero has this spell in spellbook
|
||||
|| (spell->air && hasBonusOfType(HeroBonus::AIR_SPELLS)) // this is air spell and hero can cast all air spells
|
||||
|| (spell->fire && hasBonusOfType(HeroBonus::FIRE_SPELLS)) // this is fire spell and hero can cast all fire spells
|
||||
|| (spell->water && hasBonusOfType(HeroBonus::WATER_SPELLS)) // this is water spell and hero can cast all water spells
|
||||
|| (spell->earth && hasBonusOfType(HeroBonus::EARTH_SPELLS)) // this is earth spell and hero can cast all earth spells
|
||||
|| hasBonusOfType(HeroBonus::SPELL, spell->id)
|
||||
|| hasBonusOfType(HeroBonus::SPELLS_OF_LEVEL, spell->level)
|
||||
)
|
||||
return true;
|
||||
|
||||
for(std::list<HeroBonus>::const_iterator it = bonuses.begin(); it != bonuses.end(); ++it)
|
||||
{
|
||||
if(it->type == HeroBonus::SPELL && it->subtype == spell->id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(it->type == HeroBonus::SPELLS_OF_LEVEL && it->subtype == spell->level)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1165,40 +1143,15 @@ si32 CGHeroInstance::manaRegain() const
|
||||
|
||||
int CGHeroInstance::valOfBonuses( HeroBonus::BonusType type, int subtype /*= -1*/ ) const
|
||||
{
|
||||
int ret = 0;
|
||||
if(subtype == -1)
|
||||
{
|
||||
for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
|
||||
if(i->type == type)
|
||||
ret += i->val;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
|
||||
if(i->type == type && i->subtype == subtype)
|
||||
ret += i->val;
|
||||
}
|
||||
return ret;
|
||||
return bonuses.valOfBonuses(type, subtype) + ownerBonuses()->valOfBonuses(type, subtype);
|
||||
}
|
||||
|
||||
bool CGHeroInstance::hasBonusOfType(HeroBonus::BonusType type, int subtype /*= -1*/) const
|
||||
{
|
||||
if(!this) //to allow calls on NULL and avoid checking duplication
|
||||
return false; //if hero doesn't exist then bonus neither can
|
||||
|
||||
if(subtype == -1) //any subtype
|
||||
{
|
||||
for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
|
||||
if(i->type == type)
|
||||
return true;
|
||||
}
|
||||
else //given subtype
|
||||
{
|
||||
for(std::list<HeroBonus>::const_iterator i=bonuses.begin(); i != bonuses.end(); i++)
|
||||
if(i->type == type && i->subtype == subtype)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
else
|
||||
return bonuses.hasBonusOfType(type, subtype) || ownerBonuses()->hasBonusOfType(type, subtype);
|
||||
}
|
||||
|
||||
si32 CGHeroInstance::getArtPos(int aid) const
|
||||
@ -1252,6 +1205,21 @@ bool CGHeroInstance::hasArt( ui32 aid ) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void CGHeroInstance::getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype /*= -1*/ ) const
|
||||
{
|
||||
bonuses.getModifiersWDescr(out, type, subtype);
|
||||
ownerBonuses()->getModifiersWDescr(out, type, subtype);
|
||||
}
|
||||
|
||||
const BonusList * CGHeroInstance::ownerBonuses() const
|
||||
{
|
||||
const PlayerState *p = cb->getPlayerState(tempOwner);
|
||||
if(!p)
|
||||
return NULL;
|
||||
else
|
||||
return &p->bonuses;
|
||||
}
|
||||
|
||||
void CGDwelling::initObj()
|
||||
{
|
||||
switch(ID)
|
||||
@ -2166,7 +2134,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
{
|
||||
GiveBonus gb;
|
||||
gb.bonus = HeroBonus(HeroBonus::ONE_WEEK, HeroBonus::LAND_MOVEMENT, HeroBonus::OBJECT, 600, 94, VLC->generaltexth->arraytxt[100]);
|
||||
gb.hid = heroID;
|
||||
gb.id = heroID;
|
||||
cb->giveHeroBonus(&gb);
|
||||
iw.text << VLC->generaltexth->allTexts[580];
|
||||
cb->showInfoDialog(&iw);
|
||||
@ -3632,7 +3600,7 @@ void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward
|
||||
HeroBonus hb(HeroBonus::ONE_WEEK, (rewardType == 3 ? HeroBonus::MORALE : HeroBonus::LUCK),
|
||||
HeroBonus::OBJECT, rVal, h->id, "", -1);
|
||||
GiveBonus gb;
|
||||
gb.hid = h->id;
|
||||
gb.id = h->id;
|
||||
gb.bonus = hb;
|
||||
//gb.descr = "";
|
||||
cb->giveHeroBonus(&gb);
|
||||
@ -3752,7 +3720,7 @@ void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
GiveBonus gbonus;
|
||||
gbonus.hid = h->id;
|
||||
gbonus.id = h->id;
|
||||
gbonus.bonus.duration = HeroBonus::ONE_BATTLE;
|
||||
gbonus.bonus.source = HeroBonus::OBJECT;
|
||||
gbonus.bonus.id = ID;
|
||||
@ -4121,7 +4089,7 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
|
||||
cb->showInfoDialog(&iw);
|
||||
GiveBonus gb;
|
||||
gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::MORALE,HeroBonus::OBJECT,moraleDiff,id,"");
|
||||
gb.hid = h->id;
|
||||
gb.id = h->id;
|
||||
cb->giveHeroBonus(&gb);
|
||||
}
|
||||
|
||||
@ -4132,7 +4100,7 @@ void CGPandoraBox::giveContents( const CGHeroInstance *h, bool afterBattle ) con
|
||||
cb->showInfoDialog(&iw);
|
||||
GiveBonus gb;
|
||||
gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::LUCK,HeroBonus::OBJECT,luckDiff,id,"");
|
||||
gb.hid = h->id;
|
||||
gb.id = h->id;
|
||||
cb->giveHeroBonus(&gb);
|
||||
}
|
||||
|
||||
@ -4688,7 +4656,7 @@ void CGOnceVisitable::searchTomb(const CGHeroInstance *h, ui32 accept) const
|
||||
{
|
||||
//ruin morale
|
||||
GiveBonus gb;
|
||||
gb.hid = h->id;
|
||||
gb.id = h->id;
|
||||
gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::MORALE,HeroBonus::OBJECT,-3,id,"");
|
||||
gb.bdescr.addTxt(MetaString::ARRAY_TXT,104); //Warrior Tomb Visited -3
|
||||
cb->giveHeroBonus(&gb);
|
||||
@ -4912,7 +4880,7 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons
|
||||
{
|
||||
iw.components.push_back (Component (Component::MORALE, 0 , -2, 0));
|
||||
GiveBonus gbonus;
|
||||
gbonus.hid = h->id;
|
||||
gbonus.id = h->id;
|
||||
gbonus.bonus.duration = HeroBonus::ONE_BATTLE;
|
||||
gbonus.bonus.source = HeroBonus::OBJECT;
|
||||
gbonus.bonus.id = ID;
|
||||
@ -4931,7 +4899,7 @@ void CBank::endBattle (const CGHeroInstance *h, const BattleResult *result) cons
|
||||
{
|
||||
iw.components.push_back (Component (Component::MORALE, 0 , -1, 0));
|
||||
GiveBonus gbonus;
|
||||
gbonus.hid = h->id;
|
||||
gbonus.id = h->id;
|
||||
gbonus.bonus.duration = HeroBonus::ONE_BATTLE;
|
||||
gbonus.bonus.source = HeroBonus::OBJECT;
|
||||
gbonus.bonus.id = ID;
|
||||
@ -5077,7 +5045,7 @@ void CGPyramid::onHeroVisit (const CGHeroInstance * h) const
|
||||
iw.components.push_back (Component (Component::LUCK, 0 , -2, 0));
|
||||
GiveBonus gb;
|
||||
gb.bonus = HeroBonus(HeroBonus::ONE_BATTLE,HeroBonus::LUCK,HeroBonus::OBJECT,-2,id,VLC->generaltexth->arraytxt[70]);
|
||||
gb.hid = h->id;
|
||||
gb.id = h->id;
|
||||
cb->giveHeroBonus(&gb);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
@ -5569,4 +5537,114 @@ void CGRefugeeCamp::reset(ui32 val)
|
||||
void CGDenOfthieves::onHeroVisit (const CGHeroInstance * h) const
|
||||
{
|
||||
cb->showThievesGuildWindow(id);
|
||||
}
|
||||
|
||||
void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
|
||||
if(!hasVisited(h->tempOwner))
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 96);
|
||||
cb->sendAndApply(&iw);
|
||||
|
||||
cb->setObjProperty(id,20,h->tempOwner); //increment general visited obelisks counter
|
||||
|
||||
OpenWindow ow;
|
||||
ow.id1 = h->tempOwner;
|
||||
ow.window = OpenWindow::PUZZLE_MAP;
|
||||
cb->sendAndApply(&ow);
|
||||
|
||||
cb->setObjProperty(id,10,h->tempOwner); //mark that particular obelisk as visited
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 97);
|
||||
cb->sendAndApply(&iw);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CGObelisk::initObj()
|
||||
{
|
||||
obeliskCount++;
|
||||
}
|
||||
|
||||
const std::string & CGObelisk::getHoverText() const
|
||||
{
|
||||
hoverName = VLC->generaltexth->names[ID];
|
||||
if(hasVisited(cb->getCurrentPlayer()))
|
||||
hoverName += " " + VLC->generaltexth->allTexts[352]; //not visited
|
||||
else
|
||||
hoverName += " " + VLC->generaltexth->allTexts[353]; //visited
|
||||
return hoverName;
|
||||
}
|
||||
|
||||
void CGObelisk::setPropertyDer( ui8 what, ui32 val )
|
||||
{
|
||||
CPlayersVisited::setPropertyDer(what, val);
|
||||
switch(what)
|
||||
{
|
||||
case 20:
|
||||
assert(val < PLAYER_LIMIT);
|
||||
visited[val]++;
|
||||
assert(visited[val] <= obeliskCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
if(h->tempOwner != tempOwner)
|
||||
{
|
||||
ui8 oldOwner = tempOwner;
|
||||
cb->setOwner(id,h->tempOwner); //not ours? flag it!
|
||||
|
||||
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 69);
|
||||
iw.soundID = soundBase::LIGHTHOUSE;
|
||||
cb->sendAndApply(&iw);
|
||||
|
||||
giveBonusTo(h->tempOwner);
|
||||
|
||||
if(oldOwner < PLAYER_LIMIT) //remove bonus from old owner
|
||||
{
|
||||
RemoveBonus rb(RemoveBonus::PLAYER);
|
||||
rb.whoID = oldOwner;
|
||||
rb.source = HeroBonus::OBJECT;
|
||||
rb.id = id;
|
||||
cb->sendAndApply(&rb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGLighthouse::initObj()
|
||||
{
|
||||
if(tempOwner < PLAYER_LIMIT)
|
||||
{
|
||||
giveBonusTo(tempOwner);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string & CGLighthouse::getHoverText() const
|
||||
{
|
||||
hoverName = VLC->generaltexth->names[ID];
|
||||
//TODO: owned by %s player
|
||||
return hoverName;
|
||||
}
|
||||
|
||||
void CGLighthouse::giveBonusTo( ui8 player ) const
|
||||
{
|
||||
GiveBonus gb(GiveBonus::PLAYER);
|
||||
gb.bonus.type = HeroBonus::SEA_MOVEMENT;
|
||||
gb.bonus.val = 500;
|
||||
gb.id = player;
|
||||
gb.bonus.duration = HeroBonus::PERMANENT;
|
||||
gb.bonus.source = HeroBonus::OBJECT;
|
||||
gb.bonus.id = id;
|
||||
cb->sendAndApply(&gb);
|
||||
|
||||
}
|
@ -70,7 +70,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CQuest
|
||||
class DLL_EXPORT CQuest
|
||||
{
|
||||
public:
|
||||
enum Emission {MISSION_NONE = 0, MISSION_LEVEL = 1, MISSION_PRIMARY_STAT = 2, MISSION_KILL_HERO = 3, MISSION_KILL_CREATURE = 4,
|
||||
@ -267,7 +267,7 @@ public:
|
||||
}
|
||||
} patrol;
|
||||
|
||||
std::list<HeroBonus> bonuses;
|
||||
BonusList bonuses;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@ -292,9 +292,7 @@ public:
|
||||
int getSightRadious() const; //sight distance (should be used if player-owned structure)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const HeroBonus *getBonus(int from, int id) const;
|
||||
int valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
|
||||
bool hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const; //determines if hero has a bonus of given type (and optionally subtype)
|
||||
|
||||
const std::string &getBiography() const;
|
||||
bool needsLastStack()const;
|
||||
unsigned int getTileCost(const TerrainTile &dest, const TerrainTile &from) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
|
||||
@ -304,6 +302,18 @@ public:
|
||||
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
|
||||
bool canWalkOnSea() const;
|
||||
int getCurrentLuck(int stack=-1, bool town=false) const;
|
||||
|
||||
const BonusList *ownerBonuses() const;
|
||||
const HeroBonus *getBonus(int from, int id) const;
|
||||
int valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
|
||||
bool hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const; //determines if hero has a bonus of given type (and optionally subtype)
|
||||
void getModifiersWDescr(std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype = -1) const; //out: pairs<modifier value, modifier description>
|
||||
template<int N> void getModifiersWDescr(std::vector<std::pair<int,std::string> > &out, const HeroBonus::BonusType (&types)[N]) const //retreive array of types
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
getModifiersWDescr(out, types[i]);
|
||||
}
|
||||
|
||||
std::vector<std::pair<int,std::string> > getCurrentLuckModifiers(int stack=-1, bool town=false) const; //args as above
|
||||
int getCurrentMorale(int stack=-1, bool town=false) const; //if stack - position of creature, if -1 then morale for hero is calculated; town - if bonuses from town (tavern) should be considered
|
||||
std::vector<std::pair<int,std::string> > getCurrentMoraleModifiers(int stack=-1, bool town=false) const; //args as above
|
||||
@ -1078,6 +1088,37 @@ class DLL_EXPORT CGDenOfthieves : public CGObjectInstance
|
||||
void onHeroVisit (const CGHeroInstance * h) const;
|
||||
};
|
||||
|
||||
class DLL_EXPORT CGObelisk : public CPlayersVisited
|
||||
{
|
||||
public:
|
||||
static ui8 obeliskCount; //how many obelisks are on map
|
||||
static std::map<ui8, ui8> visited; //map: color_id => how many obelisks has been visited
|
||||
|
||||
void setPropertyDer (ui8 what, ui32 val);
|
||||
void onHeroVisit(const CGHeroInstance * h) const;
|
||||
void initObj();
|
||||
const std::string & getHoverText() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CPlayersVisited&>(*this);;
|
||||
}
|
||||
};
|
||||
|
||||
class DLL_EXPORT CGLighthouse : public CGObjectInstance
|
||||
{
|
||||
public:
|
||||
void onHeroVisit(const CGHeroInstance * h) const;
|
||||
void initObj();
|
||||
const std::string & getHoverText() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CGObjectInstance&>(*this);;
|
||||
}
|
||||
void giveBonusTo( ui8 player ) const;
|
||||
};
|
||||
|
||||
struct BankConfig
|
||||
{
|
||||
BankConfig() {level = chance = upgradeChance = combatValue = value = rewardDifficulty = easiest = 0; };
|
||||
|
@ -1280,6 +1280,45 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
|
||||
scenarioOps = si;
|
||||
this->map = map;
|
||||
loadTownDInfos();
|
||||
|
||||
//pick grail location
|
||||
if(map->grailPos.x < 0 || map->grailRadious) //grail not set or set within a radius around some place
|
||||
{
|
||||
if(!map->grailRadious) //radius not given -> anywhere on map
|
||||
map->grailRadious = map->width * 2;
|
||||
|
||||
|
||||
std::vector<int3> allowedPos;
|
||||
|
||||
// add all not blocked tiles in range
|
||||
for (int i = 0; i < map->width ; i++)
|
||||
{
|
||||
for (int j = 0; j < map->height ; j++)
|
||||
{
|
||||
for (int k = 0; k <= map->twoLevel ; k++)
|
||||
{
|
||||
const TerrainTile &t = map->terrain[i][j][k];
|
||||
if(!t.blocked
|
||||
&& !t.visitable
|
||||
&& t.tertype != TerrainTile::water
|
||||
&& t.tertype != TerrainTile::rock
|
||||
&& map->grailPos.dist2d(int3(i,j,k)) <= map->grailRadious)
|
||||
allowedPos.push_back(int3(i,j,k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove tiles with holes
|
||||
for(unsigned int no=0; no<map->objects.size(); ++no)
|
||||
if(map->objects[no]->ID == 124)
|
||||
allowedPos -= map->objects[no]->pos;
|
||||
|
||||
if(allowedPos.size())
|
||||
map->grailPos = allowedPos[ran() % allowedPos.size()];
|
||||
else
|
||||
tlog2 << "Warning: Grail cannot be placed, no appropriate tile found!\n";
|
||||
}
|
||||
|
||||
//picking random factions for players
|
||||
for(unsigned int i=0;i<scenarioOps->playerInfos.size();i++)
|
||||
{
|
||||
@ -1293,11 +1332,12 @@ void CGameState::init(StartInfo * si, Mapa * map, int Seed)
|
||||
scenarioOps->playerInfos[i].castle = f;
|
||||
}
|
||||
}
|
||||
|
||||
//randomizing objects
|
||||
for(unsigned int no=0; no<map->objects.size(); ++no)
|
||||
{
|
||||
randomizeObject(map->objects[no]);
|
||||
if(map->objects[no]->ID==26)
|
||||
if(map->objects[no]->ID==EVENTI_TYPE)
|
||||
{
|
||||
map->objects[no]->defInfo->handler=NULL;
|
||||
}
|
||||
@ -1940,7 +1980,7 @@ void CGameState::apply(CPack *pack)
|
||||
applierGs->apps[typ]->applyOnGS(this,pack);
|
||||
}
|
||||
|
||||
PlayerState * CGameState::getPlayer( ui8 color )
|
||||
PlayerState * CGameState::getPlayer( ui8 color, bool verbose )
|
||||
{
|
||||
if(vstd::contains(players,color))
|
||||
{
|
||||
@ -1948,14 +1988,15 @@ PlayerState * CGameState::getPlayer( ui8 color )
|
||||
}
|
||||
else
|
||||
{
|
||||
tlog2 << "Warning: Cannot find info for player " << int(color) << std::endl;
|
||||
if(verbose)
|
||||
tlog2 << "Warning: Cannot find info for player " << int(color) << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const PlayerState * CGameState::getPlayer( ui8 color ) const
|
||||
const PlayerState * CGameState::getPlayer( ui8 color, bool verbose ) const
|
||||
{
|
||||
return (const_cast<CGameState *>(this))->getPlayer(color);
|
||||
return (const_cast<CGameState *>(this))->getPlayer(color, verbose);
|
||||
}
|
||||
|
||||
bool CGameState::getPath(int3 src, int3 dest, const CGHeroInstance * hero, CPath &ret)
|
||||
@ -2163,7 +2204,7 @@ void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int
|
||||
node.accessible = CGPathNode::BLOCKVIS;
|
||||
break;
|
||||
}
|
||||
else if(obj->ID != 26) //pathfinder should ignore placed events
|
||||
else if(obj->ID != EVENTI_TYPE) //pathfinder should ignore placed events
|
||||
{
|
||||
node.accessible = CGPathNode::VISITABLE;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include "StackFeature.h"
|
||||
#include "HeroBonus.h"
|
||||
#ifdef _WIN32
|
||||
#include <tchar.h>
|
||||
#else
|
||||
@ -117,6 +118,7 @@ public:
|
||||
std::vector<CGTownInstance *> towns;
|
||||
std::vector<CGHeroInstance *> availableHeroes; //heroes available in taverns
|
||||
std::vector<CGDwelling *> dwellings; //used for town growth
|
||||
BonusList bonuses; //player bonuses
|
||||
|
||||
ui8 status; //0 - in game, 1 - loser, 2 - winner <- uses EStatus enum
|
||||
ui8 daysWithoutCastle;
|
||||
@ -126,7 +128,7 @@ public:
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & color & serial & human & currentSelection & fogOfWarMap & resources & status;
|
||||
h & heroes & towns & availableHeroes & dwellings & status & daysWithoutCastle;
|
||||
h & heroes & towns & availableHeroes & dwellings & bonuses & status & daysWithoutCastle;
|
||||
|
||||
// ui32 size;
|
||||
// if(h.saving) //write subids of available heroes
|
||||
@ -405,8 +407,8 @@ public:
|
||||
|
||||
boost::shared_mutex *mx;
|
||||
|
||||
PlayerState *getPlayer(ui8 color);
|
||||
const PlayerState *getPlayer(ui8 color) const;
|
||||
PlayerState *getPlayer(ui8 color, bool verbose = true);
|
||||
const PlayerState *getPlayer(ui8 color, bool verbose = true) const;
|
||||
void init(StartInfo * si, Mapa * map, int Seed);
|
||||
void loadTownDInfos();
|
||||
void randomizeObject(CGObjectInstance *cur);
|
||||
|
73
lib/HeroBonus.cpp
Normal file
73
lib/HeroBonus.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#define VCMI_DLL
|
||||
#include "HeroBonus.h"
|
||||
|
||||
int BonusList::valOfBonuses( HeroBonus::BonusType type, int subtype /*= -1*/ ) const /*subtype -> subtype of bonus, if -1 then any */
|
||||
{
|
||||
if(!this) //to avoid null-checking in maany places -> no bonus list means 0 bonus value
|
||||
return 0;
|
||||
|
||||
int ret = 0;
|
||||
if(subtype == -1)
|
||||
{
|
||||
for(const_iterator i = begin(); i != end(); i++)
|
||||
if(i->type == type)
|
||||
ret += i->val;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(const_iterator i = begin(); i != end(); i++)
|
||||
if(i->type == type && i->subtype == subtype)
|
||||
ret += i->val;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool BonusList::hasBonusOfType( HeroBonus::BonusType type, int subtype /*= -1*/ ) const
|
||||
{
|
||||
if(!this) //to avoid null-checking in maany places -> no bonus list means there is no searched bonus
|
||||
return 0;
|
||||
|
||||
if(subtype == -1) //any subtype
|
||||
{
|
||||
for(const_iterator i = begin(); i != end(); i++)
|
||||
if(i->type == type)
|
||||
return true;
|
||||
}
|
||||
else //given subtype
|
||||
{
|
||||
for(const_iterator i = begin(); i != end(); i++)
|
||||
if(i->type == type && i->subtype == subtype)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const HeroBonus * BonusList::getBonus( int from, int id ) const
|
||||
{
|
||||
if(!this) //to avoid null-checking in maany places -> no bonus list means bonus cannot be retreived
|
||||
return NULL;
|
||||
|
||||
for (const_iterator i = begin(); i != end(); i++)
|
||||
if(i->source == from && i->id == id)
|
||||
return &*i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void BonusList::getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype /*= -1 */ ) const
|
||||
{
|
||||
if(!this) //to avoid null-checking in maany places -> no bonus list means nothing has to be done here
|
||||
return;
|
||||
|
||||
if(subtype == -1)
|
||||
{
|
||||
for(const_iterator i = begin(); i != end(); i++)
|
||||
if(i->type == type)
|
||||
out.push_back(std::make_pair(i->val, i->description));
|
||||
}
|
||||
else
|
||||
{
|
||||
for(const_iterator i = begin(); i != end(); i++)
|
||||
if(i->type == type && i->subtype == subtype)
|
||||
out.push_back(std::make_pair(i->val, i->description));
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "../global.h"
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
/*
|
||||
* HeroBonus.h, part of VCMI engine
|
||||
@ -75,6 +76,19 @@ struct DLL_EXPORT HeroBonus
|
||||
{
|
||||
subtype = -1;
|
||||
}
|
||||
|
||||
// //comparison
|
||||
// bool operator==(const HeroBonus &other)
|
||||
// {
|
||||
// return &other == this;
|
||||
// //TODO: what is best logic for that?
|
||||
// }
|
||||
// bool operator<(const HeroBonus &other)
|
||||
// {
|
||||
// return &other < this;
|
||||
// //TODO: what is best logic for that?
|
||||
// }
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & duration & type & subtype & source & val & id & description;
|
||||
@ -96,4 +110,23 @@ struct DLL_EXPORT HeroBonus
|
||||
{
|
||||
return hb.source==source && (id==0xffffff || hb.id==id);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static const HeroBonus::BonusType MORALE_AFFECTING[] = {HeroBonus::LUCK, HeroBonus::MORALE_AND_LUCK};
|
||||
static const HeroBonus::BonusType LUCK_AFFECTING[] = {HeroBonus::MORALE, HeroBonus::MORALE_AND_LUCK};
|
||||
typedef std::vector<std::pair<int,std::string> > TModDescr; //modifiers values and their descriptions
|
||||
|
||||
class BonusList : public std::list<HeroBonus>
|
||||
{
|
||||
public:
|
||||
int DLL_EXPORT valOfBonuses(HeroBonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then any
|
||||
bool DLL_EXPORT hasBonusOfType(HeroBonus::BonusType type, int subtype = -1) const;
|
||||
const DLL_EXPORT HeroBonus * getBonus( int from, int id ) const;
|
||||
void DLL_EXPORT getModifiersWDescr( std::vector<std::pair<int,std::string> > &out, HeroBonus::BonusType type, int subtype = -1 ) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<std::list<HeroBonus>&>(*this);
|
||||
}
|
||||
};
|
@ -256,4 +256,9 @@ inline TerrainTile * IGameCallback::getTile( int3 pos )
|
||||
if(!gs->map->isInTheMap(pos))
|
||||
return NULL;
|
||||
return &gs->map->getTile(pos);
|
||||
}
|
||||
|
||||
const PlayerState * IGameCallback::getPlayerState( int color )
|
||||
{
|
||||
return gs->getPlayer(color, false);
|
||||
}
|
@ -34,6 +34,7 @@ class CArtHandler;
|
||||
class CArtifact;
|
||||
class CArmedInstance;
|
||||
struct TerrainTile;
|
||||
struct PlayerState;
|
||||
|
||||
class DLL_EXPORT IGameCallback
|
||||
{
|
||||
@ -63,6 +64,7 @@ public:
|
||||
virtual void getAllowedSpells(std::vector<ui16> &out, ui16 level);
|
||||
virtual int3 getMapSize(); //returns size of the map
|
||||
virtual TerrainTile * getTile(int3 pos);
|
||||
virtual const PlayerState * getPlayerState(int color);
|
||||
|
||||
//do sth
|
||||
virtual void changeSpells(int hid, bool give, const std::set<ui32> &spells)=0;
|
||||
|
@ -357,17 +357,24 @@ struct SetAvailableHeroes : public CPackForClient //113
|
||||
|
||||
struct GiveBonus : public CPackForClient //115
|
||||
{
|
||||
GiveBonus(){type = 115;};
|
||||
GiveBonus(ui8 Who = 0)
|
||||
{
|
||||
who = Who;
|
||||
type = 115;
|
||||
}
|
||||
|
||||
void applyCl(CClient *cl);
|
||||
DLL_EXPORT void applyGs(CGameState *gs);
|
||||
|
||||
ui32 hid;
|
||||
enum {HERO, PLAYER};
|
||||
ui8 who; //who receives bonus, uses enum above
|
||||
ui32 id; //hero or player id
|
||||
HeroBonus bonus;
|
||||
MetaString bdescr;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & bonus & hid & bdescr;
|
||||
h & bonus & id & bdescr & who;
|
||||
}
|
||||
};
|
||||
|
||||
@ -408,6 +415,34 @@ struct PlayerEndsGame : public CPackForClient //117
|
||||
};
|
||||
|
||||
|
||||
struct RemoveBonus : public CPackForClient //118
|
||||
{
|
||||
RemoveBonus(ui8 Who = 0)
|
||||
{
|
||||
who = Who;
|
||||
type = 118;
|
||||
}
|
||||
|
||||
void applyCl(CClient *cl);
|
||||
DLL_EXPORT void applyGs(CGameState *gs);
|
||||
|
||||
enum {HERO, PLAYER};
|
||||
ui8 who; //who receives bonus, uses enum above
|
||||
ui32 whoID; //hero or player id
|
||||
|
||||
//vars to identify bonus: its source
|
||||
ui8 source;
|
||||
ui32 id; //source id
|
||||
|
||||
//used locally: copy of removed bonus
|
||||
HeroBonus bonus;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & source & id & who & whoID;
|
||||
}
|
||||
};
|
||||
|
||||
struct RemoveObject : public CPackForClient //500
|
||||
{
|
||||
RemoveObject(){type = 500;};
|
||||
@ -573,7 +608,7 @@ struct OpenWindow : public CPackForClient //517
|
||||
OpenWindow(){type = 517;};
|
||||
void applyCl(CClient *cl);
|
||||
|
||||
enum EWindow {EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL, SHIPYARD_WINDOW, THIEVES_GUILD};
|
||||
enum EWindow {EXCHANGE_WINDOW, RECRUITMENT_FIRST, RECRUITMENT_ALL, SHIPYARD_WINDOW, THIEVES_GUILD, PUZZLE_MAP};
|
||||
ui8 window;
|
||||
ui32 id1, id2;
|
||||
|
||||
|
@ -186,16 +186,32 @@ DLL_EXPORT void SetAvailableHeroes::applyGs( CGameState *gs )
|
||||
|
||||
DLL_EXPORT void GiveBonus::applyGs( CGameState *gs )
|
||||
{
|
||||
CGHeroInstance *h = gs->getHero(hid);
|
||||
h->bonuses.push_back(bonus);
|
||||
BonusList *bonuses = NULL;
|
||||
switch(who)
|
||||
{
|
||||
case HERO:
|
||||
{
|
||||
CGHeroInstance *h = gs->getHero(id);
|
||||
assert(h);
|
||||
bonuses = &h->bonuses;
|
||||
}
|
||||
break;
|
||||
case PLAYER:
|
||||
{
|
||||
PlayerState *p = gs->getPlayer(id);
|
||||
assert(p);
|
||||
bonuses = &p->bonuses;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
std::string &descr = h->bonuses.back().description;
|
||||
bonuses->push_back(bonus);
|
||||
std::string &descr = bonuses->back().description;
|
||||
|
||||
if(!bdescr.message.size()
|
||||
&& bonus.source == HeroBonus::OBJECT
|
||||
&& (bonus.type == HeroBonus::LUCK || bonus.type == HeroBonus::MORALE || bonus.type == HeroBonus::MORALE_AND_LUCK)
|
||||
&& gs->map->objects[bonus.id]->ID == 26) //it's morale/luck bonus from an event without description
|
||||
&& gs->map->objects[bonus.id]->ID == EVENTI_TYPE) //it's morale/luck bonus from an event without description
|
||||
{
|
||||
descr = VLC->generaltexth->arraytxt[bonus.val > 0 ? 110 : 109]; //+/-%d Temporary until next battle"
|
||||
boost::replace_first(descr,"%d",boost::lexical_cast<std::string>(std::abs(bonus.val)));
|
||||
@ -225,6 +241,21 @@ DLL_EXPORT void PlayerEndsGame::applyGs( CGameState *gs )
|
||||
p->status = victory ? 2 : 1;
|
||||
}
|
||||
|
||||
DLL_EXPORT void RemoveBonus::applyGs( CGameState *gs )
|
||||
{
|
||||
std::list<HeroBonus> &bonuses = (who == HERO ? gs->getHero(whoID)->bonuses : gs->getPlayer(whoID)->bonuses);
|
||||
|
||||
for(std::list<HeroBonus>::iterator i = bonuses.begin(); i != bonuses.end(); i++)
|
||||
{
|
||||
if(i->source == source && i->id == id)
|
||||
{
|
||||
bonus = *i; //backup bonus (to show to interfaces later)
|
||||
bonuses.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DLL_EXPORT void RemoveObject::applyGs( CGameState *gs )
|
||||
{
|
||||
CGObjectInstance *obj = gs->map->objects[id];
|
||||
@ -588,6 +619,10 @@ DLL_EXPORT void NewTurn::applyGs( CGameState *gs )
|
||||
i->second.daysWithoutCastle = 0;
|
||||
else
|
||||
i->second.daysWithoutCastle++;
|
||||
|
||||
i->second.bonuses.remove_if(HeroBonus::OneDay);
|
||||
if(gs->getDate(1) == 7) //new week
|
||||
i->second.bonuses.remove_if(HeroBonus::OneWeek);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,8 @@ void registerTypes1(Serializer &s)
|
||||
s.template registerType<CGObjectInstance>();
|
||||
s.template registerType<COPWBonus>();
|
||||
s.template registerType<CGDenOfthieves>();
|
||||
s.template registerType<CGObelisk>();
|
||||
s.template registerType<CGLighthouse>();
|
||||
}
|
||||
|
||||
template<typename Serializer> DLL_EXPORT
|
||||
@ -82,6 +84,7 @@ void registerTypes2(Serializer &s)
|
||||
s.template registerType<GiveBonus>();
|
||||
s.template registerType<ChangeObjPos>();
|
||||
s.template registerType<PlayerEndsGame>();
|
||||
s.template registerType<RemoveBonus>();
|
||||
s.template registerType<RemoveObject>();
|
||||
s.template registerType<TryMoveHero>();
|
||||
s.template registerType<SetGarrisons>();
|
||||
|
@ -270,6 +270,10 @@
|
||||
RelativePath="..\hch\CBuildingHandler.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\hch\CCampaignHandler.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\CConsoleHandler.cpp"
|
||||
>
|
||||
@ -314,6 +318,10 @@
|
||||
RelativePath="..\hch\CTownHandler.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\HeroBonus.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\IGameCallback.cpp"
|
||||
>
|
||||
@ -356,6 +364,10 @@
|
||||
RelativePath="..\hch\CBuildingHandler.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\hch\CCampaignHandler.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\CConsoleHandler.h"
|
||||
>
|
||||
|
18
lib/map.cpp
18
lib/map.cpp
@ -529,6 +529,7 @@ void Mapa::addBlockVisTiles(CGObjectInstance * obj)
|
||||
}
|
||||
}
|
||||
Mapa::Mapa(std::string filename)
|
||||
:grailPos(-1, -1, -1)
|
||||
{
|
||||
int mapsize = 0;
|
||||
|
||||
@ -1318,7 +1319,7 @@ void Mapa::readDefInfo( const unsigned char * bufor, int &i)
|
||||
vinya->visitDir = 0xff;
|
||||
}
|
||||
|
||||
if(vinya->id == 26)
|
||||
if(vinya->id == EVENTI_TYPE)
|
||||
std::memset(vinya->blockMap,255,6);
|
||||
|
||||
//calculating coverageMap
|
||||
@ -1353,7 +1354,7 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
|
||||
|
||||
switch(defInfo->id)
|
||||
{
|
||||
case 26: //for event objects
|
||||
case EVENTI_TYPE: //for event objects
|
||||
{
|
||||
CGEvent *evnt = new CGEvent();
|
||||
nobj = evnt;
|
||||
@ -1648,7 +1649,6 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
|
||||
i+=3;
|
||||
break;
|
||||
}
|
||||
case 42: //lighthouse
|
||||
case 220://mine (?)
|
||||
{
|
||||
nobj = new CGObjectInstance();
|
||||
@ -1911,11 +1911,23 @@ void Mapa::readObjects( const unsigned char * bufor, int &i)
|
||||
nobj = new CGDenOfthieves();
|
||||
break;
|
||||
}
|
||||
case 57: //Obelisk
|
||||
{
|
||||
nobj = new CGObelisk();
|
||||
break;
|
||||
}
|
||||
case 42: //Lighthouse
|
||||
{
|
||||
nobj = new CGLighthouse();
|
||||
nobj->tempOwner = readNormalNr(bufor,i); i+=4;
|
||||
break;
|
||||
}
|
||||
default: //any other object
|
||||
{
|
||||
nobj = new CGObjectInstance();
|
||||
break;
|
||||
}
|
||||
|
||||
} //end of main switch
|
||||
|
||||
nobj->pos = pos;
|
||||
|
@ -375,12 +375,13 @@ struct DLL_EXPORT Mapa : public CMapHeader
|
||||
objects.resize(hlp);
|
||||
}
|
||||
|
||||
//static structures
|
||||
//static members
|
||||
h & CGTeleport::objs;
|
||||
h & CGTeleport::gates;
|
||||
h & CGKeys::playerKeyMap;
|
||||
h & CGMagi::eyelist;
|
||||
h & CGPyramid::pyramidConfig;
|
||||
h & CGObelisk::obeliskCount & CGObelisk::visited;
|
||||
|
||||
for(unsigned int i=0; i<objects.size(); i++)
|
||||
{
|
||||
|
@ -301,7 +301,7 @@ void CMapHandler::initObjectRects()
|
||||
}
|
||||
static void processDef (CGDefInfo* def)
|
||||
{
|
||||
if(def->id == 26) //if it's event, return from function
|
||||
if(def->id == EVENTI_TYPE)
|
||||
return;
|
||||
|
||||
if(!def->handler) //if object has already set handler (eg. heroes) it should not be overwritten
|
||||
|
@ -856,6 +856,9 @@ void CGameHandler::newTurn()
|
||||
for(std::list<HeroBonus>::iterator j = h->bonuses.begin(); j != h->bonuses.end(); j++)
|
||||
if(j->type == HeroBonus::GENERATE_RESOURCE)
|
||||
n.res[i->first][j->subtype] += j->val;
|
||||
|
||||
//TODO player bonuses
|
||||
|
||||
}
|
||||
}
|
||||
//n.res.push_back(r);
|
||||
@ -1276,7 +1279,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
|
||||
{
|
||||
GiveBonus gs;
|
||||
gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::PRIMARY_SKILL, HeroBonus::OBJECT, val, -1, "", i);
|
||||
gs.hid = hero2->id;
|
||||
gs.id = hero2->id;
|
||||
sendAndApply(&gs);
|
||||
}
|
||||
}
|
||||
@ -1334,7 +1337,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
|
||||
|
||||
GiveBonus gs;
|
||||
gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::MAGIC_SCHOOL_SKILL, HeroBonus::OBJECT, 3, -1, "", bonusSubtype);
|
||||
gs.hid = cHero->id;
|
||||
gs.id = cHero->id;
|
||||
|
||||
sendAndApply(&gs);
|
||||
}
|
||||
@ -1393,7 +1396,7 @@ void CGameHandler::setupBattle( BattleInfo * curB, int3 tile, const CCreatureSet
|
||||
|
||||
GiveBonus gs;
|
||||
gs.bonus = HeroBonus(HeroBonus::ONE_BATTLE, HeroBonus::BLOCK_SPELLS_ABOVE_LEVEL, HeroBonus::OBJECT, 1, -1, "", bonusSubtype);
|
||||
gs.hid = cHero->id;
|
||||
gs.id = cHero->id;
|
||||
|
||||
sendAndApply(&gs);
|
||||
}
|
||||
@ -1612,7 +1615,13 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
|
||||
//call objects if they are visited
|
||||
|
||||
if(t.visitableObjects.size())
|
||||
objectVisited(t.visitableObjects.back(), h);
|
||||
{
|
||||
//to prevent self-visiting heroes on space press
|
||||
if(t.visitableObjects.back() != h)
|
||||
objectVisited(t.visitableObjects.back(), h);
|
||||
else if(t.visitableObjects.size() > 1)
|
||||
objectVisited(*(t.visitableObjects.end()-2),h);
|
||||
}
|
||||
// BOOST_FOREACH(CGObjectInstance *obj, t.visitableObjects)
|
||||
// {
|
||||
// objectVisited(obj, h);
|
||||
|
Loading…
Reference in New Issue
Block a user