Files
wagic/projects/mtg/src/GameObserver.cpp
T
solo81@web.de 897114af70 Added the new zone "OPPONENTHAND" (by Zethfox).
Zethfox: "Opponenthand gui access, cards that target(*|opponenthand) can now be coded as such, target chooser will activate allowing you to click the new icon under the avatar of the opponent and open the opponents hand Gui so you may select the target card."

Cards in opponents hand are only viewable when target choosing would allow you to enter that zone! ;)


- Added the new keyword "NAME". (by Zethfox). 
When used in an autoline it replaces an autoline's "ability" text with a custom ability name. 
The basic phrase is: "auto=name(whatever you want) &&".

Example card:
[card]
name=Order of the Stars
abilities=defender
auto=choice name(white) && counter(0/0,1,White) all(this)
auto=choice name(blue) && counter(0/0,1,Blue) all(this)
auto=choice name(black) && counter(0/0,1,Black) all(this)
auto=choice name(red) && counter(0/0,1,Red) all(this)
auto=choice name(green) && counter(0/0,1,Green) all(this)
auto=this(counter{0/0.1.White}) protection from white
auto=this(counter{0/0.1.Blue}) protection from blue
auto=this(counter{0/0.1.Black}) protection from black
auto=this(counter{0/0.1.Red}) protection from red
auto=this(counter{0/0.1.Green}) protection from green
text=Defender (This creature can't attack.) -- As Order of the Stars enters the battlefield, choose a color. -- Order of the Stars has protection from the chosen color.
mana={W}
type=Creature
subtype=Human Cleric
power=0
toughness=1
[/card]

The popup window for this card will now contain a list with 

"white,"blue","black","red" and "green" 
instead of 
"ability","ability","ability","ability","ability".

This will make a lot of cards much easier to handle!


- Added 42 successfully test using one (or both) new keywords.
Card list --> First comment


Note that we did not add tests for both new keywords:
It is simply not possible to write them yet! They will follow as soon as they are possible. We guarantee that everything we submitted in this revision has been tested excessively!
2010-08-13 00:38:56 +00:00

531 lines
14 KiB
C++

#include "../include/config.h"
#include "../include/GameObserver.h"
#include "../include/GameOptions.h"
#include "../include/CardGui.h"
#include "../include/Damage.h"
#include "../include/Rules.h"
#include "../include/ExtraCost.h"
#include <JRenderer.h>
GameObserver * GameObserver::mInstance = NULL;
GameObserver* GameObserver::GetInstance()
{
return mInstance;
}
void GameObserver::EndInstance()
{
SAFE_DELETE(mInstance);
}
void GameObserver::Init(Player * _players[], int _nbplayers){
mInstance = NEW GameObserver(_players, _nbplayers);
}
GameObserver::GameObserver(Player * _players[], int _nb_players){
for (int i = 0; i < _nb_players;i ++){
players[i] = _players[i];
}
currentPlayer = NULL;
currentActionPlayer = NULL;
isInterrupting = NULL;
currentPlayerId = 0;
nbPlayers = _nb_players;
currentGamePhase = -1;
targetChooser = NULL;
cardWaitingForTargets = NULL;
waitForExtraPayment = NULL;
reaction = 0;
gameOver = NULL;
phaseRing = NULL;
replacementEffects = NEW ReplacementEffects();
combatStep = BLOCKERS;
mRules = NULL;
}
int GameObserver::getCurrentGamePhase(){
return currentGamePhase;
}
Player * GameObserver::opponent(){
int index = (currentPlayerId+1)%nbPlayers;
return players[index];
}
void GameObserver::nextPlayer(){
turn++;
currentPlayerId = (currentPlayerId+1)%nbPlayers;
currentPlayer = players[currentPlayerId];
currentActionPlayer = currentPlayer;
combatStep = BLOCKERS;
}
void GameObserver::nextGamePhase(){
Phase * cPhaseOld = phaseRing->getCurrentPhase();
if (cPhaseOld->id == Constants::MTG_PHASE_COMBATDAMAGE)
if (FIRST_STRIKE == combatStep || END_FIRST_STRIKE == combatStep || DAMAGE == combatStep) {
nextCombatStep(); return;
}
if (cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS)
if (BLOCKERS == combatStep) { nextCombatStep(); return; }
phaseRing->forward();
//Go directly to end of combat if no attackers
if (cPhaseOld->id == Constants::MTG_PHASE_COMBATATTACKERS && !(currentPlayer->game->inPlay->getNextAttacker(NULL))){
phaseRing->forward();
phaseRing->forward();
}
Phase * cPhase = phaseRing->getCurrentPhase();
currentGamePhase = cPhase->id;
if (Constants::MTG_PHASE_COMBATDAMAGE == currentGamePhase)
nextCombatStep();
if (currentPlayer != cPhase->player) nextPlayer();
//init begin of turn
if (currentGamePhase == Constants::MTG_PHASE_BEFORE_BEGIN){
cleanupPhase();
currentPlayer->canPutLandsIntoPlay = 1;
mLayers->actionLayer()->cleanGarbage(); //clean abilities history for this turn;
mLayers->stackLayer()->garbageCollect(); //clean stack history for this turn;
mLayers->actionLayer()->Update(0);
for (int i=0; i < 2; i++){
delete (players[i]->game->garbage);
players[i]->game->garbage = NEW MTGGameZone();
players[i]->game->garbage->setOwner(players[i]);
}
combatStep = BLOCKERS;
return nextGamePhase();
}
for (int i = 0; i < 2; ++i)
players[i]->getManaPool()->init();
//After End of turn
if (currentGamePhase == Constants::MTG_PHASE_AFTER_EOT){
//Auto Hand cleaning, in case the player didn't do it himself
while(currentPlayer->game->hand->nb_cards > 7)
currentPlayer->game->putInGraveyard(currentPlayer->game->hand->cards[0]);
mLayers->actionLayer()->Update(0);
return nextGamePhase();
}
//Phase Specific actions
switch(currentGamePhase){
case Constants::MTG_PHASE_UNTAP:
untapPhase();
break;
case Constants::MTG_PHASE_DRAW:
//mLayers->stackLayer()->addDraw(currentPlayer,1);
break;
default:
break;
}
}
int GameObserver::cancelCurrentAction(){
SAFE_DELETE(targetChooser);
return mLayers->actionLayer()->cancelCurrentAction();
}
void GameObserver::nextCombatStep()
{
switch (combatStep)
{
case BLOCKERS : receiveEvent(NEW WEventBlockersChosen());
receiveEvent(NEW WEventCombatStepChange(combatStep = ORDER)); return;
case ORDER : receiveEvent(NEW WEventCombatStepChange(combatStep = FIRST_STRIKE)); return;
case FIRST_STRIKE : receiveEvent(NEW WEventCombatStepChange(combatStep = END_FIRST_STRIKE)); return;
case END_FIRST_STRIKE : receiveEvent(NEW WEventCombatStepChange(combatStep = DAMAGE)); return;
case DAMAGE : receiveEvent(NEW WEventCombatStepChange(combatStep = END_DAMAGE)); return;
case END_DAMAGE : ; // Nothing : go to next phase
}
}
void GameObserver::userRequestNextGamePhase(){
if (mLayers->stackLayer()->getNext(NULL,0,NOT_RESOLVED)) return;
if (getCurrentTargetChooser()) return;
Phase * cPhaseOld = phaseRing->getCurrentPhase();
if ((cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS && combatStep == ORDER) ||
cPhaseOld->id == Constants::MTG_PHASE_COMBATDAMAGE ||
opponent()->isAI() ||
options[Options::optionInterrupt(currentGamePhase)].number)
mLayers->stackLayer()->AddNextGamePhase();
else
nextGamePhase();
}
int GameObserver::forceShuffleLibraries(){
int result = 0;
if (players[0]->game->library->needShuffle)
{
players[0]->game->library->shuffle();
players[0]->game->library->needShuffle = false;
++result;
}
if (players[1]->game->library->needShuffle)
{
players[1]->game->library->shuffle();
players[1]->game->library->needShuffle = false;
++result;
}
return result;
}
void GameObserver::startGame(Rules * rules){
turn = 0;
mRules = rules;
if (rules)
rules->initPlayers();
mLayers = NEW DuelLayers();
mLayers->init();
currentPlayerId = 0;
currentPlayer = players[0];
currentActionPlayer = currentPlayer;
phaseRing = NEW PhaseRing(players,nbPlayers);
if (rules)
rules->initGame();
//Preload images from hand
if (!players[0]->isAI()){
for (int i=0; i< players[0]->game->hand->nb_cards; i++){
resources.RetrieveCard(players[0]->game->hand->cards[i],CACHE_THUMB);
resources.RetrieveCard(players[0]->game->hand->cards[i]);
}
}
startedAt = time(0);
//Difficult mode special stuff
if (!players[0]->isAI() && players[1]->isAI()){
int difficulty = options[Options::DIFFICULTY].number;
if (options[Options::DIFFICULTY_MODE_UNLOCKED].number && difficulty) {
Player * p = players[1];
for (int level=0; level < difficulty; level ++){
MTGCardInstance * card = NULL;
MTGGameZone * z = p->game->library;
for (int j = 0; j<z->nb_cards; j++){
MTGCardInstance * _card = z->cards[j];
if (_card->hasType("land")){
card = _card;
j = z->nb_cards;
}
}
if (card){
MTGCardInstance * copy = p->game->putInZone(card, p->game->library, p->game->stack);
Spell * spell = NEW Spell(copy);
spell->resolve();
delete spell;
}
}
}
}
}
void GameObserver::addObserver(MTGAbility * observer){
mLayers->actionLayer()->Add(observer);
}
void GameObserver::removeObserver(ActionElement * observer){
if (observer)mLayers->actionLayer()->moveToGarbage(observer);
else
{} //TODO log error
}
GameObserver::~GameObserver(){
LOG("==Destroying GameObserver==");
SAFE_DELETE(targetChooser);
SAFE_DELETE(mLayers);
SAFE_DELETE(phaseRing);
SAFE_DELETE(replacementEffects);
for (int i = 0; i < nbPlayers; ++i){
SAFE_DELETE(players[i]->game);
SAFE_DELETE(players[i]);
}
LOG("==GameObserver Destroyed==");
}
void GameObserver::Update(float dt){
Player * player = currentPlayer;
if (Constants::MTG_PHASE_COMBATBLOCKERS == currentGamePhase && BLOCKERS == combatStep)
player = player->opponent();
currentActionPlayer = player;
if (isInterrupting) player = isInterrupting;
mLayers->Update(dt,player);
while (mLayers->actionLayer()->stuffHappened){
mLayers->actionLayer()->Update(0);
}
stateEffects();
oldGamePhase = currentGamePhase;
}
//applies damage to creatures after updates
//Players life test
void GameObserver::stateEffects()
{
if (mLayers->stackLayer()->count(0,NOT_RESOLVED) != 0) return;
if (mLayers->actionLayer()->menuObject) return;
if (targetChooser || mLayers->actionLayer()->isWaitingForAnswer()) return;
for (int i =0; i < 2; i++){
MTGGameZone * zone = players[i]->game->inPlay;
for (int j = zone->nb_cards-1 ; j>=0; j--){
MTGCardInstance * card = zone->cards[j];
card->afterDamage();
//Remove auras that don't have a valid target anymore
if (card->target && !isInPlay(card->target) && !card->hasType("equipment")){
players[i]->game->putInGraveyard(card);
}
}
}
for (int i =0; i < 2; i++)
if (players[i]->life <= 0) gameOver = players[i];
}
void GameObserver::Render()
{
mLayers->Render();
if (targetChooser || mLayers->actionLayer()->isWaitingForAnswer())
JRenderer::GetInstance()->DrawRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB(255,255,0,0));
if (waitForExtraPayment)
waitForExtraPayment->Render();
for (int i = 0; i < nbPlayers; ++i){
players[i]->Render();
}
}
void GameObserver::ButtonPressed(PlayGuiObject * target){
#if defined (WIN32) || defined (LINUX)
OutputDebugString("GAMEOBSERVER Click\n");
#endif
if (CardView* cardview = dynamic_cast<CardView*>(target)){
MTGCardInstance * card = cardview->getCard();
cardClick(card, card);
}
else if (GuiLibrary* library = dynamic_cast<GuiLibrary*>(target)){
if (library->showCards){
library->toggleDisplay();
forceShuffleLibraries();
} else {
TargetChooser * _tc = this->getCurrentTargetChooser();
if (_tc && _tc->targetsZone(library->zone)){
library->toggleDisplay();
library->zone->needShuffle = true;
}
}
}
else if (GuiGraveyard* graveyard = dynamic_cast<GuiGraveyard*>(target))
graveyard->toggleDisplay();
//opponenthand
else if (GuiOpponentHand* opponentHand = dynamic_cast<GuiOpponentHand*>(target))
if (opponentHand->showCards){
opponentHand->toggleDisplay();
} else {
TargetChooser * _tc = this->getCurrentTargetChooser();
if (_tc && _tc->targetsZone(opponentHand->zone)){
opponentHand->toggleDisplay();
}
}
//end opponenthand
else if (GuiAvatar* avatar = dynamic_cast<GuiAvatar*>(target)){
cardClick(NULL, avatar->player);
}
}
void GameObserver::stackObjectClicked(Interruptible * action){
if (targetChooser != NULL){
int result = targetChooser->toggleTarget(action);
if (result == TARGET_OK_FULL){
cardClick(cardWaitingForTargets);
}else{
return;
}
}else{
reaction = mLayers->actionLayer()->isReactingToTargetClick(action);
if (reaction == -1) mLayers->actionLayer()->reactToTargetClick(action);
}
}
void GameObserver::cardClick (MTGCardInstance * card, Targetable * object){
Player * clickedPlayer = NULL;
if (!card) clickedPlayer = ((Player *)object);
if (targetChooser){
int result;
if (card) {
if (card == cardWaitingForTargets){
int _result = targetChooser->ForceTargetListReady();
if (_result){
result = TARGET_OK_FULL;
}else{
result = targetChooser->targetsReadyCheck();
}
}else{
result = targetChooser->toggleTarget(card);
}
}else{
result = targetChooser->toggleTarget(clickedPlayer);
}
if (result == TARGET_OK_FULL)
card = cardWaitingForTargets;
else
return;
}
if (waitForExtraPayment){
if (card){
waitForExtraPayment->tryToSetPayment(card);
}
if (waitForExtraPayment->isPaymentSet()){
mLayers->actionLayer()->reactToClick(waitForExtraPayment->action, waitForExtraPayment->source);
waitForExtraPayment = NULL;
}
return;
}
if (ORDER == combatStep)
{
card->defenser->raiseBlockerRankOrder(card);
return;
}
if (card){
/* Fix for Issue http://code.google.com/p/wagic/issues/detail?id=270
put into play is hopefully the only ability causing that kind of trouble
If the same kind of issue occurs with other abilities, let's think of a cleaner solution
*/
if (targetChooser) {
MTGAbility * a = mLayers->actionLayer()->getAbility(MTGAbility::PUT_INTO_PLAY);
a->reactToClick(card);
return;
}
reaction = mLayers->actionLayer()->isReactingToClick(card);
if (reaction == -1) mLayers->actionLayer()->reactToClick(card);
}else{
reaction = mLayers->actionLayer()->isReactingToTargetClick(object);
if (reaction == -1) mLayers->actionLayer()->reactToTargetClick(object);
}
if (reaction == -1) return;
if (!card) return;
//Current player's hand
if (currentPlayer->game->hand->hasCard(card) && currentGamePhase == Constants::MTG_PHASE_CLEANUP && currentPlayer->game->hand->nb_cards > 7){
currentPlayer->game->putInGraveyard(card);
}else if (reaction){
if (reaction == 1){
mLayers->actionLayer()->reactToClick(card);
}else{
mLayers->actionLayer()->setMenuObject(object);
}
}else if (card->isTapped() && card->controller() == currentPlayer){
untap(card);
}
}
int GameObserver::untap(MTGCardInstance * card) {
if (!card->isUntapping()){
return 0;
}
if (card->has(Constants::DOESNOTUNTAP)) return 0;
card->attemptUntap();
return 1;
}
TargetChooser * GameObserver::getCurrentTargetChooser(){
TargetChooser * _tc = mLayers->actionLayer()->getCurrentTargetChooser();
if (_tc) return _tc;
return targetChooser;
}
/* Returns true if the card is in one of the player's play zone */
int GameObserver::isInPlay(MTGCardInstance * card){
for (int i = 0; i < 2; i++){
if (players[i]->game->isInPlay(card)) return 1;
}
return 0;
}
void GameObserver::draw(){
currentPlayer->game->drawFromLibrary();
}
void GameObserver::cleanupPhase(){
currentPlayer->cleanupPhase();
opponent()->cleanupPhase();
}
void GameObserver::untapPhase(){
currentPlayer->inPlay()->untapAll();
}
int GameObserver::receiveEvent(WEvent * e){
if (!e) return 0;
eventsQueue.push(e);
if (eventsQueue.size() > 1) return -1;
int result = 0;
while(eventsQueue.size()){
WEvent * ev = eventsQueue.front();
result += mLayers->receiveEvent(ev);
for (int i = 0; i < 2; ++i){
result += players[i]->receiveEvent(ev);
}
SAFE_DELETE(ev);
eventsQueue.pop();
}
return result;
}
Player * GameObserver::currentlyActing(){
if (isInterrupting) return isInterrupting;
return currentActionPlayer;
}
//TODO CORRECT THIS MESS
int GameObserver::targetListIsSet(MTGCardInstance * card){
if (targetChooser == NULL){
TargetChooserFactory tcf;
targetChooser = tcf.createTargetChooser(card);
cardWaitingForTargets = card;
if (targetChooser == NULL){
return 1;
}
}
return (targetChooser->targetListSet());
}