1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

* no more divison by 0 in slider

* little more code for upgrades 
* version set to 0.6
* "plural" reference names for Conflux creatures (starting armies of Conflux heroes should now be working)
* minor changes
This commit is contained in:
Michał W. Urbańczyk 2008-05-31 20:37:54 +00:00
parent 88a29416da
commit 7d7abbb68d
14 changed files with 205 additions and 35 deletions

View File

@ -183,9 +183,14 @@ void CSlider::moveTo(int to)
else if(to>amount) else if(to>amount)
to=amount; to=amount;
value = to; value = to;
float part = (float)to/amount; if(amount)
part*=(pos.w-48); {
slider.pos.x = part + pos.x + 16; float part = (float)to/amount;
part*=(pos.w-48);
slider.pos.x = part + pos.x + 16;
}
else
slider.pos.x = pos.x+16;
moved(to); moved(to);
} }

View File

@ -559,7 +559,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
switch(CBattleHex::mutualPosition(aStack.position, dest)) //attack direction switch(CBattleHex::mutualPosition(aStack.position, dest)) //attack direction
{ {
case 0: case 0:
/*reverseCreature(ID, aStack.position, true); //reverseCreature(ID, aStack.position, true);
creAnims[ID]->setType(10); creAnims[ID]->setType(10);
for(int i=0; i<creAnims[ID]->framesInGroup(10); ++i) for(int i=0; i<creAnims[ID]->framesInGroup(10); ++i)
{ {
@ -567,7 +567,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
CSDL_Ext::update(); CSDL_Ext::update();
SDL_framerateDelay(LOCPLINT->mainFPSmng); SDL_framerateDelay(LOCPLINT->mainFPSmng);
} }
reverseCreature(ID, aStack.position, true);*/ //reverseCreature(ID, aStack.position, true);
break; break;
case 1: case 1:
creAnims[ID]->setType(10); creAnims[ID]->setType(10);
@ -597,7 +597,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
} }
break; break;
case 4: case 4:
/*reverseCreature(ID, aStack.position, true); //reverseCreature(ID, aStack.position, true);
creAnims[ID]->setType(12); creAnims[ID]->setType(12);
for(int i=0; i<creAnims[ID]->framesInGroup(12); ++i) for(int i=0; i<creAnims[ID]->framesInGroup(12); ++i)
{ {
@ -605,10 +605,10 @@ void CBattleInterface::stackAttacking(int ID, int dest)
CSDL_Ext::update(); CSDL_Ext::update();
SDL_framerateDelay(LOCPLINT->mainFPSmng); SDL_framerateDelay(LOCPLINT->mainFPSmng);
} }
reverseCreature(ID, aStack.position, true);*/ //reverseCreature(ID, aStack.position, true);
break; break;
case 5: case 5:
/*reverseCreature(ID, aStack.position, true); reverseCreature(ID, aStack.position, true);
creAnims[ID]->setType(11); creAnims[ID]->setType(11);
for(int i=0; i<creAnims[ID]->framesInGroup(11); ++i) for(int i=0; i<creAnims[ID]->framesInGroup(11); ++i)
{ {
@ -616,7 +616,7 @@ void CBattleInterface::stackAttacking(int ID, int dest)
CSDL_Ext::update(); CSDL_Ext::update();
SDL_framerateDelay(LOCPLINT->mainFPSmng); SDL_framerateDelay(LOCPLINT->mainFPSmng);
} }
reverseCreature(ID, aStack.position, true);*/ reverseCreature(ID, aStack.position, true);
break; break;
} }
creAnims[ID]->setType(2); creAnims[ID]->setType(2);

View File

