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

Quick Combat system setting works… if user doesn't click in the meantime.

This commit is contained in:
Michał W. Urbańczyk 2013-06-23 11:25:48 +00:00
parent 2a471eeb1a
commit 04d11519ef
8 changed files with 149 additions and 157 deletions

View File

@ -66,6 +66,15 @@
// They do not own any mutexes intiially.
#define THREAD_CREATED_BY_CLIENT
#define RETURN_IF_QUICK_COMBAT \
if(isAutoFightOn) \
return;
#define BATTLE_EVENT_POSSIBLE_RETURN\
if(LOCPLINT != this) \
return; \
RETURN_IF_QUICK_COMBAT
using namespace boost::assign;
using namespace CSDL_Ext;
@ -600,12 +609,19 @@ void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID build
void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
if(settings["adventure"]["quickCombat"].Bool())
{
autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
autofightingAI->init(cb);
autofightingAI->battleStart(army1, army2, int3(0,0,0), hero1, hero2, side);
isAutoFightOn = true;
cb->registerBattleInterface(autofightingAI);
}
waitForAllDialogs();
BATTLE_EVENT_POSSIBLE_RETURN;
GH.pushInt(battleInt);
}
@ -613,10 +629,7 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
for(int b=0; b<healedStacks.size(); ++b)
{
@ -663,10 +676,7 @@ void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, u
void CPlayerInterface::battleNewStackAppeared(const CStack * stack)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->newStack(stack);
}
@ -674,10 +684,7 @@ void CPlayerInterface::battleNewStackAppeared(const CStack * stack)
void CPlayerInterface::battleObstaclesRemoved(const std::set<si32> & removedObstacles)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
// for(std::set<si32>::const_iterator it = removedObstacles.begin(); it != removedObstacles.end(); ++it)
// {
@ -697,10 +704,7 @@ void CPlayerInterface::battleObstaclesRemoved(const std::set<si32> & removedObst
void CPlayerInterface::battleCatapultAttacked(const CatapultAttack & ca)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->stackIsCatapulting(ca);
}
@ -708,10 +712,7 @@ void CPlayerInterface::battleCatapultAttacked(const CatapultAttack & ca)
void CPlayerInterface::battleStacksRemoved(const BattleStacksRemoved & bsr)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
for(std::set<ui32>::const_iterator it = bsr.stackIDs.begin(); it != bsr.stackIDs.end(); ++it) //for each removed stack
{
@ -723,10 +724,7 @@ void CPlayerInterface::battleStacksRemoved(const BattleStacksRemoved & bsr)
void CPlayerInterface::battleNewRound(int round) //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->newRound(round);
}
@ -734,10 +732,7 @@ void CPlayerInterface::battleNewRound(int round) //called at the beginning of ea
void CPlayerInterface::actionStarted(const BattleAction &action)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
curAction = new BattleAction(action);
battleInt->startAction(curAction);
@ -746,10 +741,7 @@ void CPlayerInterface::actionStarted(const BattleAction &action)
void CPlayerInterface::actionFinished(const BattleAction &action)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->endAction(curAction);
delete curAction;
@ -760,6 +752,23 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
{
THREAD_CREATED_BY_CLIENT;
logGlobal->traceStream() << "Awaiting command for " << stack->nodeName();
if(autofightingAI)
{
if(isAutoFightOn)
{
assert(autofightingAI);
auto ret = autofightingAI->activeStack(stack);
if(isAutoFightOn)
{
return ret;
}
}
cb->unregisterBattleInterface(autofightingAI);
autofightingAI = nullptr;
}
CBattleInterface *b = battleInt;
assert(!b->givenCommand->get()); //command buffer must be clean (we don't want to use old command)
@ -790,56 +799,56 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
void CPlayerInterface::battleEnd(const BattleResult *br)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
if(isAutoFightOn)
{
isAutoFightOn = false;
cb->unregisterBattleInterface(autofightingAI);
autofightingAI = nullptr;
SDL_Rect temp_rect = genRect(561, 470, (screen->w - 800)/2 + 165, (screen->h - 600)/2 + 19);
auto resWindow = new CBattleResultWindow(*br, temp_rect, *this);
GH.pushInt(resWindow);
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->battleFinished(*br);
}
void CPlayerInterface::battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->stackMoved(stack, dest, distance);
}
void CPlayerInterface::battleSpellCast( const BattleSpellCast *sc )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->spellCast(sc);
}
void CPlayerInterface::battleStacksEffectsSet( const SetStackEffect & sse )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->battleStacksEffectsSet(sse);
}
void CPlayerInterface::battleTriggerEffect (const BattleTriggerEffect & bte)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
//TODO why is this different (no return on LOPLINT != this) ?
RETURN_IF_QUICK_COMBAT;
battleInt->battleTriggerEffect(bte);
}
void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
std::vector<StackAttackedInfo> arg;
for(std::vector<BattleStackAttacked>::const_iterator i = bsa.begin(); i != bsa.end(); i++)
@ -866,10 +875,7 @@ void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacke
void CPlayerInterface::battleAttack(const BattleAttack *ba)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
assert(curAction);
if(ba->lucky()) //lucky hit
@ -938,10 +944,7 @@ void CPlayerInterface::battleAttack(const BattleAttack *ba)
void CPlayerInterface::battleObstaclePlaced(const CObstacleInstance &obstacle)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->obstaclePlaced(obstacle);
}
@ -2236,10 +2239,7 @@ void CPlayerInterface::updateInfo(const CGObjectInstance * specific)
void CPlayerInterface::battleNewRoundFirst( int round )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(LOCPLINT != this)
{ //another local interface should do this
return;
}
BATTLE_EVENT_POSSIBLE_RETURN;
battleInt->newRoundFirst(round);
}

