Files
wagic/projects/mtg/src/ActionLayer.cpp
wrenczes@gmail.com ffd3b7b074 At Z's request, fixed Issue 184, ExtraCost types with targets such as Siege-Gang Commander/Drowner of secrets combined with targeted abilities bug.
This one was a bit of a doozy to fix correctly, but the actual fix ended up being fairly simple - the upshot is that TargetAbility never checked for whether an extra cost needed setting prior doing a target selection.  While at it, I discovered and fixed another bug:  if you're in the middle of an extra cost choice (like sacrifice, for instance) and hit the next phase button, the game would let you proceed, and then hang in an endless loop.

While at it, did a little cleanup/refactoring around GameObserver's waitForExtraPayment - any time a bool has something that sounds like a verb, it probably deserves to be a function.  Now it is. (I needed to refactor it anyway, as I reused that code for the next phase hang.)

Note that after this fix, I had to patch two test cases (siege_gang_commander.txt & seismic_assault.txt) - since I've change the selection order (ie a target ability with a sacrifice cost requires the cost to be paid up front before picking the target), this means that tests involving targeting & sacrifices need to switch the order of the cards to pass.
2010-12-06 06:49:36 +00:00

346 lines
7.9 KiB
C++

#include "PrecompiledHeader.h"
#include "ActionLayer.h"
#include "GameObserver.h"
#include "Targetable.h"
#include "WEvent.h"
MTGAbility* ActionLayer::getAbility(int type)
{
for (int i = 1; i < mCount; i++)
{
MTGAbility * a = ((MTGAbility *) mObjects[i]);
if (a->aType == type)
{
return a;
}
}
return NULL;
}
int ActionLayer::moveToGarbage(ActionElement * e)
{
int i = getIndexOf(e);
if (i != -1)
{
if (isWaitingForAnswer() == e)
setCurrentWaitingAction(NULL);
e->destroy();
mObjects.erase(mObjects.begin() + i);
mCount--;
garbage.push_back(e);
return 1;
}
return 0;
}
int ActionLayer::cleanGarbage()
{
for (size_t i = 0; i < garbage.size(); ++i)
{
delete (garbage[i]);
}
garbage.clear();
return 1;
}
int ActionLayer::reactToClick(ActionElement * ability, MTGCardInstance * card)
{
int result = ability->reactToClick(card);
if (result)
stuffHappened = 1;
return result;
}
int ActionLayer::reactToTargetClick(ActionElement* ability, Targetable * card)
{
int result = ability->reactToTargetClick(card);
if (result)
stuffHappened = 1;
return result;
}
bool ActionLayer::CheckUserInput(JButton key)
{
GameObserver * g = GameObserver::GetInstance();
if (g->mExtraPayment && key == JGE_BTN_SEC)
{
g->mExtraPayment = NULL;
return 1;
}
if (menuObject)
{
return false;
}
for (int i = 0; i < mCount; i++)
{
if (mObjects[i] != NULL)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
if (currentAction->CheckUserInput(key))
return true;
}
}
return false;
}
void ActionLayer::Update(float dt)
{
stuffHappened = 0;
if (menuObject)
{
abilitiesMenu->Update(dt);
return;
}
modal = 0;
GameObserver* game = GameObserver::GetInstance();
for (int i = mCount - 1; i >= 0; i--)
{
if (mObjects[i] != NULL)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
if (currentAction->testDestroy())
game->removeObserver(currentAction);
}
}
int newPhase = game->getCurrentGamePhase();
for (int i = 0; i < mCount; i++)
{
if (mObjects[i] != NULL)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
currentAction->newPhase = newPhase;
currentAction->Update(dt);
currentAction->currentPhase = newPhase;
}
}
if (cantCancel)
{
ActionElement * ae = isWaitingForAnswer();
if (ae && !ae->tc->validTargetsExist())
{
cantCancel = 0;
cancelCurrentAction();
}
}
}
void ActionLayer::Render()
{
if (menuObject)
{
abilitiesMenu->Render();
return;
}
for (int i = 0; i < mCount; i++)
{
if (mObjects[i] != NULL)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
currentAction->Render();
}
}
}
void ActionLayer::setCurrentWaitingAction(ActionElement * ae)
{
assert(!ae || !currentWaitingAction);//this assert causes crashes when may abilities overlap each other on ai. this conidiation is preexsiting.
currentWaitingAction = ae;
if (!ae)
cantCancel = 0;
}
TargetChooser * ActionLayer::getCurrentTargetChooser()
{
if (currentWaitingAction && currentWaitingAction->waitingForAnswer)
return currentWaitingAction->tc;
return NULL;
}
int ActionLayer::cancelCurrentAction()
{
ActionElement * ae = isWaitingForAnswer();
if (!ae)
return 0;
if (cantCancel && ae->tc->validTargetsExist())
return 0;
ae->waitingForAnswer = 0; //TODO MOVE THIS IN ActionElement
setCurrentWaitingAction(NULL);
return 1;
}
ActionElement * ActionLayer::isWaitingForAnswer()
{
if (currentWaitingAction && currentWaitingAction->waitingForAnswer)
return currentWaitingAction;
return NULL;
}
int ActionLayer::stillInUse(MTGCardInstance * card)
{
for (int i = 0; i < mCount; i++)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
if (currentAction->stillInUse(card))
return 1;
}
return 0;
}
int ActionLayer::receiveEventPlus(WEvent * event)
{
int result = 0;
for (int i = 0; i < mCount; i++)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
result += currentAction->receiveEvent(event);
}
return 0;
}
int ActionLayer::isReactingToTargetClick(Targetable * card)
{
int result = 0;
if (isWaitingForAnswer())
return -1;
for (int i = 0; i < mCount; i++)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
result += currentAction->isReactingToTargetClick(card);
}
return result;
}
int ActionLayer::reactToTargetClick(Targetable * card)
{
int result = 0;
ActionElement * ae = isWaitingForAnswer();
if (ae)
return reactToTargetClick(ae, card);
for (int i = 0; i < mCount; i++)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
result += currentAction->reactToTargetClick(card);
}
return result;
}
//TODO Simplify with only object !!!
int ActionLayer::isReactingToClick(MTGCardInstance * card)
{
int result = 0;
if (isWaitingForAnswer())
return -1;
for (int i = 0; i < mCount; i++)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
result += currentAction->isReactingToClick(card);
}
return result;
}
int ActionLayer::reactToClick(MTGCardInstance * card)
{
int result = 0;
ActionElement * ae = isWaitingForAnswer();
if (ae)
return reactToClick(ae, card);
for (int i = 0; i < mCount; i++)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
result += reactToClick(currentAction, card);
if (result)
return result;
}
return result;
}
void ActionLayer::setMenuObject(Targetable * object, bool must)
{
if (!object)
{
DebugTrace("FATAL: ActionLayer::setMenuObject");
return;
}
menuObject = object;
SAFE_DELETE(abilitiesMenu);
abilitiesMenu = NEW SimpleMenu(10, this, Fonts::MAIN_FONT, 100, 100, object->getDisplayName().c_str());
for (int i = 0; i < mCount; i++)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
if (currentAction->isReactingToTargetClick(object))
{
abilitiesMenu->Add(i, currentAction->getMenuText());
}
}
if (!must)
abilitiesMenu->Add(kCancelMenuID, "Cancel");
else
cantCancel = 1;
modal = 1;
}
void ActionLayer::doReactTo(int menuIndex)
{
if (menuObject)
{
int controlid = abilitiesMenu->mObjects[menuIndex]->GetId();
DebugTrace("ActionLayer::doReactTo " << controlid);
ButtonPressed(0, controlid);
}
}
void ActionLayer::ButtonPressed(int controllerid, int controlid)
{
if (controlid >= 0 && controlid < static_cast<int>(mObjects.size()))
{
ActionElement * currentAction = (ActionElement *) mObjects[controlid];
currentAction->reactToTargetClick(menuObject);
menuObject = 0;
}
else if (controlid == kCancelMenuID)
{
GameObserver::GetInstance()->mLayers->stackLayer()->endOfInterruption();
menuObject = 0;
}
else
{
// fallthrough case. We have an id we don't recognize - do nothing, don't clear the menu!
//assert(false);
}
}
ActionLayer::ActionLayer()
{
menuObject = NULL;
abilitiesMenu = NULL;
stuffHappened = 0;
currentWaitingAction = NULL;
cantCancel = 0;
}
ActionLayer::~ActionLayer()
{
for (int i = mCount - 1; i >= 0; i--)
{
moveToGarbage((ActionElement *) mObjects[i]);
}
SAFE_DELETE(abilitiesMenu);
cleanGarbage();
}