added new auto keys, this and thisforeach, functionallity similair to aslongas and foreach, but for properties of the card as opposed to cards on the field. More details in first comment.

This commit is contained in:
salmelo16
2010-03-28 02:21:25 +00:00
parent a2987f7b0e
commit a06980a197
17 changed files with 728 additions and 13 deletions

View File

@@ -2887,6 +2887,14 @@ power=1
toughness=1
[/card]
[card]
name=Barrin's Codex
auto=@each my upkeep:may counter(0/0,1,Page)
auto={4}{T}{S}:thisforeach(counter{0/0.1.Page}) draw:1 controller
text=At the beginning of your upkeep, you may put a page counter on Barrin's Codex. -- {4}, {T}, Sacrifice Barrin's Codex: Draw X cards, where X is the number of page counters on Barrin's Codex.
mana={4}
type=Artifact
[/card]
[card]
name=Basal Sliver
auto=lord(sliver) {S}:Add {B}{B}
text=All Slivers have "Sacrifice this permanent: Add {B}{B} to your mana pool."
@@ -3517,6 +3525,14 @@ mana={0}
type=Artifact
[/card]
[card]
name=Black Market
auto=@movedTo(creature|graveyard) from(battlefield):counter(0/0,1,Charge)
auto=@each my firstmain:thisforeach(counter{0/0.1.Charge}) add{B} controller
text=Whenever a creature is put into a graveyard from the battlefield, put a charge counter on Black Market. -- At the beginning of your precombat main phase, add Black to your mana pool for each charge counter on Black Market.
mana={3}{B}{B}
type=Enchantment
[/card]
[card]
name=Black Poplar Shaman
auto={2}{B}:regenerate target(treefolk)
text={2}{B}: Regenerate target Treefolk.
@@ -15974,6 +15990,17 @@ power=2
toughness=2
[/card]
[card]
name=Goblin Razerunners
auto={1}{R}{S(land|myBattlefield?)}:counter(1/1,1)
auto=@each my endofturn:may thisforeach(counter{1/1,1}) damage:1 target(player)
text={1}{R}, Sacrifice a land: Put a +1/+1 counter on Goblin Razerunners. -- At the beginning of your end step, you may have Goblin Razerunners deal damage equal to the number of +1/+1 counters on it to target player.
mana={2}{R}{R}
type=Creature
subtype=Goblin Warrior
power=3
toughness=4
[/card]
[card]
name=Goblin Replica
auto={3}{R}{S}:destroy target(artifact)
text={3}{R}, Sacrifice Goblin Replica: Destroy target artifact.
@@ -21233,6 +21260,21 @@ subtype=Human Knight
power=2
toughness=2
[/card]
####Preemptive addition for Rise of the Eldrazi and to test new counter functions.
[card]
name=Knight of Cliffhaven
auto={3}:counter(0/0,1,Level Up) asSorcery
auto=this(counter{0/0,1,Level Up}) flying
auto=this(counter{0/0,1,Level Up}) 0/1
auto=this(counter{0/0,4,Level Up}) vigilance
auto=this(counter{0/0,4,Level Up}) 2/1
text=Level up 3 -- [Level 1-3] Flying (2/3) -- [Level 4+] Flying, vigilance (4/4)
mana={1}{W}
type=Creature
subtype=Kor Knight
power=2
toughness=2
[/card]
[card]
name=Knight of Dawn
abilities=first strike
@@ -39958,6 +40000,17 @@ mana={2}{U}
type=Sorcery
[/card]
[card]
name=Thelon of Havenwood
auto={B}{G}:moveTo(exile) target(fungus|mygraveyard) && counter(0/0,1,Spore) all(fungus)
auto=lord(fungus) thisforeach(counter{0/0,1,Spore}) 1/1
text=Each Fungus creature gets +1/+1 for each spore counter on it. -- {B}{G}, Exile a Fungus card from a graveyard: Put a spore counter on each Fungus on the battlefield.
mana={G}{G}
type=Legendary Creature
subtype=Elf Druid
power=2
toughness=2
[/card]
[card]
name=Thelonite Druid
auto={1}{G}{T}{S(creature|myBattlefield)}:lord(forest|myBattlefield) becomes(Creature,2/3) ueot
text={1}{G}, {T}, Sacrifice a creature: Forests you control become 2/3 creatures until end of turn. They're still lands.

