Psyringe - introducing new ability: cantBeBlockedBy(T). T can be any targez specification, like "wall", "creature[flying]", etc. See added cards for examples. Note: This ability currently has the same restrictions as "protection from(T)", i.e. it can't be used in activated abilities or instants/sorceries. It *can* be used for creatures,auras, and other continuous abilities (e.g. an enchantment which grants "unblockable by walls" to all rats should be possible, although I didn't test that). There are 47 cards which use the phrase "can't be blocked by", so we should be able to get another fair mount of cards out of that.

Notes to programmers:
1. This feature uses an awful lot of copy-pasta, using "protetcion from()" as a base. While I'm learning the architecture, it's easier for me to have a dedicated single-purpose piece of code to work with, than trying to create multi-purpose code. I'm aware that this isn't a very elegant approach though, and I hope to be able to refactor additions like this into multi-purpose code once I have a better understanding of the architecture as a whole.

2. Please check the questions I'll add in my next code comments, specifically about AI integration and activated abilities / instants / sorceries.

3. I did have a crash (with the debugger complaining about stack corruption around the cd variable) during testing. I wasn't able to reproduce it though. I did have to clean the solution in-between when I updated to the primitives system, so perhaps there was this cleaning solved whatever corruption was in my files. I'm mentioning the issue in case someone has an idea on where my code might be risky (I'm still probne to making beginners mistakes).
This commit is contained in:
Psyyringe
2009-12-28 18:19:17 +00:00
parent 365c854e90
commit 49536fce44
20 changed files with 266 additions and 76 deletions
+13
View File
@@ -1216,6 +1216,19 @@ type=Artifact
mana={4} mana={4}
[/card] [/card]
[card] [card]
text=Juggernaut attacks each turn if able. Juggernaut can't be blocked by Walls.
auto=cantbeblockedby(wall)
abilities=mustattack
id=135240
name=Juggernaut
rarity=U
type=Artifact Creature
mana={4}
power=5
subtype=Juggernaut
toughness=3
[/card]
[card]
text=Haste (This creature can attack and {T} as soon as it comes under your control.) {T}: Kamahl, Pit Fighter deals 3 damage to target creature or player. text=Haste (This creature can attack and {T} as soon as it comes under your control.) {T}: Kamahl, Pit Fighter deals 3 damage to target creature or player.
abilities=haste,legendary abilities=haste,legendary
auto={T}:Damage:3 target(creature,player) auto={T}:Damage:3 target(creature,player)
-14
View File
@@ -486,20 +486,6 @@ type=Instant
mana={1}{R} mana={1}{R}
[/card] [/card]
[card] [card]
text=Juggernaut attacks each turn if able. Juggernaut can't be blocked by Walls.
id=135240
name=Juggernaut
rarity=U
color=Artifact
type=Artifact Creature
mana={4}
power=5
abilities=mustattack
auto=cantbeblockedby(wall)
subtype=Juggernaut
toughness=3
[/card]
[card]
text=Karplusan Strider can't be the target of blue or black spells. text=Karplusan Strider can't be the target of blue or black spells.
id=129911 id=129911
name=Karplusan Strider name=Karplusan Strider
+12
View File
@@ -110,6 +110,18 @@ toughness=9
abilities=trample abilities=trample
[/card] [/card]
[card] [card]
id=107526
name=Gnat Alley Creeper
mana={2}{R}
type=Creature
subtype=Human Rogue
power=3
toughness=1
text=Gnat Alley Creeper can't be blocked by creatures with flying.
auto=cantbeblockedby(creature[flying])
rarity=U
[/card]
[card]
text={T}, Sacrifice Haazda Exonerator: Destroy target Aura. text={T}, Sacrifice Haazda Exonerator: Destroy target Aura.
id=107467 id=107467
name=Haazda Exonerator name=Haazda Exonerator
+1 -12
View File
@@ -1,6 +1,6 @@
[card] [card]
id=111192 id=111192
name=ÁEhermage's Touch name=AEthermage's Touch
mana={2}{W}{U} mana={2}{W}{U}
type=Instant type=Instant
text=Reveal the top four cards of your library. You may put a creature card from among them onto the battlefield. It has "At the beginning of your end step, return this creature to its owner's hand." Then put the rest of the cards revealed this way on the bottom of your library in any order. text=Reveal the top four cards of your library. You may put a creature card from among them onto the battlefield. It has "At the beginning of your end step, return this creature to its owner's hand." Then put the rest of the cards revealed this way on the bottom of your library in any order.
@@ -468,17 +468,6 @@ text={T}: Add {1} to your mana pool. {T}, Sacrifice Ghost Quarter: Destroy targe
rarity=U rarity=U
[/card] [/card]
[card] [card]
id=107526
name=Gnat Alley Creeper
mana={2}{R}
type=Creature
subtype=Human Rogue
power=3
toughness=1
text=Gnat Alley Creeper can't be blocked by creatures with flying.
rarity=U
[/card]
[card]
id=107542 id=107542
name=Gobhobbler Rats name=Gobhobbler Rats
mana={B}{R} mana={B}{R}
+9
View File
@@ -522,6 +522,15 @@ mana={0}
type=Artifact type=Artifact
[/card] [/card]
[card] [card]
id=1725
name=Tower of Coireall
mana={2}
type=Artifact
text={T}: Target creature can't be blocked by Walls this turn.
auto={T}:cantbeblockedby(wall) target(creature)
rarity=U
[/card]
[card]
text=Water Wurm gets +0/+1 as long as an opponent controls an Island. text=Water Wurm gets +0/+1 as long as an opponent controls an Island.
id=1764 id=1764
name=Water Wurm name=Water Wurm
-8
View File
@@ -553,14 +553,6 @@ text=At the beginning of your upkeep, The Fallen deals 1 damage to each opponent
rarity=U rarity=U
[/card] [/card]
[card] [card]
id=1725
name=Tower of Coireall
mana={2}
type=Artifact
text={T}: Target creature can't be blocked by Walls this turn.
rarity=U
[/card]
[card]
id=1779 id=1779
name=Tracker name=Tracker
mana={2}{G} mana={2}{G}
+1 -4
View File
@@ -231,10 +231,7 @@ subtype=Juggernaut
power=5 power=5
toughness=3 toughness=3
text=Juggernaut attacks each turn if able. Juggernaut can't be blocked by Walls. text=Juggernaut attacks each turn if able. Juggernaut can't be blocked by Walls.
# Following line is not entirely correct: Walls cannot target auto=cantbeblockedby(wall)
# Juggernaut although they should be able to. But it's hard to
# find a case where this difference actually matters.
auto=protection from(wall)
abilities=mustattack abilities=mustattack
rarity=U rarity=U
[/card] [/card]
+12
View File
@@ -133,6 +133,18 @@ power=2
toughness=2 toughness=2
[/card] [/card]
[card] [card]
id=110501
name=Dust Corona
mana={R}
type=Enchantment
subtype=Aura
text=Enchant creature -- Enchanted creature gets +2/+0 and can't be blocked by creatures with flying.
target=creature
auto=2/0
auto=cantbeblockedby(creatures[flying])
rarity=C
[/card]
[card]
text=Enchant creature You control enchanted creature. At the beginning of your upkeep, enchanted creature deals 1 damage to its owner. text=Enchant creature You control enchanted creature. At the beginning of your upkeep, enchanted creature deals 1 damage to its owner.
target=creature target=creature
auto=@each my upkeep:life:-1 opponent auto=@each my upkeep:life:-1 opponent
+1 -10
View File
@@ -11,7 +11,7 @@ rarity=R
[/card] [/card]
[card] [card]
id=124504 id=124504
name=ÁEher Membrane name=AEther Membrane
mana={1}{R}{R} mana={1}{R}{R}
type=Creature type=Creature
subtype=Wall subtype=Wall
@@ -278,15 +278,6 @@ text=Protection from green At the beginning of the end step, if Dunerider Outlaw
rarity=U rarity=U
[/card] [/card]
[card] [card]
id=110501
name=Dust Corona
mana={R}
type=Enchantment
subtype=Aura
text=Enchant creature Enchanted creature gets +2/+0 and can't be blocked by creatures with flying.
rarity=C
[/card]
[card]
id=124343 id=124343
name=Dust Elemental name=Dust Elemental
mana={2}{W}{W} mana={2}{W}{W}
+13
View File
@@ -1189,6 +1189,19 @@ mana={4}
type=Artifact type=Artifact
[/card] [/card]
[card] [card]
text=Juggernaut attacks each turn if able. Juggernaut can't be blocked by Walls.
auto=cantbeblockedby(wall)
abilities=mustattack
id=1120
name=Juggernaut
rarity=U
type=Artifact Creature
mana={4}
power=5
subtype=Juggernaut
toughness=3
[/card]
[card]
text=Target creature gains flying until end of turn. text=Target creature gains flying until end of turn.
target=creature target=creature
auto=flying auto=flying
-12
View File
@@ -195,18 +195,6 @@ type=Artifact
mana={4} mana={4}
[/card] [/card]
[card] [card]
text=Juggernaut attacks each turn if able. Juggernaut can't be blocked by Walls.
id=1120
name=Juggernaut
rarity=U
color=Artifact
type=Artifact Creature
mana={4}
power=5
subtype=Juggernaut
toughness=3
[/card]
[card]
text=You have no maximum hand size. If an effect causes you to discard a card, discard it, but you may put it on top of your library instead of into your graveyard. text=You have no maximum hand size. If an effect causes you to discard a card, discard it, but you may put it on top of your library instead of into your graveyard.
id=1122 id=1122
name=Library of Leng name=Library of Leng
+4
View File
@@ -178,6 +178,7 @@ drift_of_the_dead.txt
dromad_purebred.txt dromad_purebred.txt
dross_harvester.txt dross_harvester.txt
duskwalker.txt duskwalker.txt
dust_corona.txt
dwarven_warriors.txt dwarven_warriors.txt
ebony_horse.txt ebony_horse.txt
ekundu_cyclops1_i218.txt ekundu_cyclops1_i218.txt
@@ -219,6 +220,8 @@ giant_growth.txt
giant_growth2.txt giant_growth2.txt
glimpse_the_unthinkable.txt glimpse_the_unthinkable.txt
gnarled_effigy.txt gnarled_effigy.txt
gnat_alley_creeper1.txt
gnat_alley_creeper2.txt
goblin_balloon_brigade.txt goblin_balloon_brigade.txt
goblin_balloon_brigade2.txt goblin_balloon_brigade2.txt
goblin_king.txt goblin_king.txt
@@ -246,6 +249,7 @@ imaginary_pet.txt
immaculate_magistrate.txt immaculate_magistrate.txt
instill_energy_i166.txt instill_energy_i166.txt
jodahs_avenger.txt jodahs_avenger.txt
juggernaut.txt
jump.txt jump.txt
karns_touch_i233.txt karns_touch_i233.txt
keldon_warlord.txt keldon_warlord.txt
+29
View File
@@ -0,0 +1,29 @@
#NAME: Dust Corona
#DESC: checks an aura which grants
#DESC: a "can't be blocked by" ability
[INIT]
firstmain
[PLAYER1]
inplay:Raging Goblin
hand:Dust Corona
manapool:{R}
[PLAYER2]
inplay:Suntail Hawk
[DO]
Dust Corona
Raging Goblin
next
next
Raging Goblin
next
Suntail Hawk
next
next
[ASSERT]
combatend
[PLAYER1]
inplay:Raging Goblin,Dust Corona
[PLAYER2]
inplay:Suntail Hawk
life:17
[END]
@@ -0,0 +1,23 @@
#NAME: Gnat Alley Creeper
#DESC: checks the ability "can't be
#DESC: blocked by creatures with flying"
[INIT]
combatattackers
[PLAYER1]
inplay:Gnat Alley Creeper
[PLAYER2]
inplay:Suntail Hawk
[DO]
Gnat Alley Creeper
next
Suntail Hawk
next
next
[ASSERT]
combatend
[PLAYER1]
inplay:Gnat Alley Creeper
[PLAYER2]
inplay:Suntail Hawk
life:17
[END]
@@ -0,0 +1,23 @@
#NAME: Gnat Alley Creeper 2
#DESC: checks the ability "can't be
#DESC: blocked by creatures with flying"
#DESC: Pass 2: Does normal blocking work?
[INIT]
combatattackers
[PLAYER1]
inplay:Gnat Alley Creeper
[PLAYER2]
inplay:Raging Goblin
[DO]
Gnat Alley Creeper
next
Raging Goblin
next
next
[ASSERT]
combatend
[PLAYER1]
graveyard:Gnat Alley Creeper
[PLAYER2]
graveyard:Raging Goblin
[END]
+23
View File
@@ -0,0 +1,23 @@
#NAME: Juggernaut
#DESC: checks Juggernaut's ability
#DESC: "can't be blocked by walls"
[INIT]
combatattackers
[PLAYER1]
inplay:Juggernaut
[PLAYER2]
inplay:Wall of Stone
[DO]
Juggernaut
next
Wall of Stone
next
next
[ASSERT]
combatend
[PLAYER1]
inplay:Juggernaut
[PLAYER2]
inplay:Wall of Stone
life:15
[END]
+32
View File
@@ -1190,6 +1190,38 @@ class AProtectionFrom: public MTGAbility{
}; };
//Can't be blocked by...
class ACantBeBlockedBy: public MTGAbility{
public:
TargetChooser * fromTc;
ACantBeBlockedBy(int id, MTGCardInstance * _source, MTGCardInstance * _target, TargetChooser *fromTc):MTGAbility(id,_source,_target),fromTc(fromTc){
}
int addToGame(){
MTGCardInstance * _target = (MTGCardInstance *)target;
_target->addCantBeBlockedBy(fromTc);
return MTGAbility::addToGame();
}
int destroy(){
((MTGCardInstance *)target)->removeCantBeBlockedBy(fromTc);
return 1;
}
ACantBeBlockedBy * clone() const{
ACantBeBlockedBy * a = NEW ACantBeBlockedBy(*this);
a->isClone = 1;
return a;
}
~ACantBeBlockedBy(){
SAFE_DELETE(fromTc);
}
};
//Alteration of Power and Toughness (enchantments) //Alteration of Power and Toughness (enchantments)
class APowerToughnessModifier: public MTGAbility{ class APowerToughnessModifier: public MTGAbility{
public: public:
+5
View File
@@ -123,6 +123,11 @@ class MTGCardInstance: public CardPrimitive, public MTGCard, public Damageable {
int removeProtection(TargetChooser *tc, int erase = 0); int removeProtection(TargetChooser *tc, int erase = 0);
int protectedAgainst(MTGCardInstance * card); int protectedAgainst(MTGCardInstance * card);
vector<TargetChooser *>cantBeBlockedBys;
int addCantBeBlockedBy(TargetChooser * tc);
int removeCantBeBlockedBy(TargetChooser *tc, int erase = 0);
int cantBeBlockedBy(MTGCardInstance * card);
void copy(MTGCardInstance * card); void copy(MTGCardInstance * card);
void setUntapping(); void setUntapping();
+37 -16
View File
@@ -737,22 +737,6 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
return a; return a;
} }
//Gain/loose Ability
for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++){
found = s.find(Constants::MTGBasicAbilities[j]);
if (found == 0 || found == 1){
int modifier = 1;
if (found > 0 && s[found-1] == '-') modifier = 0;
if (!activated){
if(card->hasType("instant") || card->hasType("sorcery") || forceUEOT){
return NEW AInstantBasicAbilityModifierUntilEOT(id, card,target, j,modifier);
}
return NEW ABasicAbilityModifier(id, card,target, j,modifier);
}
return NEW ABasicAbilityAuraModifierUntilEOT(id, card,target, NULL,j,modifier);
}
}
//Protection from... //Protection from...
found = s.find("protection from("); found = s.find("protection from(");
if (found == 0){ if (found == 0){
@@ -771,6 +755,40 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
return NULL; //TODO return NULL; //TODO
} }
//Can't be blocked by...
found = s.find("cantbeblockedby(");
if (found == 0){
size_t end = s.find (")", found);
string targets = s.substr(found+16,end - found - 16);
TargetChooserFactory tcf;
TargetChooser * fromTc = tcf.createTargetChooser(targets, card);
if (!fromTc) return NULL;
//default target zone to opponentbattlefield here?
if (!activated){
if(card->hasType("instant") || card->hasType("sorcery") || forceUEOT){
return NULL; //TODO
}
return NEW ACantBeBlockedBy(id, card,target,fromTc);
}
return NULL; //TODO
}
//Gain/loose simple Ability
for (int j = 0; j < Constants::NB_BASIC_ABILITIES; j++){
found = s.find(Constants::MTGBasicAbilities[j]);
if (found == 0 || found == 1){
int modifier = 1;
if (found > 0 && s[found-1] == '-') modifier = 0;
if (!activated){
if(card->hasType("instant") || card->hasType("sorcery") || forceUEOT){
return NEW AInstantBasicAbilityModifierUntilEOT(id, card,target, j,modifier);
}
return NEW ABasicAbilityModifier(id, card,target, j,modifier);
}
return NEW ABasicAbilityAuraModifierUntilEOT(id, card,target, NULL,j,modifier);
}
}
//Untapper (Ley Druid...) //Untapper (Ley Druid...)
found = s.find("untap"); found = s.find("untap");
if (found != string::npos){ if (found != string::npos){
@@ -845,6 +863,9 @@ int AbilityFactory::abilityEfficiency(MTGAbility * a, Player * p, int mode, Targ
if (APowerToughnessModifier * abi = dynamic_cast<APowerToughnessModifier *>(a)) return (abi->wppt->power.getValue()>=0 && abi->wppt->toughness.getValue()>=0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD; if (APowerToughnessModifier * abi = dynamic_cast<APowerToughnessModifier *>(a)) return (abi->wppt->power.getValue()>=0 && abi->wppt->toughness.getValue()>=0) ? BAKA_EFFECT_GOOD : BAKA_EFFECT_BAD;
if (APowerToughnessModifierUntilEndOfTurn * abi = dynamic_cast<APowerToughnessModifierUntilEndOfTurn *>(a)) return abilityEfficiency(abi->ability, p, mode,tc); if (APowerToughnessModifierUntilEndOfTurn * abi = dynamic_cast<APowerToughnessModifierUntilEndOfTurn *>(a)) return abilityEfficiency(abi->ability, p, mode,tc);
if (dynamic_cast<ACantBeBlockedBy *>(a)) return BAKA_EFFECT_GOOD;
if (dynamic_cast<AProtectionFrom *>(a)) return BAKA_EFFECT_GOOD;
map<int,bool> badAbilities; map<int,bool> badAbilities;
badAbilities[Constants::CANTATTACK] = true; badAbilities[Constants::CANTATTACK] = true;
badAbilities[Constants::CANTBLOCK] = true; badAbilities[Constants::CANTBLOCK] = true;
+28
View File
@@ -353,6 +353,7 @@ int MTGCardInstance::canBlock(MTGCardInstance * opponent){
if (!opponent->isAttacker()) return 0; if (!opponent->isAttacker()) return 0;
// Comprehensive rule 502.7f : If a creature with protection attacks, it can't be blocked by creatures that have the stated quality. // Comprehensive rule 502.7f : If a creature with protection attacks, it can't be blocked by creatures that have the stated quality.
if (opponent->protectedAgainst(this)) return 0; if (opponent->protectedAgainst(this)) return 0;
if (opponent->cantBeBlockedBy(this)) return 0;
if (opponent->basicAbilities[Constants::UNBLOCKABLE]) return 0; if (opponent->basicAbilities[Constants::UNBLOCKABLE]) return 0;
if (opponent->basicAbilities[Constants::FEAR] && !(hasColor(Constants::MTG_COLOR_ARTIFACT) || hasColor(Constants::MTG_COLOR_BLACK))) return 0; if (opponent->basicAbilities[Constants::FEAR] && !(hasColor(Constants::MTG_COLOR_ARTIFACT) || hasColor(Constants::MTG_COLOR_BLACK))) return 0;
@@ -569,6 +570,33 @@ int MTGCardInstance::protectedAgainst(MTGCardInstance * card){
return 0; return 0;
} }
int MTGCardInstance::addCantBeBlockedBy(TargetChooser * tc){
cantBeBlockedBys.push_back(tc);
return cantBeBlockedBys.size();
}
int MTGCardInstance::removeCantBeBlockedBy(TargetChooser * tc, int erase){
for (size_t i = 0; i < cantBeBlockedBys.size() ; i++){
if (cantBeBlockedBys[i] == tc){
if (erase) delete (cantBeBlockedBys[i]);
cantBeBlockedBys.erase(cantBeBlockedBys.begin()+i);
return 1;
}
}
return 0;
}
int MTGCardInstance::cantBeBlockedBy(MTGCardInstance * card){
for (size_t i = 0; i < cantBeBlockedBys.size() ; i++){
if (cantBeBlockedBys[i]->canTarget(card))
return 1;
}
return 0;
}
/* Choose a sound sample to associate to that card */ /* Choose a sound sample to associate to that card */
JSample * MTGCardInstance::getSample(){ JSample * MTGCardInstance::getSample(){
JSample * js; JSample * js;