Erwan
- (Finally) adding "must" abilities. Usage is as natural as possible, check Aven cloudchaser in 10E.
This commit is contained in:
@@ -175,6 +175,19 @@ type=Sorcery
|
|||||||
mana={2}{B}
|
mana={2}{B}
|
||||||
[/card]
|
[/card]
|
||||||
[card]
|
[card]
|
||||||
|
text=Flying (This creature can't be blocked except by creatures with flying or reach.) When Aven Cloudchaser comes into play, destroy target enchantment.
|
||||||
|
id=129470
|
||||||
|
name=Aven Cloudchaser
|
||||||
|
auto=destroy target(enchantment)
|
||||||
|
rarity=C
|
||||||
|
color=White
|
||||||
|
type=Creature
|
||||||
|
mana={3}{W}
|
||||||
|
power=2
|
||||||
|
subtype=Bird Soldier
|
||||||
|
toughness=2
|
||||||
|
[/card]
|
||||||
|
[card]
|
||||||
text=Flying (This creature can't be blocked except by creatures with flying or reach.) When Aven Fisher is put into a graveyard from play, you may draw a card.
|
text=Flying (This creature can't be blocked except by creatures with flying or reach.) When Aven Fisher is put into a graveyard from play, you may draw a card.
|
||||||
abilities=flying
|
abilities=flying
|
||||||
auto=@movedTo(this|graveyard):draw:1
|
auto=@movedTo(this|graveyard):draw:1
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ generic/legendary.txt
|
|||||||
generic/lifelink.txt
|
generic/lifelink.txt
|
||||||
generic/m10_blockers.txt
|
generic/m10_blockers.txt
|
||||||
generic/m10_blockers2.txt
|
generic/m10_blockers2.txt
|
||||||
|
generic/must1.txt
|
||||||
|
generic/must2.txt
|
||||||
generic/nofizzle.txt
|
generic/nofizzle.txt
|
||||||
generic/persist.txt
|
generic/persist.txt
|
||||||
generic/persist2.txt
|
generic/persist2.txt
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#Test:"must" abilities
|
||||||
|
[INIT]
|
||||||
|
FIRSTMAIN
|
||||||
|
[PLAYER1]
|
||||||
|
manapool:{3}{W}
|
||||||
|
hand:Aven Cloudchaser
|
||||||
|
inplay:lifeforce
|
||||||
|
[PLAYER2]
|
||||||
|
[DO]
|
||||||
|
Aven cloudchaser
|
||||||
|
choice 0
|
||||||
|
lifeforce
|
||||||
|
[ASSERT]
|
||||||
|
FIRSTMAIN
|
||||||
|
[PLAYER1]
|
||||||
|
inplay:aven cloudchaser
|
||||||
|
graveyard:lifeforce
|
||||||
|
[PLAYER2]
|
||||||
|
[END]
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#Test:"must" abilities
|
||||||
|
[INIT]
|
||||||
|
FIRSTMAIN
|
||||||
|
[PLAYER1]
|
||||||
|
manapool:{3}{W}{1}
|
||||||
|
hand:Aven Cloudchaser,black vise
|
||||||
|
[PLAYER2]
|
||||||
|
[DO]
|
||||||
|
Aven cloudchaser
|
||||||
|
black vise
|
||||||
|
[ASSERT]
|
||||||
|
FIRSTMAIN
|
||||||
|
[PLAYER1]
|
||||||
|
inplay:aven cloudchaser,black vise
|
||||||
|
[PLAYER2]
|
||||||
|
[END]
|
||||||
@@ -37,7 +37,7 @@ class ActionLayer: public GuiLayer, public JGuiListener{
|
|||||||
int reactToClick(ActionElement * ability,MTGCardInstance * card);
|
int reactToClick(ActionElement * ability,MTGCardInstance * card);
|
||||||
int reactToTargetClick(ActionElement * ability,Targetable * card);
|
int reactToTargetClick(ActionElement * ability,Targetable * card);
|
||||||
int stillInUse(MTGCardInstance * card);
|
int stillInUse(MTGCardInstance * card);
|
||||||
void setMenuObject(Targetable * object);
|
void setMenuObject(Targetable * object, bool must = false);
|
||||||
void ButtonPressed(int controllerid, int controlid);
|
void ButtonPressed(int controllerid, int controlid);
|
||||||
void doReactTo(int menuIndex);
|
void doReactTo(int menuIndex);
|
||||||
TargetChooser * getCurrentTargetChooser();
|
TargetChooser * getCurrentTargetChooser();
|
||||||
@@ -47,6 +47,7 @@ class ActionLayer: public GuiLayer, public JGuiListener{
|
|||||||
int cleanGarbage();
|
int cleanGarbage();
|
||||||
protected:
|
protected:
|
||||||
ActionElement * currentWaitingAction;
|
ActionElement * currentWaitingAction;
|
||||||
|
int cantCancel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -267,9 +267,10 @@ class AAFizzler:public ActivatedAbility{
|
|||||||
class MayAbility:public MTGAbility{
|
class MayAbility:public MTGAbility{
|
||||||
public:
|
public:
|
||||||
int triggered;
|
int triggered;
|
||||||
|
bool must;
|
||||||
MTGAbility * ability;
|
MTGAbility * ability;
|
||||||
MTGAbility * mClone;
|
MTGAbility * mClone;
|
||||||
MayAbility(int _id, MTGAbility * _ability, MTGCardInstance * _source):MTGAbility(_id,_source),ability(_ability){
|
MayAbility(int _id, MTGAbility * _ability, MTGCardInstance * _source, bool must = false):MTGAbility(_id,_source),must(must),ability(_ability){
|
||||||
triggered = 0;
|
triggered = 0;
|
||||||
mClone = NULL;
|
mClone = NULL;
|
||||||
}
|
}
|
||||||
@@ -279,7 +280,10 @@ public:
|
|||||||
MTGAbility::Update(dt);
|
MTGAbility::Update(dt);
|
||||||
if (!triggered){
|
if (!triggered){
|
||||||
triggered = 1;
|
triggered = 1;
|
||||||
game->mLayers->actionLayer()->setMenuObject(source);
|
if (TargetAbility * ta = dynamic_cast<TargetAbility *>(ability)){
|
||||||
|
if (!ta->tc->validTargetsExist()) return;
|
||||||
|
}
|
||||||
|
game->mLayers->actionLayer()->setMenuObject(source,must);
|
||||||
game->mLayers->stackLayer()->setIsInterrupting(source->controller());
|
game->mLayers->stackLayer()->setIsInterrupting(source->controller());
|
||||||
OutputDebugString("ALLABILITIES SetMenuObject!\n");
|
OutputDebugString("ALLABILITIES SetMenuObject!\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class TargetChooser: public TargetsList {
|
|||||||
MTGCardInstance * targetter; //Optional, usually equals source, used for protection from...
|
MTGCardInstance * targetter; //Optional, usually equals source, used for protection from...
|
||||||
|
|
||||||
int maxtargets; //Set to -1 for "unlimited"
|
int maxtargets; //Set to -1 for "unlimited"
|
||||||
|
bool validTargetsExist();
|
||||||
virtual int setAllZones(){return 0;}
|
virtual int setAllZones(){return 0;}
|
||||||
virtual bool targetsZone(MTGGameZone * z){return false;};
|
virtual bool targetsZone(MTGGameZone * z){return false;};
|
||||||
int ForceTargetListReady();
|
int ForceTargetListReady();
|
||||||
|
|||||||
@@ -94,6 +94,14 @@ void ActionLayer::Update(float dt){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cantCancel){
|
||||||
|
ActionElement * ae = isWaitingForAnswer();
|
||||||
|
if (ae && !ae->tc->validTargetsExist()) {
|
||||||
|
cantCancel = 0;
|
||||||
|
cancelCurrentAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionLayer::Render (){
|
void ActionLayer::Render (){
|
||||||
@@ -113,6 +121,7 @@ void ActionLayer::Render (){
|
|||||||
void ActionLayer::setCurrentWaitingAction(ActionElement * ae){
|
void ActionLayer::setCurrentWaitingAction(ActionElement * ae){
|
||||||
assert(!ae || !currentWaitingAction);
|
assert(!ae || !currentWaitingAction);
|
||||||
currentWaitingAction = ae;
|
currentWaitingAction = ae;
|
||||||
|
if (!ae) cantCancel = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetChooser * ActionLayer::getCurrentTargetChooser(){
|
TargetChooser * ActionLayer::getCurrentTargetChooser(){
|
||||||
@@ -124,6 +133,7 @@ TargetChooser * ActionLayer::getCurrentTargetChooser(){
|
|||||||
int ActionLayer::cancelCurrentAction(){
|
int ActionLayer::cancelCurrentAction(){
|
||||||
ActionElement * ae = isWaitingForAnswer();
|
ActionElement * ae = isWaitingForAnswer();
|
||||||
if (!ae) return 0;
|
if (!ae) return 0;
|
||||||
|
if (cantCancel && ae->tc->validTargetsExist()) return 0;
|
||||||
ae->waitingForAnswer = 0; //TODO MOVE THIS IN ActionElement
|
ae->waitingForAnswer = 0; //TODO MOVE THIS IN ActionElement
|
||||||
setCurrentWaitingAction(NULL);
|
setCurrentWaitingAction(NULL);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -206,12 +216,12 @@ int ActionLayer::reactToClick(MTGCardInstance * card){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ActionLayer::setMenuObject(Targetable * object){
|
void ActionLayer::setMenuObject(Targetable * object, bool must){
|
||||||
menuObject = object;
|
menuObject = object;
|
||||||
|
|
||||||
SAFE_DELETE(abilitiesMenu);
|
SAFE_DELETE(abilitiesMenu);
|
||||||
|
|
||||||
abilitiesMenu = NEW SimpleMenu(10, this, Constants::MAIN_FONT, 100, 100);
|
abilitiesMenu = NEW SimpleMenu(10, this, Constants::MAIN_FONT, 100, 100,object->getDisplayName().c_str());
|
||||||
|
|
||||||
for (int i=0;i<mCount;i++){
|
for (int i=0;i<mCount;i++){
|
||||||
ActionElement * currentAction = (ActionElement *)mObjects[i];
|
ActionElement * currentAction = (ActionElement *)mObjects[i];
|
||||||
@@ -219,7 +229,8 @@ void ActionLayer::setMenuObject(Targetable * object){
|
|||||||
abilitiesMenu->Add(i,currentAction->getMenuText());
|
abilitiesMenu->Add(i,currentAction->getMenuText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
abilitiesMenu->Add(-1, "Cancel");
|
if (!must) abilitiesMenu->Add(-1, "Cancel");
|
||||||
|
else cantCancel = 1;
|
||||||
modal = 1;
|
modal = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +263,7 @@ ActionLayer::ActionLayer(){
|
|||||||
abilitiesMenu = NULL;
|
abilitiesMenu = NULL;
|
||||||
stuffHappened = 0;
|
stuffHappened = 0;
|
||||||
currentWaitingAction = NULL;
|
currentWaitingAction = NULL;
|
||||||
|
cantCancel = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionLayer::~ActionLayer(){
|
ActionLayer::~ActionLayer(){
|
||||||
|
|||||||
@@ -206,17 +206,35 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
|||||||
|
|
||||||
int restrictions = parseRestriction(s);
|
int restrictions = parseRestriction(s);
|
||||||
|
|
||||||
size_t delimiter = s.find("}:");
|
TargetChooser * tc = NULL;
|
||||||
size_t firstNonSpace = s.find_first_not_of(" ");
|
string sWithoutTc = s;
|
||||||
if (delimiter!= string::npos && firstNonSpace !=string::npos && s[firstNonSpace] == '{'){
|
//Target Abilities
|
||||||
ManaCost * cost = ManaCost::parseManaCost(s.substr(0,delimiter+1),NULL,card);
|
found = s.find("target(");
|
||||||
|
if (found != string::npos){
|
||||||
|
int end = s.find(")", found);
|
||||||
|
string starget = s.substr(found + 7,end - found - 7);
|
||||||
|
TargetChooserFactory tcf;
|
||||||
|
tc = tcf.createTargetChooser(starget, card);
|
||||||
|
if (tc && s.find("notatarget(") != string::npos){
|
||||||
|
tc->targetter = NULL;
|
||||||
|
found = found - 4;
|
||||||
|
}
|
||||||
|
string temp = s.substr(0,found);
|
||||||
|
temp.append(s.substr(end+1));
|
||||||
|
sWithoutTc = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t delimiter = sWithoutTc.find("}:");
|
||||||
|
size_t firstNonSpace = sWithoutTc.find_first_not_of(" ");
|
||||||
|
if (delimiter!= string::npos && firstNonSpace !=string::npos && sWithoutTc[firstNonSpace] == '{'){
|
||||||
|
ManaCost * cost = ManaCost::parseManaCost(sWithoutTc.substr(0,delimiter+1),NULL,card);
|
||||||
if (doTap || cost){
|
if (doTap || cost){
|
||||||
string s1 = s.substr(delimiter+2);
|
string s1 = sWithoutTc.substr(delimiter+2);
|
||||||
|
|
||||||
MTGAbility * a = parseMagicLine(s1, id, spell, card, 1);
|
MTGAbility * a = parseMagicLine(s1, id, spell, card, 1);
|
||||||
if (!a){
|
if (!a){
|
||||||
OutputDebugString("ABILITYFACTORY Error parsing:");
|
OutputDebugString("ABILITYFACTORY Error parsing:");
|
||||||
OutputDebugString(s.c_str());
|
OutputDebugString(sWithoutTc.c_str());
|
||||||
OutputDebugString("\n");
|
OutputDebugString("\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -231,21 +249,12 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
|||||||
}
|
}
|
||||||
|
|
||||||
int limit = 0;
|
int limit = 0;
|
||||||
unsigned int limit_str = s.find("limit:");
|
unsigned int limit_str = sWithoutTc.find("limit:");
|
||||||
if (limit_str != string::npos){
|
if (limit_str != string::npos){
|
||||||
limit = atoi(s.substr(limit_str+6).c_str());
|
limit = atoi(sWithoutTc.substr(limit_str+6).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetChooser * tc = NULL;
|
|
||||||
//Target Abilities
|
|
||||||
found = s.find("target(");
|
|
||||||
if (found != string::npos){
|
|
||||||
int end = s.find(")", found);
|
|
||||||
string starget = s.substr(found + 7,end - found - 7);
|
|
||||||
TargetChooserFactory tcf;
|
|
||||||
tc = tcf.createTargetChooser(starget, card);
|
|
||||||
if (tc && s.find("notatarget(") != string::npos) tc->targetter = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
AEquip *ae = dynamic_cast<AEquip*>(a);
|
AEquip *ae = dynamic_cast<AEquip*>(a);
|
||||||
if (ae){
|
if (ae){
|
||||||
@@ -264,6 +273,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
|||||||
SAFE_DELETE(cost);
|
SAFE_DELETE(cost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//kicker cost
|
//kicker cost
|
||||||
found = s.find("kicker ");
|
found = s.find("kicker ");
|
||||||
if (found == 0){
|
if (found == 0){
|
||||||
@@ -276,19 +286,10 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
|||||||
//When...comes into play, you may...
|
//When...comes into play, you may...
|
||||||
found = s.find("may ");
|
found = s.find("may ");
|
||||||
if (found == 0){
|
if (found == 0){
|
||||||
string s1 = s.substr(found+4);
|
string s1 = sWithoutTc.substr(found+4);
|
||||||
MTGAbility * a1 = parseMagicLine(s1,id,spell, card);
|
MTGAbility * a1 = parseMagicLine(s1,id,spell, card);
|
||||||
if (!a1) return NULL;
|
if (!a1) return NULL;
|
||||||
TargetChooser * tc = NULL;
|
|
||||||
//Target Abilities
|
|
||||||
found = s.find("target(");
|
|
||||||
if (found != string::npos){
|
|
||||||
int end = s.find(")", found);
|
|
||||||
string starget = s.substr(found + 7,end - found - 7);
|
|
||||||
TargetChooserFactory tcf;
|
|
||||||
tc = tcf.createTargetChooser(starget, card);
|
|
||||||
if (tc && s.find("notatarget(") != string::npos) tc->targetter = NULL;
|
|
||||||
}
|
|
||||||
if (tc) a1 = NEW GenericTargetAbility(id, card, tc, a1);
|
if (tc) a1 = NEW GenericTargetAbility(id, card, tc, a1);
|
||||||
else a1 = NEW GenericActivatedAbility(id, card, a1,NULL);
|
else a1 = NEW GenericActivatedAbility(id, card, a1,NULL);
|
||||||
return NEW MayAbility(id,a1,card);
|
return NEW MayAbility(id,a1,card);
|
||||||
@@ -298,6 +299,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
|||||||
//Multiple abilities for ONE cost
|
//Multiple abilities for ONE cost
|
||||||
found = s.find("&&");
|
found = s.find("&&");
|
||||||
if (found != string::npos){
|
if (found != string::npos){
|
||||||
|
SAFE_DELETE(tc);
|
||||||
string s1 = s.substr(0,found);
|
string s1 = s.substr(0,found);
|
||||||
string s2 = s.substr(found+2);
|
string s2 = s.substr(found+2);
|
||||||
MultiAbility * multi = NEW MultiAbility(id, card,target,NULL,NULL);
|
MultiAbility * multi = NEW MultiAbility(id, card,target,NULL,NULL);
|
||||||
@@ -321,6 +323,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found != string::npos){
|
if (found != string::npos){
|
||||||
|
SAFE_DELETE(tc);
|
||||||
size_t header = lords[i].size();
|
size_t header = lords[i].size();
|
||||||
size_t end = s.find(")", found+header);
|
size_t end = s.find(")", found+header);
|
||||||
string s1;
|
string s1;
|
||||||
@@ -381,6 +384,21 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!activated && tc){
|
||||||
|
|
||||||
|
MTGAbility * a = parseMagicLine(sWithoutTc, id, spell, card);
|
||||||
|
if (!a){
|
||||||
|
OutputDebugString("ABILITYFACTORY Error parsing:");
|
||||||
|
OutputDebugString(s.c_str());
|
||||||
|
OutputDebugString("\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
a = NEW GenericTargetAbility(id,card,tc,a);
|
||||||
|
return NEW MayAbility(id,a,card,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SAFE_DELETE(tc);
|
||||||
|
|
||||||
//Cycling
|
//Cycling
|
||||||
found = s.find("cycling");
|
found = s.find("cycling");
|
||||||
if (found != string::npos){
|
if (found != string::npos){
|
||||||
@@ -472,7 +490,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Copy a target
|
//Copy a target
|
||||||
found = s.find("copy ");
|
found = s.find("copy");
|
||||||
if (found != string::npos){
|
if (found != string::npos){
|
||||||
MTGAbility * a = NEW AACopier(id,card,target);
|
MTGAbility * a = NEW AACopier(id,card,target);
|
||||||
a->oneShot = 1;
|
a->oneShot = 1;
|
||||||
|
|||||||
@@ -386,6 +386,23 @@ int TargetChooser::targetListSet(){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TargetChooser::validTargetsExist(){
|
||||||
|
for (int i = 0; i < 2; ++i){
|
||||||
|
Player *p = GameObserver::GetInstance()->players[i];
|
||||||
|
if (canTarget(p)) return true;
|
||||||
|
MTGGameZone * zones[] = {p->game->inPlay,p->game->graveyard,p->game->hand,p->game->library};
|
||||||
|
for (int k = 0; k < 4; k++){
|
||||||
|
MTGGameZone * z = zones[k];
|
||||||
|
if (targetsZone(z)){
|
||||||
|
for (int j = 0; j < z->nb_cards; j++){
|
||||||
|
if (canTarget(z->cards[j])) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
a specific Card
|
a specific Card
|
||||||
**/
|
**/
|
||||||
|
|||||||
Reference in New Issue
Block a user