View File

@@ -31,6 +31,9 @@ generic/kicker.txt
generic/kicker2.txt
generic/landwalk.txt
generic/legendary.txt
###Level up tests disabled until knight of cliffhaven has an id
###generic/level_up.txt
###generic/level_up2.txt
generic/lord_counter.txt
generic/lord_counter_any.txt
generic/lifelink.txt
@@ -55,6 +58,7 @@ generic/summoning_sickness.txt
generic/targetController_life.txt
generic/targetController_life2.txt
generic/targetController_damage.txt
generic/thisforeach.txt
generic/tokens.txt
generic/tokens2.txt
generic/trample.txt
@@ -121,6 +125,7 @@ behemoth_sledge4.txt
behemoth_sledge5.txt
belligerent_hatchling.txt
benalish_knight.txt
black_market.txt
black_vise.txt
blessed_wine.txt
blinking_spirit.txt
@@ -246,6 +251,7 @@ goblin_lackey2.txt
goblin_lackey3.txt
goblin_lackey4.txt
goblin_offensive.txt
goblin_razerunners.txt
golgari_germination_i153.txt
gravedigger.txt
great_defender.txt
@@ -399,9 +405,10 @@ tanglesap.txt
telekinetic_sliver.txt
terror.txt
terror2.txt
thallid.txt
thellon_of_havenwood.txt
threaten.txt
throne_of_bone.txt
thallid.txt
titanic_ultimatum.txt
torture.txt
tranquil_domain.txt

View File

@@ -2,7 +2,7 @@
FIRSTMAIN
[PLAYER1]
hand:Aven Riftwatcher
manapool:{G}
manapool:{2}{W}
[PLAYER2]
[DO]
Aven Riftwatcher

View File

@@ -2,7 +2,7 @@
FIRSTMAIN
[PLAYER1]
hand:Aven Riftwatcher
manapool:{G}
manapool:{2}{W}
[PLAYER2]
[DO]
aven riftwatcher

View File

@@ -0,0 +1,21 @@
[init]
draw
[player1]
inplay:Brass Secretary,Heart Warden,Limestone Golem,Black Market
manapool:{6}
[player2]
[do]
Heart Warden
choice 1
Brass Secretary
Limestone Golem
p1
next
[assert]
firstmain
[player1]
inplay:Black Market
graveyard:Brass Secretary,Heart Warden,Limestone Golem
manapool:{B}{B}{B}
[player2]
[end]

View File

@@ -0,0 +1,35 @@
[init]
firstmain
[player1]
inplay:Knight of Cliffhaven
manapool:{12}
[player2]
inplay:island,forest,swamp,moat
hand:assassinate
[do]
Knight of Cliffhaven
Knight of Cliffhaven
Knight of Cliffhaven
Knight of Cliffhaven
next
next
Knight of Cliffhaven
eot
next
next
next
island
forest
swamp
assassinate
Knight of Cliffhaven
[assert]
firstmain
[player1]
inplay:Knight of Cliffhaven
[player2]
inplay:island,forest,swamp,moat
hand:assassinate
manapool:{G}{U}{B}
life:16
[end]

View File

@@ -0,0 +1,33 @@
[init]
firstmain
[player1]
inplay:Knight of Cliffhaven
manapool:{9}
[player2]
inplay:island,forest,swamp,moat
hand:assassinate
[do]
Knight of Cliffhaven
Knight of Cliffhaven
Knight of Cliffhaven
next
next
Knight of Cliffhaven
eot
next
next
next
island
forest
swamp
assassinate
Knight of Cliffhaven
[assert]
firstmain
[player1]
graveyard:Knight of Cliffhaven
[player2]
inplay:island,forest,swamp,moat
graveyard:assassinate
life:18
[end]

View File

@@ -0,0 +1,26 @@
[init]
untap
[player1]
inplay:Urza's Mine,Urza's Tower,Urza's Power Plant,Barrin's Codex
library:forest,mountain,island,swamp
[player2]
[do]
next
choice 0
eot
eot
next
choice 0
next
next
Urza's Mine
Urza's Power Plant
Barrin's Codex
[assert]
firstmain
[player1]
inplay:Urza's Mine,Urza's Tower,Urza's Power Plant
graveyard:Barrin's Codex
hand:forest,mountain,island,swamp
[player2]
[end]

