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:
@@ -1216,6 +1216,19 @@ type=Artifact
|
||||
mana={4}
|
||||
[/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.
|
||||
abilities=haste,legendary
|
||||
auto={T}:Damage:3 target(creature,player)
|
||||
|
||||
@@ -486,20 +486,6 @@ type=Instant
|
||||
mana={1}{R}
|
||||
[/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.
|
||||
id=129911
|
||||
name=Karplusan Strider
|
||||
|
||||
@@ -110,6 +110,18 @@ toughness=9
|
||||
abilities=trample
|
||||
[/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.
|
||||
id=107467
|
||||
name=Haazda Exonerator
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[card]
|
||||
id=111192
|
||||
name=ÁEhermage's Touch
|
||||
name=AEthermage's Touch
|
||||
mana={2}{W}{U}
|
||||
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.
|
||||
@@ -468,17 +468,6 @@ text={T}: Add {1} to your mana pool. {T}, Sacrifice Ghost Quarter: Destroy targe
|
||||
rarity=U
|
||||
[/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
|
||||
name=Gobhobbler Rats
|
||||
mana={B}{R}
|
||||
|
||||
@@ -522,6 +522,15 @@ mana={0}
|
||||
type=Artifact
|
||||
[/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.
|
||||
id=1764
|
||||
name=Water Wurm
|
||||
|
||||
@@ -553,14 +553,6 @@ text=At the beginning of your upkeep, The Fallen deals 1 damage to each opponent
|
||||
rarity=U
|
||||
[/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
|
||||
name=Tracker
|
||||
mana={2}{G}
|
||||
|
||||
@@ -231,10 +231,7 @@ subtype=Juggernaut
|
||||
power=5
|
||||
toughness=3
|
||||
text=Juggernaut attacks each turn if able. Juggernaut can't be blocked by Walls.
|
||||
# Following line is not entirely correct: Walls cannot target
|
||||
# Juggernaut although they should be able to. But it's hard to
|
||||
# find a case where this difference actually matters.
|
||||
auto=protection from(wall)
|
||||
auto=cantbeblockedby(wall)
|
||||
abilities=mustattack
|
||||
rarity=U
|
||||
[/card]
|
||||
|
||||
@@ -133,6 +133,18 @@ power=2
|
||||
toughness=2
|
||||
[/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.
|
||||
target=creature
|
||||
auto=@each my upkeep:life:-1 opponent
|
||||
|
||||
@@ -11,7 +11,7 @@ rarity=R
|
||||
[/card]
|
||||
[card]
|
||||
id=124504
|
||||
name=ÁEher Membrane
|
||||
name=AEther Membrane
|
||||
mana={1}{R}{R}
|
||||
type=Creature
|
||||
subtype=Wall
|
||||
@@ -278,15 +278,6 @@ text=Protection from green At the beginning of the end step, if Dunerider Outlaw
|
||||
rarity=U
|
||||
[/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
|
||||
name=Dust Elemental
|
||||
mana={2}{W}{W}
|
||||
|
||||
@@ -1189,6 +1189,19 @@ mana={4}
|
||||
type=Artifact
|
||||
[/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.
|
||||
target=creature
|
||||
auto=flying
|
||||
|
||||
@@ -195,18 +195,6 @@ type=Artifact
|
||||
mana={4}
|
||||
[/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.
|
||||
id=1122
|
||||
name=Library of Leng
|
||||
|
||||
@@ -178,6 +178,7 @@ drift_of_the_dead.txt
|
||||
dromad_purebred.txt
|
||||
dross_harvester.txt
|
||||
duskwalker.txt
|
||||
dust_corona.txt
|
||||
dwarven_warriors.txt
|
||||
ebony_horse.txt
|
||||
ekundu_cyclops1_i218.txt
|
||||
@@ -219,6 +220,8 @@ giant_growth.txt
|
||||
giant_growth2.txt
|
||||
glimpse_the_unthinkable.txt
|
||||
gnarled_effigy.txt
|
||||
gnat_alley_creeper1.txt
|
||||
gnat_alley_creeper2.txt
|
||||
goblin_balloon_brigade.txt
|
||||
goblin_balloon_brigade2.txt
|
||||
goblin_king.txt
|
||||
@@ -246,6 +249,7 @@ imaginary_pet.txt
|
||||
immaculate_magistrate.txt
|
||||
instill_energy_i166.txt
|
||||
jodahs_avenger.txt
|
||||
juggernaut.txt
|
||||
jump.txt
|
||||
karns_touch_i233.txt
|
||||
keldon_warlord.txt
|
||||
|
||||
29
projects/mtg/bin/Res/test/dust_corona.txt
Normal file
29
projects/mtg/bin/Res/test/dust_corona.txt
Normal 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]
|
||||
23
projects/mtg/bin/Res/test/gnat_alley_creeper1.txt
Normal file
23
projects/mtg/bin/Res/test/gnat_alley_creeper1.txt
Normal file
@@ -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]
|
||||
23
projects/mtg/bin/Res/test/gnat_alley_creeper2.txt
Normal file
23
projects/mtg/bin/Res/test/gnat_alley_creeper2.txt
Normal file
@@ -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
projects/mtg/bin/Res/test/juggernaut.txt
Normal file
23
projects/mtg/bin/Res/test/juggernaut.txt
Normal 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]
|
||||
@@ -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)
|
||||
class APowerToughnessModifier: public MTGAbility{
|
||||
public:
|
||||
|
||||
@@ -123,6 +123,11 @@ class MTGCardInstance: public CardPrimitive, public MTGCard, public Damageable {
|
||||
int removeProtection(TargetChooser *tc, int erase = 0);
|
||||
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 setUntapping();
|
||||
|
||||
@@ -737,22 +737,6 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
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...
|
||||
found = s.find("protection from(");
|
||||
if (found == 0){
|
||||
@@ -771,6 +755,40 @@ MTGAbility * AbilityFactory::parseMagicLine(string s, int id, Spell * spell, MTG
|
||||
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...)
|
||||
found = s.find("untap");
|
||||
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 (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;
|
||||
badAbilities[Constants::CANTATTACK] = true;
|
||||
badAbilities[Constants::CANTBLOCK] = true;
|
||||
|
||||
@@ -353,6 +353,7 @@ int MTGCardInstance::canBlock(MTGCardInstance * opponent){
|
||||
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.
|
||||
if (opponent->protectedAgainst(this)) return 0;
|
||||
if (opponent->cantBeBlockedBy(this)) 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;
|
||||
|
||||
@@ -569,6 +570,33 @@ int MTGCardInstance::protectedAgainst(MTGCardInstance * card){
|
||||
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 */
|
||||
JSample * MTGCardInstance::getSample(){
|
||||
JSample * js;
|
||||
|
||||
Reference in New Issue
Block a user