@ -281,7 +281,7 @@ void CCallback::recruitCreatures(const CGObjectInstance *obj, int ID, int amount
for(int i=0;i<7;i++) for(int i=0;i<7;i++)
{ {
if(!t->army.slots[i].first) if((!t->army.slots[i].first) || (t->army.slots[i].first->idNumber == ID)) //slot is free or there is saem creature
{ {
slot = i; slot = i;
break; break;
@ -295,9 +295,15 @@ void CCallback::recruitCreatures(const CGObjectInstance *obj, int ID, int amount
gs->players[player].resources[i] -= (CGI->creh->creatures[ID].cost[i] * amount); gs->players[player].resources[i] -= (CGI->creh->creatures[ID].cost[i] * amount);
t->strInfo.creatures[ser] -= amount; t->strInfo.creatures[ser] -= amount;
if(t->army.slots[slot].first) //add new creatures to the existing stack
t->army.slots[slot].first = &CGI->creh->creatures[ID]; {
t->army.slots[slot].second = amount; t->army.slots[slot].second += amount;
}
else //create new stack in the garrison
{
t->army.slots[slot].first = &CGI->creh->creatures[ID];
t->army.slots[slot].second = amount;
}
CGI->playerint[gs->players[player].serial]->garrisonChanged(obj); CGI->playerint[gs->players[player].serial]->garrisonChanged(obj);
} }
@ -306,15 +312,53 @@ void CCallback::recruitCreatures(const CGObjectInstance *obj, int ID, int amount
bool CCallback::dismissCreature(const CArmedInstance *obj, int stackPos) bool CCallback::dismissCreature(const CArmedInstance *obj, int stackPos)
{ {
return false; if(obj->tempOwner != player)
return false;
CArmedInstance *ob = const_cast<CArmedInstance*>(obj);
ob->army.slots.erase(stackPos);
CGI->playerint[gs->players[player].serial]->garrisonChanged(obj);
return true;
} }
bool CCallback::upgradeCreature(const CArmedInstance *obj, int stackPos) bool CCallback::upgradeCreature(const CArmedInstance *obj, int stackPos, int newID)
{ {
return false; return false;
} }
UpgradeInfo CCallback::getUpgradeInfo(const CArmedInstance *obj, int stackPos) UpgradeInfo CCallback::getUpgradeInfo(const CArmedInstance *obj, int stackPos)
{ {
return UpgradeInfo(); UpgradeInfo ret;
CCreature *base = ((CArmedInstance*)obj)->army.slots[stackPos].first;
if((obj->ID == 98) || ((obj->ID == 34) && static_cast<const CGHeroInstance*>(obj)->visitedTown))
{
CGTownInstance * t;
if(obj->ID == 98)
t = static_cast<CGTownInstance *>(const_cast<CArmedInstance *>(obj));
else
t = static_cast<const CGHeroInstance*>(obj)->visitedTown;
for(std::set<int>::iterator i=t->builtBuildings.begin(); i!=t->builtBuildings.end(); i++)
{
if( (*i) >= 37 && (*i) < 44 ) //upgraded creature dwelling
{
int nid = t->town->upgradedCreatures[(*i)-37]; //upgrade offered by that building
if(base->upgrades.find(nid) != base->upgrades.end()) //possible upgrade
{
ret.newID.push_back(nid);
ret.cost.push_back(std::set<std::pair<int,int> >());
for(int j=0;j<RESOURCE_QUANTITY;j++)
{
int dif = CGI->creh->creatures[nid].cost[j] - base->cost[j];
if(dif)
ret.cost[ret.cost.size()-1].insert(std::make_pair(j,dif));
}
}
}
}//end for
}
//TODO: check if hero ability makes some upgrades possible
if(ret.newID.size())
ret.oldID = base->idNumber;
return ret;
} }
int CCallback::howManyTowns() int CCallback::howManyTowns()
@ -907,6 +951,7 @@ void CScriptCallback::heroVisitCastle(CGObjectInstance * ob, int heroID)
if(n = dynamic_cast<CGTownInstance*>(ob)) if(n = dynamic_cast<CGTownInstance*>(ob))
{ {
n->visitingHero = CGI->state->getHero(heroID,0); n->visitingHero = CGI->state->getHero(heroID,0);
CGI->state->getHero(heroID,0)->visitedTown = n;
for(int b=0; b<CGI->playerint.size(); ++b) for(int b=0; b<CGI->playerint.size(); ++b)
{ {
if(CGI->playerint[b]->playerID == getHeroOwner(heroID)) if(CGI->playerint[b]->playerID == getHeroOwner(heroID))
@ -925,6 +970,7 @@ void CScriptCallback::stopHeroVisitCastle(CGObjectInstance * ob, int heroID)
CGTownInstance * n; CGTownInstance * n;
if(n = dynamic_cast<CGTownInstance*>(ob)) if(n = dynamic_cast<CGTownInstance*>(ob))
{ {
CGI->state->getHero(heroID,0)->visitedTown = NULL;
if(n->visitingHero && n->visitingHero->type->ID == heroID) if(n->visitingHero && n->visitingHero->type->ID == heroID)
n->visitingHero = NULL; n->visitingHero = NULL;
return; return;

View File

@ -35,7 +35,7 @@ public:
virtual bool swapArifacts(const CGHeroInstance * hero1, bool worn1, int pos1, const CGHeroInstance * hero2, bool worn2, int pos2)=0; //swaps artifacts between two given heroes virtual bool swapArifacts(const CGHeroInstance * hero1, bool worn1, int pos1, const CGHeroInstance * hero2, bool worn2, int pos2)=0; //swaps artifacts between two given heroes
virtual void recruitCreatures(const CGObjectInstance *obj, int ID, int amount)=0; virtual void recruitCreatures(const CGObjectInstance *obj, int ID, int amount)=0;
virtual bool dismissCreature(const CArmedInstance *obj, int stackPos)=0; virtual bool dismissCreature(const CArmedInstance *obj, int stackPos)=0;
virtual bool upgradeCreature(const CArmedInstance *obj, int stackPos)=0; virtual bool upgradeCreature(const CArmedInstance *obj, int stackPos, int newID=-1)=0; //if newID==-1 then best possible upgrade will be made
//get info //get info
virtual bool verifyPath(CPath * path, bool blockSea)=0; virtual bool verifyPath(CPath * path, bool blockSea)=0;
@ -103,7 +103,7 @@ public:
bool buildBuilding(const CGTownInstance *town, int buildingID); bool buildBuilding(const CGTownInstance *town, int buildingID);
void recruitCreatures(const CGObjectInstance *obj, int ID, int amount); void recruitCreatures(const CGObjectInstance *obj, int ID, int amount);
bool dismissCreature(const CArmedInstance *obj, int stackPos); bool dismissCreature(const CArmedInstance *obj, int stackPos);
bool upgradeCreature(const CArmedInstance *obj, int stackPos); bool upgradeCreature(const CArmedInstance *obj, int stackPos, int newID=-1);
//get info //get info

View File

@ -62,7 +62,7 @@
CGameInfo* CGI; CGameInfo* CGI;
#endif #endif
#define CHUNK 16384 #define CHUNK 16384
const char * NAME = "VCMI 0.59"; const char * NAME = "VCMI 0.6";
SDL_Color playerColorPalette[256]; //palette to make interface colors good SDL_Color playerColorPalette[256]; //palette to make interface colors good

View File

@ -104,7 +104,7 @@ void CGarrisonSlot::hover (bool on)
} }
} }
const CGObjectInstance * CGarrisonSlot::getObj() const CArmedInstance * CGarrisonSlot::getObj()
{ {
return (!upg)?(owner->oup):(owner->odown); return (!upg)?(owner->oup):(owner->odown);
} }
@ -142,7 +142,12 @@ void CGarrisonSlot::clickLeft(tribool down)
{ {
if(owner->highlighted == this) //view info if(owner->highlighted == this) //view info
{ {
//TODO: view creature info UpgradeInfo pom = LOCPLINT->cb->getUpgradeInfo(getObj(),ID);
(new CCreInfoWindow
(creature->idNumber,1,NULL,
(pom.oldID>=0)?(boost::bind(&CCallback::upgradeCreature,LOCPLINT->cb,getObj(),ID,-1)):(boost::function<void()>()), //if upgrade is possible we'll bind proper function in callback
boost::bind(&CCallback::dismissCreature,LOCPLINT->cb,getObj(),ID)))
->activate();
owner->highlighted = NULL; owner->highlighted = NULL;
show(); show();
refr = true; refr = true;
@ -399,7 +404,7 @@ void CGarrisonInt::splitStacks(int am2)
am2); am2);
} }
CGarrisonInt::CGarrisonInt(int x, int y, int inx, int iny, SDL_Surface *pomsur, int OX, int OY, const CGObjectInstance *s1, const CGObjectInstance *s2) CGarrisonInt::CGarrisonInt(int x, int y, int inx, int iny, SDL_Surface *pomsur, int OX, int OY, const CArmedInstance *s1, const CArmedInstance *s2)
:interx(inx),intery(iny),sur(pomsur),highlighted(NULL),sup(NULL),sdown(NULL),oup(s1),odown(s2), :interx(inx),intery(iny),sur(pomsur),highlighted(NULL),sup(NULL),sdown(NULL),oup(s1),odown(s2),
offx(OX),offy(OY) offx(OX),offy(OY)
{ {
@ -2926,10 +2931,17 @@ void CCreInfoWindow::show(SDL_Surface * to)
blitAt(bitmap,pos.x,pos.y,screen); blitAt(bitmap,pos.x,pos.y,screen);
blitAt(CGI->creh->backgrounds[c->faction],pos.x+21,pos.y+48,screen); blitAt(CGI->creh->backgrounds[c->faction],pos.x+21,pos.y+48,screen);
anim->nextFrameMiddle(screen,pos.x+90,pos.y+95,true,false,false); anim->nextFrameMiddle(screen,pos.x+90,pos.y+95,true,false,false);
if(upgrade)
upgrade->show();
if(dismiss)
dismiss->show();
if(ok)
ok->show();
} }
CCreInfoWindow::CCreInfoWindow(int Cid, int Type, StackState *State, boost::function<void()> Upg, boost::function<void()> Dsm) CCreInfoWindow::CCreInfoWindow
:ok(0),dismiss(0),upgrade(0),type(Type) (int Cid, int Type, StackState *State, boost::function<void()> Upg, boost::function<void()> Dsm)
:ok(0),dismiss(0),upgrade(0),type(Type),dsm(Dsm)
{ {
c = &CGI->creh->creatures[Cid]; c = &CGI->creh->creatures[Cid];
bitmap = CGI->bitmaph->loadBitmap("CRSTKPU.bmp"); bitmap = CGI->bitmaph->loadBitmap("CRSTKPU.bmp");
@ -3008,9 +3020,10 @@ CCreInfoWindow::CCreInfoWindow(int Cid, int Type, StackState *State, boost::func
if(type) if(type)
{ {
if(Upg) if(Upg)
upgrade = new AdventureMapButton("",CGI->preth->zelp[446].second,Upg,76,237,"IVIEWCR.DEF"); upgrade = new AdventureMapButton("",CGI->preth->zelp[446].second,Upg,pos.x+76,pos.y+237,"IVIEWCR.DEF");
if(Dsm) if(Dsm)
dismiss = new AdventureMapButton("",CGI->preth->zelp[445].second,Upg,21,237,"IVIEWCR2.DEF"); dismiss = new AdventureMapButton("",CGI->preth->zelp[445].second,boost::bind(&CCreInfoWindow::dismissF,this),pos.x+21,pos.y+237,"IVIEWCR2.DEF");
ok = new AdventureMapButton("",CGI->preth->zelp[445].second,boost::bind(&CCreInfoWindow::close,this),pos.x+216,pos.y+237,"IOKAY.DEF");
} }
else else
{ {
@ -3032,6 +3045,8 @@ void CCreInfoWindow::activate()
dismiss->activate(); dismiss->activate();
if(upgrade) if(upgrade)
upgrade->activate(); upgrade->activate();
if(type)
LOCPLINT->curint->deactivate();
} }
void CCreInfoWindow::close() void CCreInfoWindow::close()
{ {
@ -3049,6 +3064,11 @@ void CCreInfoWindow::clickRight(boost::logic::tribool down)
return; return;
close(); close();
} }
void CCreInfoWindow::dismissF()
{
dsm();
close();
}
void CCreInfoWindow::keyPressed (SDL_KeyboardEvent & key) void CCreInfoWindow::keyPressed (SDL_KeyboardEvent & key)
{ {
} }

View File

@ -245,7 +245,7 @@ public:
bool active; bool active;
virtual void hover (bool on); virtual void hover (bool on);
const CGObjectInstance * getObj(); const CArmedInstance * getObj();
void clickRight (boost::logic::tribool down); void clickRight (boost::logic::tribool down);
void clickLeft(boost::logic::tribool down); void clickLeft(boost::logic::tribool down);
void activate(); void activate();
@ -269,7 +269,7 @@ public:
const CCreatureSet *set2; const CCreatureSet *set2;
std::vector<CGarrisonSlot*> *sup, *sdown; std::vector<CGarrisonSlot*> *sup, *sdown;
const CGObjectInstance *oup, *odown; const CArmedInstance *oup, *odown;
void activate(); void activate();
void deactivate(); void deactivate();
@ -283,7 +283,7 @@ public:
void splitClick(); void splitClick();
void splitStacks(int am2); void splitStacks(int am2);
CGarrisonInt(int x, int y, int inx, int iny, SDL_Surface *pomsur, int OX, int OY, const CGObjectInstance *s1, const CGObjectInstance *s2=NULL); CGarrisonInt(int x, int y, int inx, int iny, SDL_Surface *pomsur, int OX, int OY, const CArmedInstance *s1, const CArmedInstance *s2=NULL);
~CGarrisonInt(); ~CGarrisonInt();
}; };
@ -514,6 +514,7 @@ public:
int type;//0 - rclick popup; 1 - normal window int type;//0 - rclick popup; 1 - normal window
SDL_Surface *bitmap; SDL_Surface *bitmap;
boost::function<void()> dsm;
CCreatureAnimation *anim; CCreatureAnimation *anim;
CCreature *c; CCreature *c;
@ -523,6 +524,7 @@ public:
void activate(); void activate();
void close(); void close();
void clickRight(boost::logic::tribool down); void clickRight(boost::logic::tribool down);
void dismissF();
void keyPressed (SDL_KeyboardEvent & key); void keyPressed (SDL_KeyboardEvent & key);
void deactivate(); void deactivate();
void show(SDL_Surface * to = NULL); void show(SDL_Surface * to = NULL);

View File

@ -0,0 +1,80 @@
0 1
2 3
4 5
6 7
8 9
10 11
12 13
14 15
16 17
18 19
20 21
22 23
24 25
26 27
28 29
30 31
32 33
34 35
36 37
38 39
40 41
42 43
44 45
46 47
48 49
50 51
52 53
54 55
56 57
58 59
60 61
62 63
64 65
66 67
68 69
70 71
72 73
74 75
76 77
78 79
80 81
82 83
84 85
86 87
88 89
90 91
92 93
94 95
96 97
98 99
100 101
102 103
104 105
106 107
108 109
110 111
118 119
120 121
112 127
113 125
114 129
115 123
130 131
2 137
3 137
18 137
19 137
34 136
35 136
8 136
9 136
13 150
27 151
41 152
55 153
69 154
83 155
97 156
111 157
131 15

View File

@ -113,12 +113,17 @@
110 Hydra 110 Hydra
111 ChaosHydra 111 ChaosHydra
112 AirElemental 112 AirElemental
112 AirElementals
113 EarthElemental 113 EarthElemental
113 EarthElementals
114 FireElemental 114 FireElemental
114 FireElementals
115 WaterElemental 115 WaterElemental
115 WaterElementals
116 GoldGolem 116 GoldGolem
117 DiamondGolem 117 DiamondGolem
118 Pixie 118 Pixie
118 Pixies
119 Sprite 119 Sprite
120 PsiElemental 120 PsiElemental
121 MagicElemental 121 MagicElemental

View File

@ -369,6 +369,16 @@ void CCreatureHandler::loadCreatures()
ifs.close(); ifs.close();
ifs.clear(); ifs.clear();
ifs.open("config/cr_upgrade_list.txt");
while(!ifs.eof())
{
int id, up;
ifs >> id >> up;
creatures[id].upgrades.insert(up);
}
ifs.close();
ifs.clear();
//loading 32x32px imgs //loading 32x32px imgs
CDefHandler *smi = CGI->spriteh->giveDef("CPRSMALL.DEF"); CDefHandler *smi = CGI->spriteh->giveDef("CPRSMALL.DEF");
smi->notFreeImgs = true; smi->notFreeImgs = true;

View File

@ -15,6 +15,7 @@ class CCreature
public: public:
std::string namePl, nameSing, nameRef; //name in singular and plural form; and reference name std::string namePl, nameSing, nameRef; //name in singular and plural form; and reference name
std::vector<int> cost; //cost[res_id] - amount of that resource std::vector<int> cost; //cost[res_id] - amount of that resource
std::set<int> upgrades; // IDs of creatures to which this creature can be upgraded
int fightValue, AIValue, growth, hordeGrowth, hitPoints, speed, attack, defence, shots, spells; int fightValue, AIValue, growth, hordeGrowth, hitPoints, speed, attack, defence, shots, spells;
int damageMin, damageMax; int damageMin, damageMax;
int ammMin, ammMax; int ammMin, ammMax;

View File

@ -116,6 +116,9 @@ void CHeroHandler::loadHeroes()
CGeneralTextHandler::loadToIt(pom,buf,it,4); CGeneralTextHandler::loadToIt(pom,buf,it,4);
nher->highStack[x] = atoi(pom.c_str()); nher->highStack[x] = atoi(pom.c_str());
CGeneralTextHandler::loadToIt(nher->refTypeStack[x],buf,it,(x==2) ? (3) : (4)); CGeneralTextHandler::loadToIt(nher->refTypeStack[x],buf,it,(x==2) ? (3) : (4));
int hlp = nher->refTypeStack[x].find_first_of(' ',0);
if(hlp>=0)
nher->refTypeStack[x].replace(hlp,1,"");
} }
nher->ID = heroes.size(); nher->ID = heroes.size();

View File

@ -349,6 +349,7 @@ CGHeroInstance::CGHeroInstance()
isStanding = true; isStanding = true;
moveDir = 4; moveDir = 4;
mana = 0; mana = 0;
visitedTown = NULL;
} }
CGHeroInstance::~CGHeroInstance() CGHeroInstance::~CGHeroInstance()

View File

@ -304,7 +304,7 @@ class CGObjectInstance
{ {
public: public:
int3 pos; //h3m pos int3 pos; //h3m pos
int ID, subID; //normal ID (this one from OH3 maps ;]) int ID, subID; //normal ID (this one from OH3 maps ;]) - eg. town=98; hero=34
int id;//number of object in CObjectHandler's vector int id;//number of object in CObjectHandler's vector
CGDefInfo * defInfo; CGDefInfo * defInfo;
CCPPObjectScript * state; CCPPObjectScript * state;
@ -350,14 +350,11 @@ public:
std::vector<int> primSkills; //0-attack, 1-defence, 2-spell power, 3-knowledge std::vector<int> primSkills; //0-attack, 1-defence, 2-spell power, 3-knowledge
std::vector<std::pair<int,int> > secSkills; //first - ID of skill, second - level of skill (0 - basic, 1 - adv., 2 - expert) std::vector<std::pair<int,int> > secSkills; //first - ID of skill, second - level of skill (0 - basic, 1 - adv., 2 - expert)
int movement; //remaining movement points int movement; //remaining movement points
bool inTownGarrison; // if hero is in town garrison bool inTownGarrison; // if hero is in town garrison
CGTownInstance * visitedTown; //set if hero is visiting town or in the town garrison
std::vector<CArtifact *> artifacts; //hero's artifacts from bag std::vector<CArtifact *> artifacts; //hero's artifacts from bag
//CArtifact * artHead, * artLRing, * artRRing, * artLHand,
// * artRhand, * artFeet, * artSpellBook, * artMach1,
// * artMach2, * artMach3, * artMach4, * artMisc1, * artMisc2,
// * artMisc3, * artMisc4, * artMisc5, * artTorso, * artNeck,
// * artShoulders; //working artifacts
std::vector<CArtifact *> artifWorn; // 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5 std::vector<CArtifact *> artifWorn; // 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
virtual bool isHero() const; virtual bool isHero() const;