View File

@@ -0,0 +1,26 @@
[init]
firstmain
[player1]
inplay:Goblin Razerunners,Forest,Island
manapool:{R}{R}{R}{R}
[player2]
[do]
goblin razerunners
forest
goblin razerunners
island
next
next
next
next
next
choice 0
p2
[assert]
endofturn
[player1]
inplay:Goblin Razerunners
graveyard:forest,island
[player2]
life:18
[end]

View File

@@ -0,0 +1,31 @@
[init]
untap
[player1]
inplay:Thelon Of Havenwood,Thallid,Deathspore Thallid,forest,swamp
graveyard:fungusaur
[player2]
[do]
next
next
next
forest
swamp
Thelon of Havenwood
fungusaur
next
next
Thallid
choice 0
Deathspore thallid
choice 0
next
next
next
next
[assert]
secondmain
[player1]
inplay:Thelon Of Havenwood,Thallid,Deathspore Thallid,forest,swamp
[player2]
life:14
[end]

View File

@@ -14,6 +14,7 @@
#include "WEvent.h"
#include "GuiStatic.h"
#include "GameObserver.h"
#include "ThisDescriptor.h"
#include <JGui.h>
#include <hge/hgeparticle.h>
@@ -1875,8 +1876,148 @@ class AForeach:public ListMaintainerAbility{
};
class AThis:public MTGAbility{
public:
MTGAbility * ability;
MTGAbility * a;
ThisDescriptor * td;
AThis(int _id, MTGCardInstance * _source, Damageable * _target, ThisDescriptor * _td, MTGAbility * ability):MTGAbility(_id, _source,_target),ability(ability){
td = _td;
ability->source = source;
ability->target = target;
a = NULL;
SAFE_DELETE(tc);
}
int removeFromGame(){
return removeAbilityFromGame();
}
int addToGame(){
return MTGAbility::addToGame();
}
void Update(float dt){
resolve();
}
int resolve(){
//TODO check if ability is oneShot ?
if (td->match(source) > 0){
addAbilityToGame();
}
if (ability->oneShot) a = NULL; //allows to call the effect several times
return 1;
}
int addAbilityToGame(){
if (a) return 0;
a = ability->clone();
if (a->oneShot){
a->resolve();
delete(a);
}else{
a->addToGame();
}
return 1;
}
int removeAbilityFromGame(){
if (!a) return 0;
game->removeObserver(a);
a = NULL;
return 1;
}
~AThis(){
if (!isClone) SAFE_DELETE(ability); SAFE_DELETE(td);
}
AThis * clone() const{
AThis * a = NEW AThis(*this);
a->isClone = 1;
return a;
}
};
class AThisForEach:public MTGAbility{
public:
MTGAbility * ability;
ThisDescriptor * td;
vector<MTGAbility *> abilities;
AThisForEach(int _id, MTGCardInstance * _source, Damageable * _target, ThisDescriptor * _td, MTGAbility * ability):MTGAbility(_id, _source,_target),ability(ability){
td = _td;
ability->source = source;
ability->target = target;
SAFE_DELETE(tc);
}
int removeFromGame(){
return removeAbilityFromGame();
}
int addToGame(){
return MTGAbility::addToGame();
}
void Update(float dt){
resolve();
}
int resolve(){
//TODO check if ability is oneShot ?
int matches = td->match(source);
if (matches > 0) {
if (abilities.size()){
removeAbilityFromGame();
}
for (int i = 0; i < matches; i++) {
addAbilityToGame();
}
}
return 1;
}
int addAbilityToGame(){
MTGAbility * a = ability->clone();
a->target = target;
if (a->oneShot){
a->resolve();
delete(a);
}else{
a->addToGame();
abilities.push_back(a);
//abilities[abilities.size()] = a;
}
return 1;
}
int removeAbilityFromGame(){
for (int i = abilities.size(); i > 0; i--){
game->removeObserver(abilities[i-1]);
}
abilities.clear();
return 1;
}
~AThisForEach(){
if (!isClone){
SAFE_DELETE(ability);
SAFE_DELETE(td);
}
if (abilities.size()){
removeAbilityFromGame();
}
}
AThisForEach * clone() const{
AThisForEach * a = NEW AThisForEach(*this);
a->isClone = 1;
return a;
}
};
class AADamager:public ActivatedAbilityTP{
public:

View File

@@ -0,0 +1,53 @@
/*
Filter-like system for determining if a card meats certain criteria, for this and thisforeach autos
*/
#ifndef _THISDESCRIPTOR_H_
#define _THISDESCRIPTOR_H_
#include "Counters.h"
#include "MTGGameZones.h"
#include "MTGCardInstance.h"
#include "CardDescriptor.h"
class ThisDescriptor{
public:
int comparisonMode;
int comparisonCriterion;
virtual int match(MTGCardInstance * card) = 0;
int matchValue(int value);
};
class ThisDescriptorFactory{
public:
ThisDescriptor * createThisDescriptor(string s);
};
class ThisCounter:public ThisDescriptor{
public:
Counter * counter;
virtual int match(MTGCardInstance * card);
ThisCounter(Counter * _counter);
ThisCounter(int power, int toughness, int nb, const char * name);
~ThisCounter();
};
class ThisCounterAny:public ThisDescriptor{
public:
virtual int match(MTGCardInstance *card);
ThisCounterAny(int nb);
};
class ThisPower:public ThisDescriptor{
public:
virtual int match(MTGCardInstance * card);
ThisPower(int power);
};
class ThisToughness:public ThisDescriptor{
public:
virtual int match(MTGCardInstance * card);
ThisToughness(int toughness);
};
#endif

View File

@@ -8,6 +8,7 @@
#include "../include/CardGui.h"
#include "../include/MTGDeck.h"
#include "../include/Translate.h"
#include "../include/ThisDescriptor.h"
int AbilityFactory::countCards(TargetChooser * tc, Player * player, int option){
@@ -38,8 +39,8 @@ int AbilityFactory::countCards(TargetChooser * tc, Player * player, int option){
Counter * AbilityFactory::parseCounter(string s, MTGCardInstance * target) {
int nb = 1;
string name = "";
size_t start = s.find("(") + 1;
size_t end = s.find(")", start);
size_t start = 0;
size_t end = s.length();
size_t separator = s.find(",", start);
if (separator == string::npos) separator = s.find(".", start);
if (separator != string::npos){
@@ -362,11 +363,79 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
multi->oneShot=1;
return multi;
}
//Lord, foreach, aslongas
string lords[] = {"lord(","foreach(", "aslongas(", "all("};
//rather dirty way to stop thises and lords from conflicting with each other.
string prelords[] = {"foreach(","lord(","aslongas(", "all("};
size_t lord = string::npos;
for (int j = 0; j < 4; ++j){
size_t found2 = s.find(prelords[j]);
if (found2!=string::npos && ((found == string::npos) || found2 < found)){
lord = found2;
}
}
//This, ThisForEach;
string thises[] = {"this(","thisforeach("};
found = string::npos;
int i = -1;
for (int j = 0; j < 2; ++j){
size_t found2 = s.find(thises[j]);
if (found2!=string::npos && ((found == string::npos) || found2 < found)){
found = found2;
i = j;
}
}
if (found != string::npos && found < lord) {
size_t header = thises[i].size();
size_t end = s.find(")", found+header);
string s1;
if (found == 0 || end != s.size()-1){
s1 = s.substr(end+1);
}else{
s1 = s.substr(0, found);
}
if (end != string::npos){
string thisDescriptorString = s.substr(found+header,end-found-header);
ThisDescriptorFactory tdf;
ThisDescriptor * td = tdf.createThisDescriptor(thisDescriptorString);
if (!td){
OutputDebugString("MTGABILITY: Parsing Error:");
OutputDebugString(s.c_str());
OutputDebugString("\n");
return NULL;
}
MTGAbility * a = parseMagicLine(s1,id,spell, card,0,activated);
if (!a){
SAFE_DELETE(td);
return NULL;
}
MTGAbility * result = NULL;
int oneShot = 0;
if (activated) oneShot = 1;
if (card->hasType("sorcery") || card->hasType("instant")) oneShot = 1;
if (a->oneShot) oneShot = 1;
Damageable * _target = NULL;
if (spell) _target = spell->getNextDamageableTarget();
if (!_target) _target = target;
switch(i){
case 0: result = NEW AThis(id, card, _target, td, a); break;
case 1: result = NEW AThisForEach(id, card, _target, td, a); break;
default: result = NULL;
}
if (result) result->oneShot = oneShot;
return result;
}
return NULL;
}
//Lord, foreach, aslongas
string lords[] = {"lord(","foreach(","aslongas(", "all("};
found = string::npos;
i = -1;
for (int j = 0; j < 4; ++j){
size_t found2 = s.find(lords[j]);
if (found2!=string::npos && ((found == string::npos) || found2 < found)){
@@ -451,6 +520,8 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
SAFE_DELETE(tc);
//Cycling
found = s.find("cycling");
if (found != string::npos){
@@ -730,7 +801,7 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
if (found != string::npos){
size_t start = s.find("(");
size_t end = s.find(")");
string counterString = s.substr(start,end-start+1);
string counterString = s.substr(start+1,end-start-1);
Counter * counter = parseCounter(counterString,target);
if (counter){
MTGAbility * a = NEW AACounter(id,card,target,counter->name.c_str(),counter->power,counter->toughness,counter->nb);

View File

@@ -71,7 +71,7 @@ ManaCost * ManaCost::parseManaCost(string s, ManaCost * _manaCost, MTGCardInstan
size_t counter_start = value.find("(");
size_t counter_end = value.find(")", counter_start);
AbilityFactory * abf = NEW AbilityFactory();
string counterString = value.substr(counter_start,counter_end-counter_start);
string counterString = value.substr(counter_start+1,counter_end-counter_start-1);
Counter * counter = abf->parseCounter(counterString,c);
size_t separator = value.find(",",counter_start);
size_t separator2 = string::npos;

View File

@@ -204,9 +204,7 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
}else{
size_t start = attribute.find("{");
size_t end = attribute.find("}");
string counterString = attribute.substr(start,end-start+1);
counterString.replace(0,1,"(");
counterString.replace(counterString.length() - 1,1,")");
string counterString = attribute.substr(start+1,end-start-1);
AbilityFactory * abf = NEW AbilityFactory();
Counter * counter = abf->parseCounter(counterString,card);
if (counter) {

View File

@@ -0,0 +1,212 @@
#include "../include/config.h"
#include "../include/ThisDescriptor.h"
#include "../include/Counters.h"
#include "../include/MTGCardInstance.h"
#include "../include/CardDescriptor.h"
//Returns the amount by which a value passes the comparison.
int ThisDescriptor::matchValue(int value){
switch (comparisonMode){
case COMPARISON_AT_MOST:
return (comparisonCriterion - value + 1);
case COMPARISON_AT_LEAST:
return (value - comparisonCriterion + 1);
case COMPARISON_EQUAL:
return (comparisonCriterion == value);
case COMPARISON_GREATER:
return (value - comparisonCriterion);
case COMPARISON_LESS:
return (comparisonCriterion - value);
case COMPARISON_UNEQUAL:
return (comparisonCriterion != value);
}
return 0;
}
ThisDescriptor * ThisDescriptorFactory::createThisDescriptor(string s){
size_t found;
string whitespaces (" \t\f\v\n\r");
found=s.find_last_not_of(whitespaces);
if (found!=string::npos)
s.erase(found+1);
else return NULL;
found=s.find_first_not_of(whitespaces);
if (found!=string::npos)
s.erase(0,found);
else return NULL;
//set comparison mode
//equal, greater, and less must remain above the others, otherwise the others may never be used.
int mode = 0;
size_t found2 = string::npos;
int opLength = 0;
found = s.find("=");
if (found != string::npos){
mode = COMPARISON_EQUAL;
found2 = found + 1;
opLength = 1;
}
found = s.find(">");
if (found != string::npos){
mode = COMPARISON_GREATER;
found2 = found + 1;
opLength = 1;
}
found = s.find("<");
if (found != string::npos){
mode = COMPARISON_LESS;
found2 = found + 1;
opLength = 1;
}
found = s.find("<=");
if (found != string::npos){
mode = COMPARISON_AT_MOST;
found2 = found + 2;
opLength = 2;
}
found = s.find(">=");
if (found != string::npos){
mode = COMPARISON_AT_LEAST;
found2 = found + 2;
opLength = 2;
}
found = s.find("!=");
if (found != string::npos){
mode = COMPARISON_UNEQUAL;
found2 = found + 2;
opLength = 2;
}
if (!mode) mode = COMPARISON_AT_LEAST;
//get comparison criterion
int criterionFound = 0;
int criterion = 1;
if ((found2 != string::npos) && (found2 < s.length())){
criterion = atoi(s.substr(found2).c_str());
criterionFound = 1;
}
if (found2 != string::npos) s.erase(found2 - opLength);
//counters
found = s.find("counter{");
if (found != string::npos) {
size_t start = s.find("{");
size_t end = s.find("}");
string counterString = s.substr(start+1,end-start-1);
AbilityFactory * abf = NEW AbilityFactory();
Counter * counter = abf->parseCounter(counterString,NULL);
if (counter) {
if (criterionFound) counter->nb = criterion;
ThisCounter * td = NEW ThisCounter(counter);
if (td) {
td->comparisonMode = mode;
return td;
}
}
return NULL;
}
//any counter
found = s.find("counters");
if (found != string::npos) {
ThisCounterAny * td = NEW ThisCounterAny(criterion);
if (td) {
td->comparisonMode = mode;
return td;
}
return NULL;
}
//power
found = s.find("power");
if (found != string::npos) {
ThisPower * td = NEW ThisPower(criterion);
if (td) {
td->comparisonMode = mode;
return td;
}
return NULL;
}
//toughness
found = s.find("toughness");
if (found != string::npos) {
ThisToughness * td = NEW ThisToughness(criterion);
if (td) {
td->comparisonMode = mode;
return td;
}
return NULL;
}
return NULL;
}
ThisCounter::ThisCounter(Counter * _counter){
counter = _counter;
comparisonCriterion = counter->nb;
}
ThisCounter::ThisCounter(int power, int toughness, int nb, const char * name){
counter = NEW Counter(NULL,name,power,toughness);
comparisonCriterion = nb;
}
int ThisCounter::match(MTGCardInstance * card){
Counter * targetCounter = card->counters->hasCounter(counter->name.c_str(),counter->power,counter->toughness);
if (targetCounter){
return matchValue(targetCounter->nb);
}else{
switch (comparisonMode) {
case COMPARISON_LESS:
return comparisonCriterion;
case COMPARISON_AT_MOST:
return comparisonCriterion + 1;
case COMPARISON_UNEQUAL:
if (comparisonCriterion) return 1;
else return 0;
case COMPARISON_EQUAL:
if (comparisonCriterion) return 0;
else return 1;
default :
return 0;
}
}
}
ThisCounter::~ThisCounter() {
SAFE_DELETE(counter);
}
ThisPower::ThisPower(int power){
comparisonCriterion = power;
}
int ThisPower::match(MTGCardInstance * card){
return matchValue(card->power);
}
ThisToughness::ThisToughness(int toughness){
comparisonCriterion = toughness;
}
int ThisToughness::match(MTGCardInstance * card){
return matchValue(card->toughness);
}
ThisCounterAny::ThisCounterAny(int nb){
comparisonCriterion = nb;
}
int ThisCounterAny::match(MTGCardInstance * card){
int result = 0;
for (int i = 0; i < card->counters->mCount; i++) {
result += card->counters->counters[i]->nb;
}
return matchValue(result);
}

View File

@@ -792,6 +792,10 @@
RelativePath=".\src\TextScroller.cpp"
>
</File>
<File
RelativePath=".\src\ThisDescriptor.cpp"
>
</File>
<File
RelativePath=".\src\Token.cpp"
>
@@ -1185,6 +1189,10 @@
RelativePath=".\include\TextScroller.h"
>
</File>
<File
RelativePath=".\include\ThisDescriptor.h"
>
</File>
<File
RelativePath=".\include\Token.h"
>