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}
|
||||
[/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.
|
||||
abilities=flying
|
||||
auto=@movedTo(this|graveyard):draw:1
|
||||
|
||||
@@ -31,6 +31,8 @@ generic/legendary.txt
|
||||
generic/lifelink.txt
|
||||
generic/m10_blockers.txt
|
||||
generic/m10_blockers2.txt
|
||||
generic/must1.txt
|
||||
generic/must2.txt
|
||||
generic/nofizzle.txt
|
||||
generic/persist.txt
|
||||
generic/persist2.txt
|
||||
|
||||
19
projects/mtg/bin/Res/test/generic/must1.txt
Normal file
19
projects/mtg/bin/Res/test/generic/must1.txt
Normal file
@@ -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]
|
||||
16
projects/mtg/bin/Res/test/generic/must2.txt
Normal file
16
projects/mtg/bin/Res/test/generic/must2.txt
Normal file
@@ -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 reactToTargetClick(ActionElement * ability,Targetable * card);
|
||||
int stillInUse(MTGCardInstance * card);
|
||||
void setMenuObject(Targetable * object);
|
||||
void setMenuObject(Targetable * object, bool must = false);
|
||||
void ButtonPressed(int controllerid, int controlid);
|
||||
void doReactTo(int menuIndex);
|
||||
TargetChooser * getCurrentTargetChooser();
|
||||
@@ -47,6 +47,7 @@ class ActionLayer: public GuiLayer, public JGuiListener{
|
||||
int cleanGarbage();
|
||||
protected:
|
||||
ActionElement * currentWaitingAction;
|
||||
int cantCancel;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -267,9 +267,10 @@ class AAFizzler:public ActivatedAbility{
|
||||
class MayAbility:public MTGAbility{
|
||||
public:
|
||||
int triggered;
|
||||
bool must;
|
||||
MTGAbility * ability;
|
||||
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;
|
||||
mClone = NULL;
|
||||
}
|
||||
@@ -279,7 +280,10 @@ public:
|
||||
MTGAbility::Update(dt);
|
||||
if (!triggered){
|
||||
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());
|
||||
OutputDebugString("ALLABILITIES SetMenuObject!\n");
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class TargetChooser: public TargetsList {
|
||||
MTGCardInstance * targetter; //Optional, usually equals source, used for protection from...
|
||||
|
||||
int maxtargets; //Set to -1 for "unlimited"
|
||||
|
||||
bool validTargetsExist();
|
||||
virtual int setAllZones(){return 0;}
|
||||
virtual bool targetsZone(MTGGameZone * z){return false;};
|
||||
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 (){
|
||||
@@ -113,6 +121,7 @@ void ActionLayer::Render (){
|
||||
void ActionLayer::setCurrentWaitingAction(ActionElement * ae){
|
||||
assert(!ae || !currentWaitingAction);
|
||||
currentWaitingAction = ae;
|
||||
if (!ae) cantCancel = 0;
|
||||
}
|
||||
|
||||
TargetChooser * ActionLayer::getCurrentTargetChooser(){
|
||||
@@ -124,6 +133,7 @@ TargetChooser * ActionLayer::getCurrentTargetChooser(){
|
||||
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;
|
||||
@@ -206,12 +216,12 @@ int ActionLayer::reactToClick(MTGCardInstance * card){
|
||||
}
|
||||
|
||||
|
||||
void ActionLayer::setMenuObject(Targetable * object){
|
||||
void ActionLayer::setMenuObject(Targetable * object, bool must){
|
||||
menuObject = object;
|
||||
|
||||
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++){
|
||||
ActionElement * currentAction = (ActionElement *)mObjects[i];
|
||||
@@ -219,7 +229,8 @@ void ActionLayer::setMenuObject(Targetable * object){
|
||||
abilitiesMenu->Add(i,currentAction->getMenuText());
|
||||
}
|
||||
}
|
||||
abilitiesMenu->Add(-1, "Cancel");
|
||||
if (!must) abilitiesMenu->Add(-1, "Cancel");
|
||||
else cantCancel = 1;
|
||||
modal = 1;
|
||||
}
|
||||
|
||||
@@ -252,6 +263,7 @@ ActionLayer::ActionLayer(){
|
||||
abilitiesMenu = NULL;
|
||||
stuffHappened = 0;
|
||||
currentWaitingAction = NULL;
|
||||
cantCancel = 0;
|
||||
}
|
||||
|
||||
ActionLayer::~ActionLayer(){
|
||||
|
||||
@@ -206,17 +206,35 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
|
||||
int restrictions = parseRestriction(s);
|
||||
|
||||
size_t delimiter = s.find("}:");
|
||||
size_t firstNonSpace = s.find_first_not_of(" ");
|
||||
if (delimiter!= string::npos && firstNonSpace !=string::npos && s[firstNonSpace] == '{'){
|
||||
ManaCost * cost = ManaCost::parseManaCost(s.substr(0,delimiter+1),NULL,card);
|
||||
TargetChooser * tc = NULL;
|
||||
string sWithoutTc = s;
|
||||
//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;
|
||||
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){
|
||||
string s1 = s.substr(delimiter+2);
|
||||
string s1 = sWithoutTc.substr(delimiter+2);
|
||||
|
||||
MTGAbility * a = parseMagicLine(s1, id, spell, card, 1);
|
||||
if (!a){
|
||||
OutputDebugString("ABILITYFACTORY Error parsing:");
|
||||
OutputDebugString(s.c_str());
|
||||
OutputDebugString(sWithoutTc.c_str());
|
||||
OutputDebugString("\n");
|
||||
return NULL;
|
||||
}
|
||||
@@ -231,21 +249,12 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
}
|
||||
|
||||
int limit = 0;
|
||||
unsigned int limit_str = s.find("limit:");
|
||||
unsigned int limit_str = sWithoutTc.find("limit:");
|
||||
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);
|
||||
if (ae){
|
||||
@@ -263,7 +272,8 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
}
|
||||
SAFE_DELETE(cost);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//kicker cost
|
||||
found = s.find("kicker ");
|
||||
if (found == 0){
|
||||
@@ -276,28 +286,20 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
//When...comes into play, you may...
|
||||
found = s.find("may ");
|
||||
if (found == 0){
|
||||
string s1 = s.substr(found+4);
|
||||
string s1 = sWithoutTc.substr(found+4);
|
||||
MTGAbility * a1 = parseMagicLine(s1,id,spell, card);
|
||||
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);
|
||||
else a1 = NEW GenericActivatedAbility(id, card, a1,NULL);
|
||||
return NEW MayAbility(id,a1,card);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Multiple abilities for ONE cost
|
||||
found = s.find("&&");
|
||||
if (found != string::npos){
|
||||
SAFE_DELETE(tc);
|
||||
string s1 = s.substr(0,found);
|
||||
string s2 = s.substr(found+2);
|
||||
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){
|
||||
SAFE_DELETE(tc);
|
||||
size_t header = lords[i].size();
|
||||
size_t end = s.find(")", found+header);
|
||||
string s1;
|
||||
@@ -381,6 +384,21 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
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
|
||||
found = s.find("cycling");
|
||||
if (found != string::npos){
|
||||
@@ -472,7 +490,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
}
|
||||
|
||||
//Copy a target
|
||||
found = s.find("copy ");
|
||||
found = s.find("copy");
|
||||
if (found != string::npos){
|
||||
MTGAbility * a = NEW AACopier(id,card,target);
|
||||
a->oneShot = 1;
|
||||
|
||||
@@ -386,6 +386,23 @@ int TargetChooser::targetListSet(){
|
||||
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
|
||||
**/
|
||||
|
||||
Reference in New Issue
Block a user