View File

@ -111,6 +111,11 @@ public:
std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in adventure map
std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
//During battle is quick combat mode is used
shared_ptr<CBattleGameInterface> autofightingAI; //AI that makes decisions
bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface.
struct SpellbookLastSetting
{
int spellbookLastPageBattle, spellbokLastPageAdvmap; //on which page we left spellbook

View File

@ -579,16 +579,17 @@ void CClient::battleStarted(const BattleInfo * info)
// if(battleCallbacks.count(side))
// battleCallbacks[side]->setBattle(info);
shared_ptr<CPlayerInterface> att, def;
if(vstd::contains(playerint, info->sides[0]) && playerint[info->sides[0]]->human)
att = std::dynamic_pointer_cast<CPlayerInterface>( playerint[info->sides[0]] );
else
att = NULL;
shared_ptr<CPlayerInterface> att = nullptr, def = nullptr;
if(vstd::contains(playerint, info->sides[1]) && playerint[info->sides[1]]->human)
def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[info->sides[1]] );
else
def = NULL;
//If quick combat is not, do not prepare interfaces for battleint
if(!settings["adventure"]["quickCombat"].Bool())
{
if(vstd::contains(playerint, info->sides[0]) && playerint[info->sides[0]]->human)
att = std::dynamic_pointer_cast<CPlayerInterface>( playerint[info->sides[0]] );
if(vstd::contains(playerint, info->sides[1]) && playerint[info->sides[1]]->human)
def = std::dynamic_pointer_cast<CPlayerInterface>( playerint[info->sides[1]] );
}
if(!gNoGUI && (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL))
{

View File

@ -97,7 +97,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(NULL), sp(NULL),
siegeH(NULL), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0),
givenCommand(NULL), myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL),
autofightingAI(nullptr), isAutoFightOn(false), aiThread(nullptr), background(nullptr)
background(nullptr)
{
OBJ_CONSTRUCTION;
@ -498,7 +498,7 @@ void CBattleInterface::setPrintMouseShadow(bool set)
void CBattleInterface::activate()
{
if(isAutoFightOn)
if(curInt->isAutoFightOn)
{
bAutofight->activate();
return;
@ -1277,23 +1277,23 @@ void CBattleInterface::bAutofightf()
return;
//Stop auto-fight mode
if(isAutoFightOn)
if(curInt->isAutoFightOn)
{
assert(autofightingAI);
isAutoFightOn = false;
assert(curInt->autofightingAI);
curInt->isAutoFightOn = false;
logGlobal->traceStream() << "Stopping the autofight...";
aiThread->interrupt();
aiThread->join();
autofightingAI = nullptr;
aiThread = nullptr;
}
else
{
isAutoFightOn = true;
autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
autofightingAI->init(curInt->cb);
autofightingAI->battleStart(army1, army2, int3(0,0,0), attackingHeroInstance, defendingHeroInstance, curInt->cb->battleGetMySide());
curInt->isAutoFightOn = true;
deactivate();
bAutofight->activate();
auto ai = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
ai->init(curInt->cb);
ai->battleStart(army1, army2, int3(0,0,0), attackingHeroInstance, defendingHeroInstance, curInt->cb->battleGetMySide());
curInt->autofightingAI = ai;
curInt->cb->registerBattleInterface(ai);
requestAutofightingAIToTakeAction();
}
@ -1627,7 +1627,7 @@ void CBattleInterface::displayBattleFinished()
CCS->curh->changeGraphic(ECursor::ADVENTURE,0);
SDL_Rect temp_rect = genRect(561, 470, (screen->w - 800)/2 + 165, (screen->h - 600)/2 + 19);
resWindow = new CBattleResultWindow(*bresult, temp_rect, this);
resWindow = new CBattleResultWindow(*bresult, temp_rect, *this->curInt);
GH.pushInt(resWindow);
}
@ -2096,9 +2096,6 @@ void CBattleInterface::activateStack()
if(!pendingAnims.size() && !active)
activate();
if(isAutoFightOn)
requestAutofightingAIToTakeAction();
GH.fakeMouseMove();
}
@ -3590,20 +3587,23 @@ InfoAboutHero CBattleInterface::enemyHero() const
void CBattleInterface::requestAutofightingAIToTakeAction()
{
assert(isAutoFightOn);
assert(curInt->isAutoFightOn);
deactivate();
bAutofight->activate();
aiThread = make_unique<boost::thread>([&]
auto tmp = make_unique<boost::thread>([&]
{
auto ba = new BattleAction(autofightingAI->activeStack(activeStack));
auto ba = new BattleAction(curInt->autofightingAI->activeStack(activeStack));
if(isAutoFightOn)
if(curInt->isAutoFightOn)
{
givenCommand->setn(ba);
}
else
{
boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
activateStack();
}
});
tmp->detach();
}
CBattleInterface::SiegeHelper::SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface * _owner)

View File

@ -156,10 +156,6 @@ private:
PossibleActions selectedAction; //last action chosen (and saved) by player
PossibleActions illegalAction; //most likely action that can't be performed here
shared_ptr<CBattleGameInterface> autofightingAI;
bool isAutoFightOn;
unique_ptr<boost::thread> aiThread;
void requestAutofightingAIToTakeAction();
void getPossibleActionsForStack (const CStack * stack); //called when stack gets its turn

View File

@ -304,13 +304,13 @@ void CBattleOptionsWindow::bExitf()
GH.popIntTotally(this);
}
CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect & pos, CBattleInterface * _owner)
CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect & pos, CPlayerInterface &_owner)
: owner(_owner)
{
OBJ_CONSTRUCTION_CAPTURING_ALL;
this->pos = pos;
CPicture * bg = new CPicture("CPRESULT");
bg->colorize(owner->curInt->playerID);
bg->colorize(owner.playerID);
exit = new CAdventureMapButton ("", "", boost::bind(&CBattleResultWindow::bExitf,this), 384, 505, "iok6432.def", SDLK_RETURN);
exit->borderColor = Colors::METALLIC_GOLD;
@ -331,59 +331,40 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
new CLabel(232, 332, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[408]);
new CLabel(232, 428, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[409]);
std::string attackerName, defenderName;
std::string sideNames[2] = {"N/A", "N/A"};
if(owner->attackingHeroInstance) //a hero attacked
for(int i = 0; i < 2; i++)
{
new CAnimImage("PortraitsLarge", owner->attackingHeroInstance->portrait, 0, 21, 38);
//setting attackerName
attackerName = owner->attackingHeroInstance->name;
}
else //a monster attacked
{
int bestMonsterID = -1;
ui32 bestPower = 0;
for(TSlots::const_iterator it = owner->army1->Slots().begin(); it!=owner->army1->Slots().end(); ++it)
auto heroInfo = owner.cb->battleGetHeroInfo(i);
const int xs[] = {21, 392};
if(heroInfo.portrait >= 0) //attacking hero
{
if(it->second->type->AIValue > bestPower)
new CAnimImage("PortraitsLarge", heroInfo.portrait, 0, xs[i], 38);
sideNames[i] = heroInfo.name;
}
else
{
int bestMonsterID = -1;
ui32 bestPower = 0;
auto stacks = owner.cb->battleGetAllStacks();
vstd::erase_if(stacks, [i](const CStack *stack) //erase stack of other side and not coming from garrison
{ return stack->attackerOwned == i || !stack->base; });
auto best = vstd::maxElementByFun(stacks, [](const CStack *stack){ return stack->type->AIValue; });
if(best != stacks.end()) //should be always but to be safe...
{
bestPower = it->second->type->AIValue;
bestMonsterID = it->second->type->idNumber;
new CAnimImage("TWCRPORT", (*best)->type->idNumber+2, 0, xs[i], 38);
sideNames[i] = CGI->creh->creatures[(*best)->type->idNumber]->namePl;
}
}
new CAnimImage("TWCRPORT", bestMonsterID+2, 0, 21, 38);
//setting attackerName
attackerName = CGI->creh->creatures[bestMonsterID]->namePl;
}
if(owner->defendingHeroInstance) //a hero defended
{
new CAnimImage("PortraitsLarge", owner->defendingHeroInstance->portrait, 0, 392, 38);
//setting defenderName
defenderName = owner->defendingHeroInstance->name;
}
else //a monster defended
{
int bestMonsterID = -1;
ui32 bestPower = 0;
for(TSlots::const_iterator it = owner->army2->Slots().begin(); it!=owner->army2->Slots().end(); ++it)
{
if( it->second->type->AIValue > bestPower)
{
bestPower = it->second->type->AIValue;
bestMonsterID = it->second->type->idNumber;
}
}
new CAnimImage("TWCRPORT", CGI->creh->creatures[bestMonsterID]->iconIndex, 0, 392, 38);
//setting defenderName
defenderName = CGI->creh->creatures[bestMonsterID]->namePl;
}
//printing attacker and defender's names
new CLabel( 89, 37, FONT_SMALL, TOPLEFT, Colors::WHITE, attackerName);
new CLabel( 89, 37, FONT_SMALL, TOPLEFT, Colors::WHITE, sideNames[0]);
new CLabel( 381, 53, FONT_SMALL, BOTTOMRIGHT, Colors::WHITE, sideNames[1]);
new CLabel( 381, 53, FONT_SMALL, BOTTOMRIGHT, Colors::WHITE, defenderName);
//printing casualities
//printing casualties
for(int step = 0; step < 2; ++step)
{
if(br.casualties[step].size()==0)
@ -405,7 +386,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
}
}
//printing result description
bool weAreAttacker = (owner->curInt->playerID == owner->attackingHeroInstance->tempOwner);
bool weAreAttacker = !(owner.cb->battleGetMySide());
if((br.winner == 0 && weAreAttacker) || (br.winner == 1 && !weAreAttacker)) //we've won
{
int text=-1;
@ -420,7 +401,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
CCS->videoh->open("WIN3.BIK");
std::string str = CGI->generaltexth->allTexts[text];
const CGHeroInstance * ourHero = weAreAttacker? owner->attackingHeroInstance : owner->defendingHeroInstance;
const CGHeroInstance * ourHero = owner.cb->battleGetMyHero();
if (ourHero)
{
str += CGI->generaltexth->allTexts[305];
@ -465,7 +446,7 @@ CBattleResultWindow::~CBattleResultWindow()
void CBattleResultWindow::activate()
{
owner->curInt->showingDialog->set(true);
owner.showingDialog->set(true);
CIntObject::activate();
}
@ -483,9 +464,14 @@ void CBattleResultWindow::bExitf()
return;
}
auto intTmp = owner->curInt;
GH.popInts(2); //first - we; second - battle interface
intTmp->showingDialog->setn(false);
CPlayerInterface &intTmp = owner; //copy reference because "this" will be destructed soon
GH.popIntTotally(this);
if(dynamic_cast<CBattleInterface*>(GH.topInt()))
GH.popInts(1); //pop battle interface if present
//Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle,
//so we can be sure that there is no dialogs left on GUI stack.
intTmp.showingDialog->setn(false);
CCS->videoh->close();
}

View File

@ -14,6 +14,7 @@ class CLabel;
struct BattleResult;
class CStack;
class CAnimImage;
class CPlayerInterface;
/*
* CBattleInterfaceClasses.h, part of VCMI engine
@ -87,9 +88,9 @@ class CBattleResultWindow : public CIntObject
{
private:
CAdventureMapButton *exit;
CBattleInterface *owner;
CPlayerInterface &owner;
public:
CBattleResultWindow(const BattleResult & br, const SDL_Rect & pos, CBattleInterface * _owner); //c-tor
CBattleResultWindow(const BattleResult & br, const SDL_Rect & pos, CPlayerInterface &_owner); //c-tor
~CBattleResultWindow(); //d-tor
void bExitf(); //exit button callback

View File

@ -288,7 +288,10 @@ InfoAboutHero CBattleInfoEssentials::battleGetHeroInfo( ui8 side ) const
{
auto hero = getBattle()->heroes[side];
if(!hero)
{
logGlobal->warnStream() << __FUNCTION__ << ": side " << (int)side << " does not have hero!";
return InfoAboutHero();
}
return InfoAboutHero(hero, battleDoWeKnowAbout(side));
}