* Some more tweaking for combat.
* This still doesn't work, but it's closer.
This commit is contained in:
jean.chalard
2009-09-03 02:16:53 +00:00
parent a7bef9142c
commit 7214248494
15 changed files with 145 additions and 203 deletions

View File

@@ -40,7 +40,7 @@ class Damage: public Interruptible {
};
class DamageStack :public GuiLayer, public Interruptible{
class DamageStack : public GuiLayer, public Interruptible{
protected:
int currentState;
GameObserver* game;
@@ -49,20 +49,7 @@ class DamageStack :public GuiLayer, public Interruptible{
int resolve();
void Render();
virtual ostream& toString(ostream& out) const;
DamageStack(GameObserver* game);
DamageStack();
};
class StableDamageStack : public Interruptible {
protected:
vector<Damage*> damage;
public:
int resolve();
void Render();
void Add(Damage*);
virtual ostream& toString(ostream& out) const;
};
#endif

View File

@@ -31,11 +31,9 @@ class GameObserver{
int nbPlayers;
int currentPlayerId;
int currentRound;
int blockersAssigned;
public:
int blockersSorted;
CombatStep combatStep;
int turn;
int forceShuffleLibraries();
int targetListIsSet(MTGCardInstance * card);
@@ -57,6 +55,7 @@ class GameObserver{
void cardClick(MTGCardInstance * card,Targetable * _object = NULL );
int enteringPhase(int phase);
int getCurrentGamePhase();
void nextCombatStep();
void userRequestNextGamePhase();
void nextGamePhase();
void cleanupPhase();

View File

@@ -7,7 +7,7 @@
#include "CardGui.h"
#include "GuiAvatars.h"
class TransientCardView;
class CardView;
struct GuiStatic : public PlayGuiObject{
GuiAvatars* parent;
@@ -35,7 +35,7 @@ struct GuiAvatar : public GuiStatic{
struct GuiGameZone : public GuiStatic{
static const int Width = 20;
static const int Height = 25;
vector<TransientCardView*> cards;
vector<CardView*> cards;
public:
MTGGameZone * zone;

View File

@@ -10,7 +10,7 @@ using std::list;
class Player;
typedef enum { ORDER, FIRST_STRIKE, DAMAGE } CombatStep;
typedef enum { BLOCKERS, ORDER, FIRST_STRIKE, DAMAGE } CombatStep;
class Phase{
public:
int id;

View File

@@ -56,12 +56,9 @@ MTGCardInstance * AIPlayer::chooseCard(TargetChooser * tc, MTGCardInstance * sou
int AIPlayer::Act(float dt){
GameObserver * gameObs = GameObserver::GetInstance();
if (gameObs->currentPlayer == this){
if (gameObs->currentPlayer == this)
gameObs->userRequestNextGamePhase();
return 1;
}else{
return 1;
}
return 1;
}
@@ -498,14 +495,14 @@ int AIPlayer::chooseBlockers(){
}
int AIPlayer::orderBlockers(){
/*
GameObserver * g = GameObserver::GetInstance();
DamageResolverLayer * drl = g->mLayers->combatLayer();
if (drl->orderingIsNeeded && g->currentPlayer==this){
drl->blockersOrderingDone(); //TODO clever rank of blockers
return 1;
}
*/
if (BLOCKERS == g->combatStep && g->currentPlayer==this)
{
g->userRequestNextGamePhase(); //TODO clever rank of blockers
return 1;
}
return 0;
}
@@ -716,6 +713,7 @@ int AIPlayerBaka::computeActions(){
break;
}
}else{
cout << "my turn" << endl;
switch(currentGamePhase){
case Constants::MTG_PHASE_COMBATBLOCKERS:
chooseBlockers();

View File

@@ -76,7 +76,7 @@ void CardGui::Render()
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE * 0.5 * actZ);
mFont->DrawString(card->getName().c_str(), actX - actZ * Width / 2 + 1, actY - actZ * Height / 2 + 1);
if (icon) { icon->SetColor(ARGB(static_cast<unsigned char>(actA),255,255,255)); renderer->RenderQuad(icon, actX, actY, 0); }
if (tc && !tc->canTarget(card)) renderer->FillRect(actX - actZ*Width/2, actY - actZ*Height/2, actZ*Width, actZ*Height, ARGB(200,0,0,0));
if (tc && !tc->canTarget(card)) renderer->FillRect(actX - actZ*Width/2, actY - actZ*Height/2, actZ*Width, actZ*Height, ARGB((static_cast<unsigned char>(actA*0.75)),0,0,0));
mFont->SetScale(DEFAULT_MAIN_FONT_SCALE);
}

View File

@@ -93,7 +93,7 @@ ostream& Damage::toString(ostream& out) const
return out;
}
DamageStack::DamageStack(GameObserver* game) : game(game){
DamageStack::DamageStack() {
currentState = -1;
type = ACTION_DAMAGES;
}
@@ -130,33 +130,3 @@ ostream& DamageStack::toString(ostream& out) const
{
return (out << "DamageStack ::: currentState : " << currentState);
}
int StableDamageStack::resolve()
{
for (vector<Damage*>::iterator it = damage.begin(); it != damage.end(); ++it)
if ((*it)->state == NOT_RESOLVED) (*it)->resolve();
for (vector<Damage*>::iterator it = damage.begin(); it != damage.end(); ++it)
if ((*it)->state == RESOLVED_OK) (*it)->target->afterDamage();
return 1;
}
void StableDamageStack::Render(){
int currenty = y;
for (vector<Damage*>::iterator it = damage.begin(); it != damage.end(); ++it)
{
if ((*it)->state == NOT_RESOLVED){
(*it)->x = x;
(*it)->y = currenty;
currenty += (*it)->mHeight;
(*it)->Render();
}
}
}
void StableDamageStack::Add(Damage* d) { damage.push_back(d); }
ostream& StableDamageStack::toString(ostream& out) const
{
return (out << "StableDamageStack ::: size : " << damage.size());
}

View File

@@ -56,6 +56,7 @@ void DamagerDamaged::Render(CombatStep mode)
switch (mode)
{
case BLOCKERS :
case ORDER :
mFont->SetColor(ARGB(92,255,255,255));
break;

View File

@@ -112,9 +112,9 @@ void DuelLayers::Render(){
int DuelLayers::receiveEvent(WEvent * e){
#if 0
#if 1
#define PRINT_IF(type) { type *foo = dynamic_cast<type*>(e); if (foo) cout << "Is a " << #type << endl; }
cout << "Received event " << e << endl;
cout << "Received event " << e << " ";
PRINT_IF(WEventZoneChange);
PRINT_IF(WEventDamage);
PRINT_IF(WEventPhaseChange);

View File

@@ -51,8 +51,7 @@ GameObserver::GameObserver(Player * _players[], int _nb_players){
gameOver = NULL;
phaseRing = NEW PhaseRing(_players,_nb_players);
replacementEffects = NEW ReplacementEffects();
blockersSorted = false;
blockersAssigned = 0;
combatStep = BLOCKERS;
}
void GameObserver::setGamePhaseManager(MTGGamePhase * _phases){
@@ -79,23 +78,12 @@ void GameObserver::nextPlayer(){
currentPlayerId = (currentPlayerId+1)%nbPlayers;
currentPlayer = players[currentPlayerId];
currentActionPlayer = currentPlayer;
blockersSorted = false;
blockersAssigned = 0;
combatStep = BLOCKERS;
}
void GameObserver::nextGamePhase(){
Phase * cPhaseOld = phaseRing->getCurrentPhase();
if (!blockersSorted && cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS){
blockersAssigned = 1;
receiveEvent(NEW WEventCombatStepChange(ORDER));
/*
if (!mLayers->combatLayer()->autoOrderBlockers()){
OutputDebugString("Player has To choose ordering!");
return;
}
*/
}
if (cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS)
if (DAMAGE != combatStep) { nextCombatStep(); return; }
phaseRing->forward();
Phase * cPhase = phaseRing->getCurrentPhase();
currentGamePhase = cPhase->id;
@@ -107,6 +95,7 @@ void GameObserver::nextGamePhase(){
cleanupPhase();
currentPlayer->canPutLandsIntoPlay = 1;
mLayers->actionLayer()->Update(0);
combatStep = BLOCKERS;
return nextGamePhase();
}
@@ -148,28 +137,30 @@ int GameObserver::cancelCurrentAction(){
return 1;
}
void GameObserver::nextCombatStep()
{
switch (combatStep)
{
case BLOCKERS : receiveEvent(NEW WEventCombatStepChange(combatStep = ORDER)); return;
case ORDER : receiveEvent(NEW WEventCombatStepChange(combatStep = FIRST_STRIKE)); return;
case FIRST_STRIKE : receiveEvent(NEW WEventCombatStepChange(combatStep = DAMAGE)); return;
case DAMAGE : ; // Nothing : go to next phase
}
}
void GameObserver::userRequestNextGamePhase(){
if (mLayers->stackLayer()->getNext(NULL,0,NOT_RESOLVED)) return;
if (getCurrentTargetChooser()) return;
// if (mLayers->combatLayer()->isDisplayed()) return;
Phase * cPhaseOld = phaseRing->getCurrentPhase();
if (!blockersSorted && cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS){
blockersAssigned = 1;
receiveEvent(NEW WEventCombatStepChange(ORDER));
/*
if (!mLayers->combatLayer()->autoOrderBlockers()){
OutputDebugString("Player has To choose ordering!");
return;
}
*/
}
if (cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS)
if (DAMAGE != combatStep) { nextCombatStep(); return; }
if (cPhaseOld->id == Constants::MTG_PHASE_COMBATBLOCKERS ||
opponent()->isAI() ||
options[GameOptions::phaseInterrupts[currentGamePhase]].number){
options[GameOptions::phaseInterrupts[currentGamePhase]].number)
mLayers->stackLayer()->AddNextGamePhase();
}else{
else
nextGamePhase();
}
}
int GameObserver::forceShuffleLibraries(){
@@ -238,15 +229,15 @@ void GameObserver::addObserver(MTGAbility * observer){
void GameObserver::removeObserver(ActionElement * observer){
if (observer){
if (mLayers->actionLayer()->getIndexOf(observer) != -1){
observer->destroy();
mLayers->actionLayer()->Remove(observer);
if (observer)
{
if (mLayers->actionLayer()->getIndexOf(observer) != -1){
observer->destroy();
mLayers->actionLayer()->Remove(observer);
}
}
}else{
//TODO log error
}
else
{} //TODO log error
}
GameObserver::~GameObserver(){
@@ -256,17 +247,10 @@ GameObserver::~GameObserver(){
SAFE_DELETE(phaseRing);
SAFE_DELETE(replacementEffects);
LOG("==GameObserver Destroyed==");
}
void GameObserver::Update(float dt){
Player * player = currentPlayer;
if (currentGamePhase == Constants::MTG_PHASE_COMBATBLOCKERS && !blockersSorted){
player = opponent();
}else if (currentGamePhase == Constants::MTG_PHASE_COMBATDAMAGE){
// DamageResolverLayer * drl = mLayers->combatLayer();
// if (drl->currentChoosingPlayer && drl->mCount) player = drl->currentChoosingPlayer;
}
currentActionPlayer = player;
if (isInterrupting) player = isInterrupting;
mLayers->Update(dt,player);
@@ -275,13 +259,12 @@ void GameObserver::Update(float dt){
}
stateEffects();
oldGamePhase = currentGamePhase;
}
//applies damage to creatures after updates
//Players life test
void GameObserver::stateEffects(){
void GameObserver::stateEffects()
{
if (mLayers->stackLayer()->count(0,NOT_RESOLVED) != 0) return;
if (mLayers->actionLayer()->menuObject) return;
if (targetChooser || mLayers->actionLayer()->isWaitingForAnswer()) return;
@@ -292,23 +275,18 @@ void GameObserver::stateEffects(){
card->afterDamage();
}
}
for (int i =0; i < 2; i++){
for (int i =0; i < 2; i++)
if (players[i]->life <= 0) gameOver = players[i];
}
}
void GameObserver::Render(){
void GameObserver::Render()
{
mLayers->Render();
if (targetChooser || mLayers->actionLayer()->isWaitingForAnswer()){
if (targetChooser || mLayers->actionLayer()->isWaitingForAnswer())
JRenderer::GetInstance()->DrawRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ARGB(255,255,0,0));
}
if (waitForExtraPayment){
if (waitForExtraPayment)
waitForExtraPayment->Render();
}
}

View File

@@ -22,8 +22,6 @@ GuiAvatars::GuiAvatars(CardSelector* cs) : cs(cs), active(NULL)
GuiAvatars::~GuiAvatars()
{
cs->Remove(self); cs->Remove(selfGraveyard); cs->Remove(selfLibrary);
cs->Remove(opponent); cs->Remove(opponentGraveyard); cs->Remove(opponentLibrary);
}
void GuiAvatars::Activate(PlayGuiObject* c)

View File

@@ -1,3 +1,4 @@
#include <assert.h>
#include "../include/config.h"
#include "../include/GameApp.h"
#include "../include/GuiCombat.h"
@@ -138,11 +139,14 @@ bool GuiCombat::CheckUserInput(u32 key)
cursor_pos = BLK;
}
else if (OK == cursor_pos)
switch (step)
{
case ORDER : go->receiveEvent(NEW WEventCombatStepChange(FIRST_STRIKE)); break;
case FIRST_STRIKE : resolve(); go->receiveEvent(NEW WEventCombatStepChange(DAMAGE)); break;
case DAMAGE : resolve(); cursor_pos = NONE; go->userRequestNextGamePhase(); break;
switch (step)
{
case BLOCKERS : assert(false); break; // that should not happen
case ORDER : go->receiveEvent(NEW WEventCombatStepChange(FIRST_STRIKE)); break;
case FIRST_STRIKE : resolve(); go->receiveEvent(NEW WEventCombatStepChange(DAMAGE)); break;
case DAMAGE : resolve(); cursor_pos = NONE; go->userRequestNextGamePhase(); break;
}
}
break;
case PSP_CTRL_TRIANGLE:
@@ -242,16 +246,20 @@ void GuiCombat::Render()
void GuiCombat::resolve()
{
StableDamageStack stack;
DamageStack* stack = NEW DamageStack();
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
{
for (vector<DefenserDamaged*>::iterator q = (*it)->blockers.begin(); q != (*it)->blockers.end(); ++q)
for (vector<Damage>::iterator d = (*q)->damages.begin(); d != (*q)->damages.end(); ++d)
stack.Add(&(*d));
if ((*it)->blockers.empty())
stack->Add(NEW Damage((*it)->card, go->opponent(), (*it)->card->stepPower(step)));
else
for (vector<DefenserDamaged*>::iterator q = (*it)->blockers.begin(); q != (*it)->blockers.end(); ++q)
for (vector<Damage>::iterator d = (*q)->damages.begin(); d != (*q)->damages.end(); ++d)
stack->Add(NEW Damage(*d));
for (vector<Damage>::iterator d = (*it)->damages.begin(); d != (*it)->damages.end(); ++d)
stack.Add(&(*d));
stack->Add(NEW Damage(*d));
}
stack.resolve();
go->mLayers->stackLayer()->Add(stack);
go->mLayers->stackLayer()->resolve(); // This will delete the damage stack which will in turn delete the Damage it contains
}
int GuiCombat::receiveEventPlus(WEvent* e)
@@ -306,7 +314,9 @@ int GuiCombat::receiveEventMinus(WEvent* e)
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
if ((*it)->card == event->card)
{
AttackerDamaged* d = *it;
attackers.erase(it);
SAFE_DELETE(d);
return 1;
}
return 1;
@@ -326,68 +336,77 @@ int GuiCombat::receiveEventMinus(WEvent* e)
}
return 0;
}
else if (WEventPhaseChange* event = dynamic_cast<WEventPhaseChange*>(e))
{
step = BLOCKERS;
}
else if (WEventCombatStepChange* event = dynamic_cast<WEventCombatStepChange*>(e))
switch (event->step)
{
case ORDER:
{
if (ORDER == step) return 0; // Why do I take this twice ? >.>
if (!go->currentPlayer->displayStack()) { go->receiveEvent(NEW WEventCombatStepChange(FIRST_STRIKE)); return 1; }
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
{
(*it)->show = (1 < (*it)->blockers.size());
autoaffectDamage(*it, DAMAGE);
}
active = NULL;
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
if ((*it)->show)
case BLOCKERS:
break;
case ORDER:
{
if (ORDER == step) return 0; // Why do I take this twice ? >.>
if (!go->currentPlayer->displayStack()) { go->nextCombatStep(); return 1; }
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
{
(*it)->y = 210;
(*it)->zoom = 2.2; (*it)->t = 0;
if (!active) active = *it;
(*it)->show = (1 < (*it)->blockers.size());
autoaffectDamage(*it, DAMAGE);
}
active = NULL;
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
if ((*it)->show)
{
(*it)->y = 210;
(*it)->zoom = 2.2; (*it)->t = 0;
if (!active) active = *it;
}
repos<AttackerDamaged>(attackers.begin(), attackers.end(), 0);
if (active)
{
active->zoom = 2.7; // We know there is at least one, so this cannot be NULL
activeAtk = static_cast<AttackerDamaged*>(active);
remaskBlkViews(NULL, static_cast<AttackerDamaged*>(active));
cursor_pos = ATK;
step = ORDER;
}
else
go->nextCombatStep();
return 1;
}
case FIRST_STRIKE:
step = FIRST_STRIKE;
for (inner_iterator attacker = attackers.begin(); attacker != attackers.end(); ++attacker)
if ((*attacker)->card->has(Constants::FIRSTSTRIKE) || (*attacker)->card->has(Constants::DOUBLESTRIKE)) goto DAMAGE;
cout << "FIRST STRIKE" << endl;
resolve();
go->nextCombatStep();
return 1;
case DAMAGE: DAMAGE:
step = event->step;
if (!go->currentPlayer->displayStack()) { cout << "DAMAGE" << endl; resolve(); go->userRequestNextGamePhase(); return 1; }
for (inner_iterator attacker = attackers.begin(); attacker != attackers.end(); ++attacker)
autoaffectDamage(*attacker, step);
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
(*it)->show = ((*it)->card->has(Constants::DOUBLESTRIKE) || ((*it)->card->has(Constants::FIRSTSTRIKE) ^ (DAMAGE == step))) && (1 < (*it)->blockers.size());
repos<AttackerDamaged>(attackers.begin(), attackers.end(), 0);
active = NULL;
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it) if ((*it)->show) { active = *it; break; }
if (active)
{
active->zoom = 2.7; // We know there is at least one, so this cannot be NULL
active->zoom = 2.7;
activeAtk = static_cast<AttackerDamaged*>(active);
remaskBlkViews(NULL, static_cast<AttackerDamaged*>(active));
cursor_pos = ATK;
step = ORDER;
}
else
go->receiveEvent(NEW WEventCombatStepChange(FIRST_STRIKE));
{
resolve();
if (FIRST_STRIKE == step) go->nextCombatStep();
else go->userRequestNextGamePhase();
}
return 1;
}
case FIRST_STRIKE:
for (inner_iterator attacker = attackers.begin(); attacker != attackers.end(); ++attacker)
if ((*attacker)->card->has(Constants::FIRSTSTRIKE) || (*attacker)->card->has(Constants::DOUBLESTRIKE)) goto DAMAGE;
go->receiveEvent(NEW WEventCombatStepChange(DAMAGE));
return 1;
case DAMAGE: DAMAGE:
if (!go->currentPlayer->displayStack()) { go->userRequestNextGamePhase(); return 1; }
step = event->step;
for (inner_iterator attacker = attackers.begin(); attacker != attackers.end(); ++attacker)
autoaffectDamage(*attacker, step);
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it)
(*it)->show = ((*it)->card->has(Constants::DOUBLESTRIKE) || ((*it)->card->has(Constants::FIRSTSTRIKE) ^ (DAMAGE == step))) && (1 < (*it)->blockers.size());
repos<AttackerDamaged>(attackers.begin(), attackers.end(), 0);
active = NULL;
for (inner_iterator it = attackers.begin(); it != attackers.end(); ++it) if ((*it)->show) { active = *it; break; }
if (active)
{
active->zoom = 2.7;
activeAtk = static_cast<AttackerDamaged*>(active);
remaskBlkViews(NULL, static_cast<AttackerDamaged*>(active));
cursor_pos = ATK;
}
else
{
resolve();
if (FIRST_STRIKE == step) go->receiveEvent(NEW WEventCombatStepChange(DAMAGE));
else go->userRequestNextGamePhase();
}
return 1;
}
return 0;
}

View File

@@ -219,6 +219,7 @@ int GuiPlay::receiveEventMinus(WEvent * e)
CardView* cv = *it;
cs->Remove(cv);
cards.erase(it);
cv->card->view = NULL;
delete cv;
Replace();
return 1;

View File

@@ -114,7 +114,7 @@ void GuiGameZone::Render(){
mFont->DrawString(buffer, actX, actY);
if (showCards) cd->Render();
for (vector<TransientCardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
for (vector<CardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
(*it)->Render();
PlayGuiObject::Render();
}
@@ -124,7 +124,7 @@ void GuiGameZone::ButtonPressed(int controllerId, int controlId){
}
void GuiGameZone::Update(float dt){
for (vector<TransientCardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
for (vector<CardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
(*it)->Update(dt);
if (showCards) cd->Update(dt);
PlayGuiObject::Update(dt);
@@ -137,9 +137,8 @@ GuiGameZone::GuiGameZone(float x, float y, bool hasFocus, MTGGameZone* zone, Gui
GuiGameZone::~GuiGameZone(){
if (cd) delete cd;
for (vector<TransientCardView*>::iterator it = cards.begin(); it != cards.end(); ++it){
for (vector<CardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
delete(*it);
}
}
ostream& GuiGameZone::toString(ostream& out) const
@@ -158,12 +157,11 @@ int GuiGraveyard::receiveEventPlus(WEvent* e)
if (WEventZoneChange* event = dynamic_cast<WEventZoneChange*>(e))
if (event->to == zone)
{
cout << "Goes to Graveyard " << event->card->view << endl;
TransientCardView* t;
CardView* t;
if (event->card->view)
t = NEW TransientCardView(event->card, *(event->card->view));
t = NEW CardView(event->card, *(event->card->view));
else
t = NEW TransientCardView(event->card, x, y);
t = NEW CardView(event->card, x, y);
t->x = x + Width / 2; t->y = y + Height / 2; t->zoom = 0.3; t->alpha = 0;
cards.push_back(t);
return 1;
@@ -175,10 +173,10 @@ int GuiGraveyard::receiveEventMinus(WEvent* e)
{
if (WEventZoneChange* event = dynamic_cast<WEventZoneChange*>(e))
if (event->from == zone)
for (vector<TransientCardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
for (vector<CardView*>::iterator it = cards.begin(); it != cards.end(); ++it)
if (event->card->previous == (*it)->card)
{
TransientCardView* cv = *it;
CardView* cv = *it;
cards.erase(it);
delete cv;
return 1;

View File

@@ -534,17 +534,10 @@ int MTGCardInstance::setDefenser(MTGCardInstance * opponent){
}
}
WEvent * e = NULL;
if (defenser != opponent){
e = NEW WEventCreatureBlocker(this, defenser, opponent);
}
if (defenser != opponent) e = NEW WEventCreatureBlocker(this, defenser, opponent);
defenser = opponent;
if (defenser){
defenser->blockers.push_back(this);
}
g->blockersSorted = false;
if (defenser) defenser->blockers.push_back(this);
if (e) g->receiveEvent(e);
//delete e;
return 1;
}