- (Finally) adding "must" abilities. Usage is as natural as possible, check Aven cloudchaser in 10E.
This commit is contained in:
wagic.the.homebrew@gmail.com
2010-01-06 14:22:41 +00:00
parent 50169eb5fb
commit da07370243
10 changed files with 141 additions and 39 deletions

View File

@@ -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

View File

@@ -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

View 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]

View 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]

View File

@@ -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;
};

View File

@@ -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");
}

View File

@@ -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();

View File

@@ -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(){

View File

@@ -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;

View File

@@ -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
**/