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

* Started making support for garrison window. Leaving guardians in flagged mines.

This commit is contained in:
Michał W. Urbańczyk 2009-04-12 00:58:41 +00:00
parent d72d988a9c
commit f9aebcc4bd
20 changed files with 259 additions and 48 deletions

View File

@ -64,6 +64,11 @@ void CGeniusAI::heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector
callback(rand() % skills.size());
}
void GeniusAI::CGeniusAI::showGarrisonDialog( const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd )
{
onEnd();
}
void CGeniusAI::showBlockingDialog( const std::string &text, const std::vector<Component> &components, ui32 askID, bool selection, bool cancel )
{
m_cb->selectionMade(cancel ? 0 : 1, askID);

View File

@ -194,6 +194,7 @@ public:
virtual void tileRevealed(int3 pos){};
virtual void tileHidden(int3 pos){};
virtual void heroGotLevel(const CGHeroInstance *hero, int pskill, std::vector<ui16> &skills, boost::function<void(ui32)> &callback);
virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd);
// battle
virtual void actionFinished(const BattleAction *action);//occurs AFTER every action taken by any stack or by the hero
virtual void actionStarted(const BattleAction *action);//occurs BEFORE every action taken by any stack or by the hero

View File

@ -1364,15 +1364,17 @@ void CAdvMapInt::fendTurn()
void CAdvMapInt::activate()
{
if(active++)
{
tlog1 << "Error: advmapint already active...\n";
}
if(subInt == heroWindow)
{
heroWindow->activate();
return;
}
if(active++)
{
tlog1 << "Error: advmapint already active...\n";
active--;
return;
}
LOCPLINT->curint = this;
LOCPLINT->statusbar = &statusbar;
kingOverview.activate();
@ -1397,10 +1399,6 @@ void CAdvMapInt::activate()
}
void CAdvMapInt::deactivate()
{
if(--active)
{
tlog1 << "Error: advmapint still active...\n";
}
if(subInt == heroWindow)
{
heroWindow->deactivate();
@ -1410,6 +1408,12 @@ void CAdvMapInt::deactivate()
hide();
LOCPLINT->cingconsole->deactivate();
if(--active)
{
tlog1 << "Error: advmapint still active...\n";
deactivate();
}
}
void CAdvMapInt::show(SDL_Surface *to)
{

View File

@ -324,16 +324,14 @@ int CCallback::getHeroSerial(const CGHeroInstance * hero) const
const CCreatureSet* CCallback::getGarrison(const CGObjectInstance *obj) const
{
boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
if(!obj)
const CArmedInstance *armi = dynamic_cast<const CArmedInstance*>(obj);
if(!armi)
return NULL;
if(obj->ID == HEROI_TYPE)
return &(dynamic_cast<const CGHeroInstance*>(obj))->army;
else if(obj->ID == TOWNI_TYPE)
return &(dynamic_cast<const CGTownInstance*>(obj)->army);
else return NULL;
else
return &armi->army;
}
int CCallback::swapCreatures(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2)
int CCallback::swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
{
if(s1->tempOwner != player || s2->tempOwner != player)
return -1;
@ -343,8 +341,8 @@ int CCallback::swapCreatures(const CGObjectInstance *s1, const CGObjectInstance
return 0;
}
int CCallback::mergeStacks(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2)
{
int CCallback::mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)
{
if ((s1->tempOwner!= player || s2->tempOwner!=player))
{
return -1;
@ -353,7 +351,7 @@ int CCallback::mergeStacks(const CGObjectInstance *s1, const CGObjectInstance *s
*cl->serv << &pack;
return 0;
}
int CCallback::splitStack(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2, int val)
int CCallback::splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val)
{
if (s1->tempOwner!= player || s2->tempOwner!=player || (!val))
{

View File

@ -33,9 +33,9 @@ class ICallback
public:
virtual bool moveHero(const CGHeroInstance *h, int3 dst) const =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
virtual void selectionMade(int selection, int asker) =0;
virtual int swapCreatures(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2)=0;//swaps creatures between two posiibly different garrisons // TODO: AI-unsafe code - fix it!
virtual int mergeStacks(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2)=0;//joins first stack tothe second (creatures must be same type)
virtual int splitStack(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2, int val)=0;//split creatures from the first stack
virtual int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)=0;//swaps creatures between two posiibly different garrisons // TODO: AI-unsafe code - fix it!
virtual int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2)=0;//joins first stack tothe second (creatures must be same type)
virtual int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val)=0;//split creatures from the first stack
virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses diven hero; true - successfuly, false - not successfuly
virtual bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2)=0; //swaps artifacts between two given heroes
virtual void recruitCreatures(const CGObjectInstance *obj, ui32 ID, ui32 amount)=0;
@ -123,9 +123,9 @@ public:
//commands
bool moveHero(const CGHeroInstance *h, int3 dst) const; //dst must be free, neighbouring tile (this function can move hero only by one tile)
void selectionMade(int selection, int asker);
int swapCreatures(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2);
int mergeStacks(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2); //first goes to the second
int splitStack(const CGObjectInstance *s1, const CGObjectInstance *s2, int p1, int p2, int val);
int swapCreatures(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2);
int mergeStacks(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2); //first goes to the second
int splitStack(const CArmedInstance *s1, const CArmedInstance *s2, int p1, int p2, int val);
bool dismissHero(const CGHeroInstance * hero);
bool swapArtifacts(const CGHeroInstance * hero1, ui16 pos1, const CGHeroInstance * hero2, ui16 pos2);
bool buildBuilding(const CGTownInstance *town, si32 buildingID);

View File

@ -72,6 +72,7 @@ public:
//virtual void showSelDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID){};
//virtual void showYesNoDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID){};
virtual void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, 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, boost::function<void()> &onEnd) = 0; //all stacks operations between these objects become allowed, interface has to call onEnd when done
virtual void tileHidden(const std::set<int3> &pos){};
virtual void tileRevealed(const std::set<int3> &pos){};
virtual void yourTurn(){};

View File

@ -232,7 +232,7 @@ void CHeroWindow::setHero(const CGHeroInstance *Hero)
portraitArea->text = hero->getBiography();
delete garInt;
/*gar4button->owner = */garInt = new CGarrisonInt(pos.x+80, pos.y+493, 8, 0, curBack, 13, 482, curHero);
/*gar4button->owner = */garInt = new CGarrisonInt(pos.x+80, pos.y+493, 8, 0, curBack, 15, 485, curHero);
garInt->update = false;
gar4button->callback = boost::bind(&CGarrisonInt::splitClick,garInt);//actualization of callback function

View File

@ -2117,11 +2117,6 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
hw->garInt->recreateSlots();
hw->garInt->show();
}
if(castleInt) //opened town window - redraw town garrison slots (change is within hero garr)
{
castleInt->garr->highlighted = NULL;
castleInt->garr->recreateSlots();
}
}
else if (obj->ID == TOWNI_TYPE) //town
@ -2137,12 +2132,24 @@ void CPlayerInterface::garrisonChanged(const CGObjectInstance * obj)
SDL_FreeSurface(graphics->heroWins[tt->visitingHero->subID]);
graphics->heroWins[tt->visitingHero->subID] = infoWin(tt->visitingHero);
}
if(LOCPLINT->castleInt)
}
if(castleInt) //opened town window - redraw town garrison slots (change is within hero garr)
{
castleInt->garr->highlighted = NULL;
castleInt->garr->recreateSlots();
}
else if(curint && curint->subInt)
{
CGarrisonWindow *gw = dynamic_cast<CGarrisonWindow*>(curint->subInt);
if(gw)
{
LOCPLINT->castleInt->garr->highlighted = NULL;
LOCPLINT->castleInt->garr->recreateSlots();
gw->garr->recreateSlots();
gw->garr->show();
}
}
if(curint == adventureInt)
adventureInt->infoBar.draw();
}
@ -2613,6 +2620,16 @@ bool CPlayerInterface::shiftPressed() const
return SDL_GetKeyState(NULL)[SDLK_LSHIFT] || SDL_GetKeyState(NULL)[SDLK_RSHIFT];
}
void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHeroInstance *down, boost::function<void()> &onEnd )
{
CGarrisonWindow *cgw = new CGarrisonWindow(up,down);
cgw->quit->callback += onEnd;
curint->deactivate();
cgw->activate();
objsToBlit += cgw;
LOCPLINT->curint->subInt = cgw;
}
CStatusBar::CStatusBar(int x, int y, std::string name, int maxw)
{
bg=BitmapHandler::loadBitmap(name);
@ -3791,7 +3808,7 @@ void CCreInfoWindow::activate()
void CCreInfoWindow::close()
{
deactivate();
if(dynamic_cast<CHeroWindow*>(LOCPLINT->curint->subInt))
if(LOCPLINT->curint->subInt)
{
if(type)
LOCPLINT->curint->subInt->activate();
@ -4791,3 +4808,57 @@ CInGameConsole::CInGameConsole() : defaultTimeout(10000), maxDisplayedTexts(10),
{
}
void CGarrisonWindow::close()
{
LOCPLINT->curint->subInt = NULL;
deactivate();
LOCPLINT->curint->activate();
LOCPLINT->objsToBlit -= this;
delete this;
}
void CGarrisonWindow::activate()
{
split->activate();
quit->activate();
garr->activate();
}
void CGarrisonWindow::deactivate()
{
split->deactivate();
quit->deactivate();
garr->deactivate();
}
void CGarrisonWindow::show(SDL_Surface * to)
{
blitAt(bg,pos);
split->show();
quit->show();
garr->show();
}
CGarrisonWindow::CGarrisonWindow( const CArmedInstance *up, const CGHeroInstance *down )
{
bg = BitmapHandler::loadBitmap("GARRISON.bmp");
SDL_SetColorKey(bg,SDL_SRCCOLORKEY,SDL_MapRGB(bg->format,0,255,255));
graphics->blueToPlayersAdv(bg,LOCPLINT->playerID);
pos.x = screen->w/2 - bg->w/2;
pos.y = screen->h/2 - bg->h/2;
pos.w = screen->w;
pos.h = screen->h;
garr = new CGarrisonInt(pos.x+92, pos.y+129, 4, 30, bg, 92, 126, up, down);
split = new AdventureMapButton(CGI->generaltexth->tcommands[3],"",boost::bind(&CGarrisonInt::splitClick,garr),pos.x+88,pos.y+314,"IDV6432.DEF");
quit = new AdventureMapButton(CGI->generaltexth->tcommands[8],"",boost::bind(&CGarrisonWindow::close,this),pos.x+399,pos.y+314,"IOK6432.DEF",SDLK_RETURN);
}
CGarrisonWindow::~CGarrisonWindow()
{
SDL_FreeSurface(bg);
delete split;
delete quit;
delete garr;
}

View File

@ -548,6 +548,7 @@ public:
//void showSelDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID);
//void showYesNoDialog(const std::string &text, const std::vector<Component*> &components, ui32 askID);
void showBlockingDialog(const std::string &text, const std::vector<Component> &components, ui32 askID, 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, boost::function<void()> &onEnd);
void tileHidden(const std::set<int3> &pos);
void tileRevealed(const std::set<int3> &pos);
void yourTurn();
@ -927,6 +928,21 @@ public:
CInGameConsole(); //c-tor
};
class CGarrisonWindow : public IShowActivable, public CIntObject
{
public:
CGarrisonInt *garr;
SDL_Surface *bg;
AdventureMapButton *split, *quit;
void close();
void activate();
void deactivate();
void show(SDL_Surface * to = NULL);
CGarrisonWindow(const CArmedInstance *up, const CGHeroInstance *down);
~CGarrisonWindow();
};
extern CPlayerInterface * LOCPLINT;

View File

@ -79,6 +79,7 @@ public:
void showInfoDialog(InfoWindow *iw){};
void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback){};
ui32 showBlockingDialog(BlockingDialog *iw){return 0;}; //synchronous version of above
void showGarrisonDialog(int upobj, int hid, const boost::function<void()> &cb){};
void giveResource(int player, int which, int val){};
void showCompInfo(ShowInInfobox * comp){};
void heroVisitCastle(int obj, int heroID){};

View File

@ -291,6 +291,18 @@ void BlockingDialog::applyCl( CClient *cl )
tlog2 << "We received YesNoDialog for not our player...\n";
}
void GarrisonDialog::applyCl(CClient *cl)
{
const CGHeroInstance *h = cl->getHero(hid);
const CArmedInstance *obj = static_cast<const CArmedInstance*>(cl->getObj(objid));
if(!vstd::contains(cl->playerint,h->getOwner()))
return;
boost::function<void()> callback = boost::bind(&CCallback::selectionMade,LOCPLINT->cb,0,id);
cl->playerint[h->getOwner()]->showGarrisonDialog(obj,h,callback);
}
void BattleStart::applyCl( CClient *cl )
{
if(vstd::contains(cl->playerint,info->side1))

View File

@ -119,7 +119,7 @@ void CHeroHandler::loadObstacles()
inp.open("config" PATHSEPARATOR "obstacles.txt", std::ios_base::in|std::ios_base::binary);
if(!inp.is_open())
{
tlog1<<"missing file: config/heroes_sec_skills.txt"<<std::endl;
tlog1<<"missing file: config/obstacles.txt"<<std::endl;
}
else
{

View File

@ -1292,7 +1292,10 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const
return;
if(h->tempOwner == tempOwner) //we're visiting our mine
return; //TODO: leaving garrison
{
cb->showGarrisonDialog(id,h->id,0);
return;
}
//TODO: check if mine is guarded
cb->setOwner(id,h->tempOwner); //not ours? flag it!

View File

@ -599,6 +599,7 @@ public:
class DLL_EXPORT CGMine : public CArmedInstance
{
public:
void offerLeavingGuards(const CGHeroInstance *h) const;
void onHeroVisit(const CGHeroInstance * h) const;
void newTurn() const;
void initObj();

16
int3.h
View File

@ -11,9 +11,9 @@ class CCreatureSet //seven combined creatures
public:
std::map<si32,std::pair<ui32,si32> > slots; //slots[slot_id]=> pair(creature_id,creature_quantity)
bool formation; //false - wide, true - tight
si32 getSlotFor(ui32 creature, ui32 slotsAmount=7) //returns -1 if no slot available
si32 getSlotFor(ui32 creature, ui32 slotsAmount=7) const //returns -1 if no slot available
{
for(std::map<si32,std::pair<ui32,si32> >::iterator i=slots.begin(); i!=slots.end(); ++i)
for(std::map<si32,std::pair<ui32,si32> >::const_iterator i=slots.begin(); i!=slots.end(); ++i)
{
if(i->second.first == creature)
{
@ -37,6 +37,18 @@ public:
{
return slots.size()>0;
}
void sweep()
{
for(std::map<si32,std::pair<ui32,si32> >::iterator i=slots.begin(); i!=slots.end(); ++i)
{
if(!i->second.second)
{
slots.erase(i);
sweep();
break;
}
}
}
};
class int3

View File

@ -52,7 +52,8 @@ public:
virtual void changeSecSkill(int ID, int which, int val, bool abs=false)=0;
virtual void showInfoDialog(InfoWindow *iw)=0;
virtual void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback)=0;
virtual ui32 showBlockingDialog(BlockingDialog *iw) =0; //synchronous version of above
virtual ui32 showBlockingDialog(BlockingDialog *iw) =0; //synchronous version of above //TODO:
virtual void showGarrisonDialog(int upobj, int hid, const boost::function<void()> &cb) =0; //cb will be called when player closes garrison window
virtual void giveResource(int player, int which, int val)=0;
virtual void showCompInfo(ShowInInfobox * comp)=0;
virtual void heroVisitCastle(int obj, int heroID)=0;

View File

@ -595,6 +595,18 @@ struct BlockingDialog : public Query//2003
}
};
struct GarrisonDialog : public Query//2004
{
GarrisonDialog(){type = 2004;}
void applyCl(CClient *cl);
si32 objid, hid;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & id & objid & hid;
}
};
struct BattleInfo;
struct BattleStart : public CPackForClient//3000
{

View File

@ -68,6 +68,7 @@ void registerTypes2(Serializer &s)
s.template registerType<SetHoverName>();
s.template registerType<HeroLevelUp>();
s.template registerType<BlockingDialog>();
s.template registerType<GarrisonDialog>();
s.template registerType<BattleStart>();
s.template registerType<BattleNextRound>();
s.template registerType<BattleSetActiveStack>();

View File

@ -38,13 +38,9 @@ extern bool end2;
bnr.round = gs->curB->round + 1;\
sendAndApply(&bnr);
boost::mutex gsm;
ui32 CGameHandler::QID = 1;
CondSh<bool> battleMadeAction;
CondSh<BattleResult *> battleResult(NULL);
std::map<ui32, CFunctionList<void(ui32)> > callbacks; //question id => callback function - for selection dialogs
class CBaseForGHApply
{
@ -581,6 +577,7 @@ void CGameHandler::moveStack(int stack, int dest)
}
CGameHandler::CGameHandler(void)
{
QID = 1;
gs = NULL;
IObjectInterface::cb = this;
applier = new CGHApplier;
@ -1466,6 +1463,12 @@ void CGameHandler::arrangeStacks(si32 id1, si32 id2, ui8 what, ui8 p1, ui8 p2, s
CCreatureSet temp1 = s1->army, temp2 = s2->army,
&S1 = temp1, &S2 = (s1!=s2)?(temp2):(temp1);
if(!isAllowedExchange(id1,id2))
{
complain("Cannot exchange stacks between these two objects!\n");
return;
}
if(what==1) //swap
{
std::swap(S1.slots[p1],S2.slots[p2]); //swap slots
@ -1925,10 +1928,25 @@ void CGameHandler::hireHero( ui32 tid, ui8 hid )
void CGameHandler::queryReply( ui32 qid, ui32 answer )
{
gsm.lock();
CFunctionList<void(ui32)> callb = callbacks[qid];
callbacks.erase(qid);
if(vstd::contains(callbacks,qid))
{
CFunctionList<void(ui32)> callb = callbacks[qid];
callbacks.erase(qid);
if(callb)
callb(answer);
}
else if(vstd::contains(garrisonCallbacks,qid))
{
if(garrisonCallbacks[qid])
garrisonCallbacks[qid]();
garrisonCallbacks.erase(qid);
allowedExchanges.erase(qid);
}
else
{
tlog1 << "Unknown query reply...\n";
}
gsm.unlock();
callb(answer);
}
void CGameHandler::makeBattleAction( BattleAction &ba )
@ -2398,4 +2416,50 @@ ui32 CGameHandler::getQueryResult( ui8 player, int queryID )
{
//TODO: write
return 0;
}
void CGameHandler::showGarrisonDialog( int upobj, int hid, const boost::function<void()> &cb )
{
ui8 player = getOwner(hid);
GarrisonDialog gd;
gd.hid = hid;
gd.objid = upobj;
gsm.lock();
gd.id = QID;
garrisonCallbacks[QID] = cb;
allowedExchanges[QID] = std::pair<si32,si32>(upobj,hid);
states.addQuery(player,QID);
QID++;
sendAndApply(&gd);
gsm.unlock();
}
bool CGameHandler::isAllowedExchange( int id1, int id2 )
{
if(id1 == id2)
return true;
{
boost::unique_lock<boost::mutex> lock(gsm);
for(std::map<ui32, std::pair<si32,si32> >::const_iterator i = allowedExchanges.begin(); i!=allowedExchanges.end(); i++)
if(id1 == i->second.first && id2 == i->second.second || id2 == i->second.first && id1 == i->second.second)
return true;
}
const CGObjectInstance *o1 = getObj(id1), *o2 = getObj(id2);
if(o1->ID == TOWNI_TYPE)
{
const CGTownInstance *t = static_cast<const CGTownInstance*>(o1);
if(t->visitingHero == o2 || t->garrisonHero == o2)
return true;
}
if(o2->ID == TOWNI_TYPE)
{
const CGTownInstance *t = static_cast<const CGTownInstance*>(o2);
if(t->visitingHero == o1 || t->garrisonHero == o1)
return true;
}
return false;
}

View File

@ -58,12 +58,19 @@ public:
class CGameHandler : public IGameCallback
{
public:
static ui32 QID;
CVCMIServer *s;
std::map<int,CConnection*> connections; //player color -> connection to clinet with interface of that player
std::map<int,CConnection*> connections; //player color -> connection to client with interface of that player
PlayerStatuses states; //player color -> player state
std::set<CConnection*> conns;
//queries stuff
boost::mutex gsm;
ui32 QID;
std::map<ui32, CFunctionList<void(ui32)> > callbacks; //query id => callback function - for selection and yes/no dialogs
std::map<ui32, boost::function<void()> > garrisonCallbacks; //query id => callback - for garrison dialogs
std::map<ui32, std::pair<si32,si32> > allowedExchanges;
bool isAllowedExchange(int id1, int id2);
void giveSpells(const CGTownInstance *t, const CGHeroInstance *h);
void moveStack(int stack, int dest);
void startBattle(CCreatureSet army1, CCreatureSet army2, int3 tile, CGHeroInstance *hero1, CGHeroInstance *hero2, boost::function<void(BattleResult*)> cb); //use hero=NULL for no hero
@ -95,6 +102,7 @@ public:
void showInfoDialog(InfoWindow *iw);
void showBlockingDialog(BlockingDialog *iw, const CFunctionList<void(ui32)> &callback);
ui32 showBlockingDialog(BlockingDialog *iw); //synchronous version of above
void showGarrisonDialog(int upobj, int hid, const boost::function<void()> &cb);
void giveResource(int player, int which, int val);
void showCompInfo(ShowInInfobox * comp);
void heroVisitCastle(int obj, int heroID);