mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Fix pthread_mutex_lock abort() in requestActionASAP impl
This commit is contained in:
parent
68cc860133
commit
fa8a282696
@ -822,7 +822,7 @@ void VCAI::makeTurnInternal()
|
|||||||
{
|
{
|
||||||
if (h->movement)
|
if (h->movement)
|
||||||
logAi->warnStream() << boost::format("hero %s has %d MP left") % h->name % h->movement;
|
logAi->warnStream() << boost::format("hero %s has %d MP left") % h->name % h->movement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(boost::thread_interrupted &e)
|
catch(boost::thread_interrupted &e)
|
||||||
{
|
{
|
||||||
@ -891,7 +891,7 @@ bool VCAI::canGetArmy (const CGHeroInstance * army, const CGHeroInstance * sourc
|
|||||||
|
|
||||||
|
|
||||||
const CArmedInstance *armies[] = {army, source};
|
const CArmedInstance *armies[] = {army, source};
|
||||||
|
|
||||||
//we calculate total strength for each creature type available in armies
|
//we calculate total strength for each creature type available in armies
|
||||||
std::map<const CCreature*, int> creToPower;
|
std::map<const CCreature*, int> creToPower;
|
||||||
for(auto armyPtr : armies)
|
for(auto armyPtr : armies)
|
||||||
@ -988,7 +988,7 @@ void VCAI::pickBestCreatures(const CArmedInstance * army, const CArmedInstance *
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
|
void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
|
||||||
{
|
{
|
||||||
auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void
|
auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void
|
||||||
{
|
{
|
||||||
bool changeMade = false;
|
bool changeMade = false;
|
||||||
@ -2059,7 +2059,7 @@ void VCAI::tryRealize(Goals::CollectRes & g)
|
|||||||
cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, i, g.resID, toGive);
|
cb->trade(obj, EMarketMode::RESOURCE_RESOURCE, i, g.resID, toGive);
|
||||||
if(cb->getResourceAmount(static_cast<Res::ERes>(g.resID)) >= g.value)
|
if(cb->getResourceAmount(static_cast<Res::ERes>(g.resID)) >= g.value)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw cannotFulfillGoalException("I cannot get needed resources by trade!");
|
throw cannotFulfillGoalException("I cannot get needed resources by trade!");
|
||||||
}
|
}
|
||||||
@ -2277,7 +2277,7 @@ Goals::TSubgoal VCAI::striveToGoalInternal(Goals::TSubgoal ultimateGoal, bool on
|
|||||||
completeGoal (goal);
|
completeGoal (goal);
|
||||||
//completed goal was main goal //TODO: find better condition
|
//completed goal was main goal //TODO: find better condition
|
||||||
if (ultimateGoal->fulfillsMe(goal) || maxGoals > searchDepth2)
|
if (ultimateGoal->fulfillsMe(goal) || maxGoals > searchDepth2)
|
||||||
return sptr(Goals::Invalid());
|
return sptr(Goals::Invalid());
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
@ -2530,7 +2530,7 @@ int3 VCAI::explorationDesperate(HeroPtr h)
|
|||||||
//logAi->debugStream() << "Looking for an another place for exploration...";
|
//logAi->debugStream() << "Looking for an another place for exploration...";
|
||||||
SectorMap sm(h);
|
SectorMap sm(h);
|
||||||
int radius = h->getSightRadious();
|
int radius = h->getSightRadious();
|
||||||
|
|
||||||
std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
|
std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow]
|
||||||
tiles.resize(radius);
|
tiles.resize(radius);
|
||||||
|
|
||||||
@ -2660,19 +2660,13 @@ void VCAI::finish()
|
|||||||
|
|
||||||
void VCAI::requestActionASAP(std::function<void()> whatToDo)
|
void VCAI::requestActionASAP(std::function<void()> whatToDo)
|
||||||
{
|
{
|
||||||
// static boost::mutex m;
|
boost::thread newThread([this, whatToDo]()
|
||||||
// boost::unique_lock<boost::mutex> mylock(m);
|
|
||||||
|
|
||||||
boost::barrier b(2);
|
|
||||||
boost::thread newThread([&b,this,whatToDo]()
|
|
||||||
{
|
{
|
||||||
setThreadName("VCAI::requestActionASAP::helper");
|
setThreadName("VCAI::requestActionASAP::whatToDo");
|
||||||
SET_GLOBAL_STATE(this);
|
SET_GLOBAL_STATE(this);
|
||||||
boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
|
boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
|
||||||
b.wait();
|
|
||||||
whatToDo();
|
whatToDo();
|
||||||
});
|
});
|
||||||
b.wait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCAI::lostHero(HeroPtr h)
|
void VCAI::lostHero(HeroPtr h)
|
||||||
@ -2867,8 +2861,8 @@ void AIStatus::heroVisit(const CGObjectInstance *obj, bool started)
|
|||||||
objectsBeingVisited.push_back(obj);
|
objectsBeingVisited.push_back(obj);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// There can be more than one object visited at the time (eg. hero visits Subterranean Gate
|
// There can be more than one object visited at the time (eg. hero visits Subterranean Gate
|
||||||
// causing visit to hero on the other side.
|
// causing visit to hero on the other side.
|
||||||
// However, we are guaranteed that start/end visit notification maintain stack order.
|
// However, we are guaranteed that start/end visit notification maintain stack order.
|
||||||
assert(!objectsBeingVisited.empty());
|
assert(!objectsBeingVisited.empty());
|
||||||
objectsBeingVisited.pop_back();
|
objectsBeingVisited.pop_back();
|
||||||
@ -2980,7 +2974,7 @@ void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp)
|
|||||||
s.embarkmentPoints.push_back(neighPos);
|
s.embarkmentPoints.push_back(neighPos);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(t->visitable)
|
if(t->visitable)
|
||||||
{
|
{
|
||||||
auto obj = t->visitableObjects.front();
|
auto obj = t->visitableObjects.front();
|
||||||
@ -3036,7 +3030,7 @@ bool isWeeklyRevisitable (const CGObjectInstance * obj)
|
|||||||
bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
||||||
{
|
{
|
||||||
switch (obj->ID)
|
switch (obj->ID)
|
||||||
{
|
{
|
||||||
case Obj::TOWN:
|
case Obj::TOWN:
|
||||||
case Obj::HERO: //never visit our heroes at random
|
case Obj::HERO: //never visit our heroes at random
|
||||||
return obj->tempOwner != h->tempOwner; //do not visit our towns at random
|
return obj->tempOwner != h->tempOwner; //do not visit our towns at random
|
||||||
@ -3087,7 +3081,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
|
|||||||
return canRecruitCreatures;
|
return canRecruitCreatures;
|
||||||
}
|
}
|
||||||
case Obj::HILL_FORT:
|
case Obj::HILL_FORT:
|
||||||
{
|
{
|
||||||
for (auto slot : h->Slots())
|
for (auto slot : h->Slots())
|
||||||
{
|
{
|
||||||
if (slot.second->type->upgrades.size())
|
if (slot.second->type->upgrades.size())
|
||||||
@ -3386,7 +3380,7 @@ void SectorMap::makeParentBFS(crint3 source)
|
|||||||
ui8 &sec = retreiveTile(curPos);
|
ui8 &sec = retreiveTile(curPos);
|
||||||
assert(sec == mySector); //consider only tiles from the same sector
|
assert(sec == mySector); //consider only tiles from the same sector
|
||||||
UNUSED(sec);
|
UNUSED(sec);
|
||||||
|
|
||||||
foreach_neighbour(curPos, [&](crint3 neighPos)
|
foreach_neighbour(curPos, [&](crint3 neighPos)
|
||||||
{
|
{
|
||||||
if(retreiveTile(neighPos) == mySector && !vstd::contains(parent, neighPos))
|
if(retreiveTile(neighPos) == mySector && !vstd::contains(parent, neighPos))
|
||||||
@ -3405,4 +3399,3 @@ unsigned char & SectorMap::retreiveTile(crint3 pos)
|
|||||||
{
|
{
|
||||||
return retreiveTileN(sector, pos);
|
return retreiveTileN(sector, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5688,7 +5688,7 @@ bool CGameHandler::isValidObject(const CGObjectInstance *obj) const
|
|||||||
|
|
||||||
bool CGameHandler::isBlockedByQueries(const CPack *pack, PlayerColor player)
|
bool CGameHandler::isBlockedByQueries(const CPack *pack, PlayerColor player)
|
||||||
{
|
{
|
||||||
if(dynamic_cast<const PlayerMessage*>(pack))
|
if(!strcmp(typeid(*pack).name(), typeid(PlayerMessage).name()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto query = queries.topQuery(player);
|
auto query = queries.topQuery(player);
|
||||||
@ -5826,7 +5826,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI
|
|||||||
auto warMachine = VLC->arth->creatureToMachineID(st->type->idNumber);
|
auto warMachine = VLC->arth->creatureToMachineID(st->type->idNumber);
|
||||||
if (warMachine != ArtifactID::NONE)
|
if (warMachine != ArtifactID::NONE)
|
||||||
{
|
{
|
||||||
auto hero = dynamic_cast<const CGHeroInstance*> (army);
|
auto hero = dynamic_ptr_cast<CGHeroInstance> (army);
|
||||||
if (hero)
|
if (hero)
|
||||||
removedWarMachines.push_back (ArtifactLocation(hero, hero->getArtPos(warMachine, true)));
|
removedWarMachines.push_back (ArtifactLocation(hero, hero->getArtPos(warMachine, true)));
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,8 @@ void CPregameServer::handleConnection(CConnection *cpc)
|
|||||||
logNetwork->infoStream() << "Got package to announce " << typeid(*cpfs).name() << " from " << *cpc;
|
logNetwork->infoStream() << "Got package to announce " << typeid(*cpfs).name() << " from " << *cpc;
|
||||||
|
|
||||||
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
|
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
|
||||||
bool quitting = dynamic_cast<QuitMenuWithoutStarting*>(cpfs),
|
bool quitting = dynamic_ptr_cast<QuitMenuWithoutStarting>(cpfs),
|
||||||
startingGame = dynamic_cast<StartWithCurrentSettings*>(cpfs);
|
startingGame = dynamic_ptr_cast<StartWithCurrentSettings>(cpfs);
|
||||||
if(quitting || startingGame) //host leaves main menu or wants to start game -> we end
|
if(quitting || startingGame) //host leaves main menu or wants to start game -> we end
|
||||||
{
|
{
|
||||||
cpc->receivedStop = true;
|
cpc->receivedStop = true;
|
||||||
@ -258,11 +258,11 @@ void CPregameServer::sendPack(CConnection * pc, const CPackForSelectionScreen &
|
|||||||
*pc << &pack;
|
*pc << &pack;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dynamic_cast<const QuitMenuWithoutStarting*>(&pack))
|
if(dynamic_ptr_cast<QuitMenuWithoutStarting>(&pack))
|
||||||
{
|
{
|
||||||
pc->sendStop = true;
|
pc->sendStop = true;
|
||||||
}
|
}
|
||||||
else if(dynamic_cast<const StartWithCurrentSettings*>(&pack))
|
else if(dynamic_ptr_cast<StartWithCurrentSettings>(&pack))
|
||||||
{
|
{
|
||||||
pc->sendStop = true;
|
pc->sendStop = true;
|
||||||
}
|
}
|
||||||
@ -270,25 +270,25 @@ void CPregameServer::sendPack(CConnection * pc, const CPackForSelectionScreen &
|
|||||||
|
|
||||||
void CPregameServer::processPack(CPackForSelectionScreen * pack)
|
void CPregameServer::processPack(CPackForSelectionScreen * pack)
|
||||||
{
|
{
|
||||||
if(dynamic_cast<CPregamePackToHost*>(pack))
|
if(dynamic_ptr_cast<CPregamePackToHost>(pack))
|
||||||
{
|
{
|
||||||
sendPack(host, *pack);
|
sendPack(host, *pack);
|
||||||
}
|
}
|
||||||
else if(SelectMap *sm = dynamic_cast<SelectMap*>(pack))
|
else if(SelectMap *sm = dynamic_ptr_cast<SelectMap>(pack))
|
||||||
{
|
{
|
||||||
vstd::clear_pointer(curmap);
|
vstd::clear_pointer(curmap);
|
||||||
curmap = sm->mapInfo;
|
curmap = sm->mapInfo;
|
||||||
sm->free = false;
|
sm->free = false;
|
||||||
announcePack(*pack);
|
announcePack(*pack);
|
||||||
}
|
}
|
||||||
else if(UpdateStartOptions *uso = dynamic_cast<UpdateStartOptions*>(pack))
|
else if(UpdateStartOptions *uso = dynamic_ptr_cast<UpdateStartOptions>(pack))
|
||||||
{
|
{
|
||||||
vstd::clear_pointer(curStartInfo);
|
vstd::clear_pointer(curStartInfo);
|
||||||
curStartInfo = uso->options;
|
curStartInfo = uso->options;
|
||||||
uso->free = false;
|
uso->free = false;
|
||||||
announcePack(*pack);
|
announcePack(*pack);
|
||||||
}
|
}
|
||||||
else if(dynamic_cast<const StartWithCurrentSettings*>(pack))
|
else if(dynamic_ptr_cast<StartWithCurrentSettings>(pack))
|
||||||
{
|
{
|
||||||
state = ENDING_AND_STARTING_GAME;
|
state = ENDING_AND_STARTING_GAME;
|
||||||
announcePack(*pack);
|
announcePack(*pack);
|
||||||
@ -307,7 +307,7 @@ void CPregameServer::initConnection(CConnection *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPregameServer::startListeningThread(CConnection * pc)
|
void CPregameServer::startListeningThread(CConnection * pc)
|
||||||
{
|
{
|
||||||
listeningThreads++;
|
listeningThreads++;
|
||||||
pc->enterPregameConnectionMode();
|
pc->enterPregameConnectionMode();
|
||||||
pc->handler = new boost::thread(&CPregameServer::handleConnection, this, pc);
|
pc->handler = new boost::thread(&CPregameServer::handleConnection, this, pc);
|
||||||
@ -355,7 +355,7 @@ void CVCMIServer::newGame()
|
|||||||
{
|
{
|
||||||
CConnection &c = *firstConnection;
|
CConnection &c = *firstConnection;
|
||||||
ui8 clients;
|
ui8 clients;
|
||||||
c >> clients; //how many clients should be connected
|
c >> clients; //how many clients should be connected
|
||||||
assert(clients == 1); //multi goes now by newPregame, TODO: custom lobbies
|
assert(clients == 1); //multi goes now by newPregame, TODO: custom lobbies
|
||||||
|
|
||||||
CGameHandler *gh = initGhFromHostingConnection(c);
|
CGameHandler *gh = initGhFromHostingConnection(c);
|
||||||
@ -469,14 +469,14 @@ void CVCMIServer::loadGame()
|
|||||||
// char sig[8];
|
// char sig[8];
|
||||||
// CMapHeader dum;
|
// CMapHeader dum;
|
||||||
// StartInfo *si;
|
// StartInfo *si;
|
||||||
//
|
//
|
||||||
// CLoadFile lf(CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
|
// CLoadFile lf(CResourceHandler::get("local")->getResourceName(ResourceID(fname, EResType::LIB_SAVEGAME)));
|
||||||
// lf >> sig >> dum >> si;
|
// lf >> sig >> dum >> si;
|
||||||
// logNetwork->infoStream() <<"Reading save signature";
|
// logNetwork->infoStream() <<"Reading save signature";
|
||||||
//
|
//
|
||||||
// lf >> *VLC;
|
// lf >> *VLC;
|
||||||
// logNetwork->infoStream() <<"Reading handlers";
|
// logNetwork->infoStream() <<"Reading handlers";
|
||||||
//
|
//
|
||||||
// lf >> (gh.gs);
|
// lf >> (gh.gs);
|
||||||
// c.addStdVecItems(gh.gs);
|
// c.addStdVecItems(gh.gs);
|
||||||
// logNetwork->infoStream() <<"Reading gamestate";
|
// logNetwork->infoStream() <<"Reading gamestate";
|
||||||
@ -493,7 +493,7 @@ void CVCMIServer::loadGame()
|
|||||||
CConnection* cc; //tcp::socket * ss;
|
CConnection* cc; //tcp::socket * ss;
|
||||||
for(int i=0; i<clients; i++)
|
for(int i=0; i<clients; i++)
|
||||||
{
|
{
|
||||||
if(!i)
|
if(!i)
|
||||||
{
|
{
|
||||||
cc = &c;
|
cc = &c;
|
||||||
}
|
}
|
||||||
@ -508,7 +508,7 @@ void CVCMIServer::loadGame()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cc = new CConnection(s,NAME);
|
cc = new CConnection(s,NAME);
|
||||||
}
|
}
|
||||||
gh.conns.insert(cc);
|
gh.conns.insert(cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -531,7 +531,7 @@ static void handleCommandOptions(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
po::store(po::parse_command_line(argc, argv, opts), cmdLineOptions);
|
po::store(po::parse_command_line(argc, argv, opts), cmdLineOptions);
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
|
std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ bool MoveHero::applyGh( CGameHandler *gh )
|
|||||||
bool CastleTeleportHero::applyGh( CGameHandler *gh )
|
bool CastleTeleportHero::applyGh( CGameHandler *gh )
|
||||||
{
|
{
|
||||||
ERROR_IF_NOT_OWNS(hid);
|
ERROR_IF_NOT_OWNS(hid);
|
||||||
|
|
||||||
return gh->teleportHero(hid,dest,source,gh->getPlayerAt(c));
|
return gh->teleportHero(hid,dest,source,gh->getPlayerAt(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ bool GarrisonHeroSwap::applyGh( CGameHandler *gh )
|
|||||||
{
|
{
|
||||||
const CGTownInstance * town = gh->getTown(tid);
|
const CGTownInstance * town = gh->getTown(tid);
|
||||||
if (!PLAYER_OWNS(tid) && !( town->garrisonHero && PLAYER_OWNS(town->garrisonHero->id) ) )
|
if (!PLAYER_OWNS(tid) && !( town->garrisonHero && PLAYER_OWNS(town->garrisonHero->id) ) )
|
||||||
ERROR_AND_RETURN;//neither town nor garrisoned hero (if present) is ours
|
ERROR_AND_RETURN;//neither town nor garrisoned hero (if present) is ours
|
||||||
return gh->garrisonSwap(tid);
|
return gh->garrisonSwap(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ bool TradeOnMarketplace::applyGh( CGameHandler *gh )
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SetFormation::applyGh( CGameHandler *gh )
|
bool SetFormation::applyGh( CGameHandler *gh )
|
||||||
{
|
{
|
||||||
ERROR_IF_NOT_OWNS(hid);
|
ERROR_IF_NOT_OWNS(hid);
|
||||||
return gh->setFormation(hid,formation);
|
return gh->setFormation(hid,formation);
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ bool SetFormation::applyGh( CGameHandler *gh )
|
|||||||
bool HireHero::applyGh( CGameHandler *gh )
|
bool HireHero::applyGh( CGameHandler *gh )
|
||||||
{
|
{
|
||||||
const CGObjectInstance *obj = gh->getObj(tid);
|
const CGObjectInstance *obj = gh->getObj(tid);
|
||||||
const CGTownInstance *town = dynamic_cast<const CGTownInstance *>(obj);
|
const CGTownInstance *town = dynamic_ptr_cast<CGTownInstance>(obj);
|
||||||
if(town && PlayerRelations::ENEMIES == gh->getPlayerRelations(obj->tempOwner, gh->getPlayerAt(c)))
|
if(town && PlayerRelations::ENEMIES == gh->getPlayerRelations(obj->tempOwner, gh->getPlayerAt(c)))
|
||||||
COMPLAIN_AND_RETURN("Can't buy hero in enemy town!");
|
COMPLAIN_AND_RETURN("Can't buy hero in enemy town!");
|
||||||
|
|
||||||
@ -240,16 +240,16 @@ bool MakeAction::applyGh( CGameHandler *gh )
|
|||||||
{
|
{
|
||||||
const BattleInfo *b = GS(gh)->curB;
|
const BattleInfo *b = GS(gh)->curB;
|
||||||
if(!b) ERROR_AND_RETURN;
|
if(!b) ERROR_AND_RETURN;
|
||||||
|
|
||||||
if(b->tacticDistance)
|
if(b->tacticDistance)
|
||||||
{
|
{
|
||||||
if(ba.actionType != Battle::WALK && ba.actionType != Battle::END_TACTIC_PHASE
|
if(ba.actionType != Battle::WALK && ba.actionType != Battle::END_TACTIC_PHASE
|
||||||
&& ba.actionType != Battle::RETREAT && ba.actionType != Battle::SURRENDER)
|
&& ba.actionType != Battle::RETREAT && ba.actionType != Battle::SURRENDER)
|
||||||
ERROR_AND_RETURN;
|
ERROR_AND_RETURN;
|
||||||
if(gh->connections[b->sides[b->tacticsSide].color] != c)
|
if(gh->connections[b->sides[b->tacticsSide].color] != c)
|
||||||
ERROR_AND_RETURN;
|
ERROR_AND_RETURN;
|
||||||
}
|
}
|
||||||
else if(gh->connections[b->battleGetStackByID(b->activeStack)->owner] != c)
|
else if(gh->connections[b->battleGetStackByID(b->activeStack)->owner] != c)
|
||||||
ERROR_AND_RETURN;
|
ERROR_AND_RETURN;
|
||||||
|
|
||||||
return gh->makeBattleAction(ba);
|
return gh->makeBattleAction(ba);
|
||||||
|
@ -22,3 +22,17 @@ inline const T * dynamic_ptr_cast(const F * ptr)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T, class F>
|
||||||
|
inline T * dynamic_ptr_cast(F * ptr)
|
||||||
|
{
|
||||||
|
#ifndef __APPLE__
|
||||||
|
return dynamic_cast<T*>(ptr);
|
||||||
|
#else
|
||||||
|
if (!strcmp(typeid(*ptr).name(), typeid(T).name()))
|
||||||
|
{
|
||||||
|
return static_cast<T*>(ptr);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user