-
Notifications
You must be signed in to change notification settings - Fork 36
Shrieking hat #586
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Shrieking hat #586
Changes from all commits
da4c134
38f80a7
9264eeb
f3b03af
8ce9de3
4056b07
21778f0
7e2c8ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package stsjorbsmod.cards; | ||
|
|
||
| import org.apache.commons.lang3.mutable.MutableInt; | ||
|
|
||
| public interface DeathPreventionCard { | ||
| MutableInt currentPriority = new MutableInt(1); | ||
|
|
||
| int getPriority(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package stsjorbsmod.cards; | ||
|
|
||
| public interface WasHPLostCardSubscriber { | ||
| void wasHPLost(int damageAmount); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| package stsjorbsmod.cards.cull; | ||
|
|
||
| import basemod.abstracts.CustomSavable; | ||
| import com.megacrit.cardcrawl.actions.AbstractGameAction; | ||
| import com.megacrit.cardcrawl.actions.animations.VFXAction; | ||
| import com.megacrit.cardcrawl.actions.common.DamageAllEnemiesAction; | ||
| import com.megacrit.cardcrawl.actions.utility.SFXAction; | ||
| import com.megacrit.cardcrawl.cards.AbstractCard; | ||
| import com.megacrit.cardcrawl.cards.DamageInfo; | ||
| import com.megacrit.cardcrawl.characters.AbstractPlayer; | ||
| import com.megacrit.cardcrawl.core.Settings; | ||
| import com.megacrit.cardcrawl.dungeons.AbstractDungeon; | ||
| import com.megacrit.cardcrawl.monsters.AbstractMonster; | ||
| import com.megacrit.cardcrawl.vfx.cardManip.ShowCardBrieflyEffect; | ||
| import com.megacrit.cardcrawl.vfx.combat.ShockWaveEffect; | ||
| import stsjorbsmod.JorbsMod; | ||
| import stsjorbsmod.actions.ConsumerGameAction; | ||
| import stsjorbsmod.actions.PermanentlyModifyDamageAction; | ||
| import stsjorbsmod.cards.CustomJorbsModCard; | ||
| import stsjorbsmod.cards.DeathPreventionCard; | ||
| import stsjorbsmod.cards.WasHPLostCardSubscriber; | ||
| import stsjorbsmod.characters.Cull; | ||
| import stsjorbsmod.util.CardMetaUtils; | ||
| import stsjorbsmod.util.EffectUtils; | ||
|
|
||
| import static stsjorbsmod.JorbsMod.JorbsCardTags.LEGENDARY; | ||
|
|
||
| /** | ||
| * Unplayable. NL Retain. NL When you die instead deal (3 +) all damage taken while this was in your hand to all enemies. NL stsjorbsmod:Destroy. | ||
| */ | ||
| public class ShriekingHat extends CustomJorbsModCard implements DeathPreventionCard, WasHPLostCardSubscriber, CustomSavable<Integer> { | ||
| public static final String ID = JorbsMod.makeID(ShriekingHat.class); | ||
|
|
||
| private static final CardRarity RARITY = CardRarity.RARE; | ||
| private static final CardTarget TARGET = CardTarget.SELF; | ||
| private static final CardType TYPE = CardType.SKILL; | ||
| public static final CardColor COLOR = Cull.Enums.CULL_CARD_COLOR; | ||
|
|
||
| private AbstractPlayer p = AbstractDungeon.player; | ||
| private int priority; | ||
|
|
||
| private static final int COST = COST_UNPLAYABLE; | ||
| private static final int UPGRADE_DAMAGE = 3; | ||
|
|
||
| public ShriekingHat() { | ||
| super(ID, COST, TYPE, COLOR, RARITY, TARGET); | ||
|
|
||
| damage = baseDamage = misc = magicNumber = 0; | ||
| selfRetain = true; | ||
|
|
||
| tags.add(LEGENDARY); | ||
| } | ||
|
|
||
| @Override | ||
| public int getPriority() { | ||
| return priority; | ||
| } | ||
|
|
||
| /** | ||
| * used in ShowCardAndAddToHandEffect::new which gets used in Discovery, Card Potions, and MakeTempCardInHandAction leading to Dead Branch | ||
| */ | ||
| @Override | ||
| public void triggerWhenCopied() { | ||
| super.triggerWhenCopied(); | ||
| priority = currentPriority.incrementAndGet(); | ||
| } | ||
|
|
||
| @Override | ||
| public void wasHPLost(int damageAmount) { | ||
| addToBot(new PermanentlyModifyDamageAction(uuid, damageAmount)); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean canUse(AbstractPlayer abstractPlayer, AbstractMonster abstractMonster) { | ||
| this.cantUseMessage = cardStrings.EXTENDED_DESCRIPTION[1]; | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public void use(AbstractPlayer abstractPlayer, AbstractMonster abstractMonster) { | ||
| addToBot(new VFXAction(new ShowCardBrieflyEffect(this))); | ||
| addToBot(new SFXAction("ATTACK_PIERCING_WAIL", -1, true)); | ||
| if (Settings.FAST_MODE) { | ||
| addToBot(new VFXAction(p, new ShockWaveEffect(p.hb.cX, p.hb.cY, Settings.RED_TEXT_COLOR, ShockWaveEffect.ShockWaveType.CHAOTIC), 0.3F)); | ||
| } else { | ||
| addToBot(new VFXAction(p, new ShockWaveEffect(p.hb.cX, p.hb.cY, Settings.RED_TEXT_COLOR, ShockWaveEffect.ShockWaveType.CHAOTIC), 1.5F)); | ||
| } | ||
| addToBot(new DamageAllEnemiesAction(p, baseDamage, DamageInfo.DamageType.THORNS, AbstractGameAction.AttackEffect.SLASH_HORIZONTAL)); | ||
| addToBot(new ConsumerGameAction<>(EffectUtils::showDestroyEffect, this)); | ||
| CardMetaUtils.destroyCardPermanently(this); | ||
| } | ||
|
|
||
| @Override | ||
| public String getRawDynamicDescriptionSuffix() { | ||
| return EXTENDED_DESCRIPTION[0]; | ||
| } | ||
|
|
||
| @Override | ||
| public void upgrade() { | ||
| if (!upgraded) { | ||
| upgradeName(); | ||
| upgradeMagicNumber(UPGRADE_DAMAGE); | ||
| upgradeDamage(UPGRADE_DAMAGE); | ||
| upgradeDescription(); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public AbstractCard makeCopy() { | ||
| ShriekingHat copy = (ShriekingHat) super.makeCopy(); | ||
| copy.priority = this.priority; | ||
| return copy; | ||
| } | ||
|
|
||
| @Override | ||
| public Integer onSave() { | ||
| return priority; | ||
| } | ||
|
|
||
| @Override | ||
| public void onLoad(Integer integer) { | ||
| this.priority = integer; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| package stsjorbsmod.patches; | ||
|
|
||
| import com.evacipated.cardcrawl.modthespire.lib.SpirePatch; | ||
| import com.megacrit.cardcrawl.actions.common.RemoveSpecificPowerAction; | ||
| import com.megacrit.cardcrawl.cards.AbstractCard; | ||
| import com.megacrit.cardcrawl.cards.DamageInfo; | ||
| import com.megacrit.cardcrawl.characters.AbstractPlayer; | ||
| import com.megacrit.cardcrawl.dungeons.AbstractDungeon; | ||
| import com.megacrit.cardcrawl.potions.FairyPotion; | ||
| import com.megacrit.cardcrawl.relics.LizardTail; | ||
| import com.megacrit.cardcrawl.relics.MarkOfTheBloom; | ||
| import javassist.CannotCompileException; | ||
| import javassist.expr.ExprEditor; | ||
| import javassist.expr.FieldAccess; | ||
| import stsjorbsmod.cards.DeathPreventionCard; | ||
| import stsjorbsmod.cards.cull.OldBook; | ||
| import stsjorbsmod.cards.cull.ShriekingHat; | ||
| import stsjorbsmod.powers.OldBookPower; | ||
| import stsjorbsmod.util.CardMetaUtils; | ||
|
|
||
| import java.util.Comparator; | ||
| import java.util.SortedSet; | ||
| import java.util.TreeSet; | ||
|
|
||
| @SpirePatch(clz = DamageInfo.class, method = SpirePatch.CLASS) | ||
| public class DeathPreventionPatch { | ||
| public static boolean triggerDeathPrevention(AbstractPlayer p, int damageAmount) { | ||
| if (damageAmount < p.currentHealth) | ||
| return false; | ||
| if (p.hasPotion(FairyPotion.POTION_ID)) | ||
| return false; | ||
| if (p.hasRelic(LizardTail.ID) && p.getRelic(LizardTail.ID).counter == -1) | ||
| return false; | ||
| if (!p.hasPower(OldBookPower.POWER_ID + OldBookPower.UPGRADED) && | ||
| !p.hasPower(OldBookPower.POWER_ID + OldBookPower.NORMAL) && | ||
| p.hand.group.stream().noneMatch(c -> c instanceof ShriekingHat)) | ||
| return false; | ||
|
|
||
| // requested that death preventing cards trigger in order of pickup. | ||
| SortedSet<DeathPreventionCard> deathPreventionCards = new TreeSet<>(Comparator.comparingInt(DeathPreventionCard::getPriority)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the change suggested above, we should be able to loop the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It added a little bit more complexity to the the logic, but I think it should work as expected. |
||
| for (AbstractCard c : p.masterDeck.group) { | ||
| if (c instanceof ShriekingHat && p.hand.group.stream().anyMatch(ch -> c.uuid.equals(ch.uuid))) { | ||
| deathPreventionCards.add((DeathPreventionCard) c); | ||
| } | ||
| else if (c instanceof OldBook) { | ||
| deathPreventionCards.add((DeathPreventionCard) c); | ||
| } | ||
| } | ||
|
|
||
| if (deathPreventionCards.isEmpty()) { | ||
| for (AbstractCard c : p.hand.group) { | ||
| if (c instanceof ShriekingHat) { | ||
| deathPreventionCards.add((DeathPreventionCard) c); | ||
| } | ||
| } | ||
| for (AbstractCard c : p.exhaustPile.group) { | ||
| if (c instanceof OldBook) { | ||
| deathPreventionCards.add((DeathPreventionCard) c); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (deathPreventionCards.isEmpty()) { | ||
| return false; | ||
| } | ||
| // n.b. for debugging: TreeSet.add (and the underlying TreeMap.put) impl only adds if there isn't | ||
| // already an object matching the comparator. As such if there's both a ShriekingHat and OldBook in the | ||
| // deck, only the first card found will be in deathPreventionCards set since priority for these cards | ||
| // added to the master deck will be 0. | ||
| AbstractCard firstDeathPreventionCard = (AbstractCard) deathPreventionCards.first(); | ||
|
|
||
| if (OldBook.ID.equals(firstDeathPreventionCard.cardID)) { | ||
| handleOldBook(p); | ||
| } else if (ShriekingHat.ID.equals(firstDeathPreventionCard.cardID)) { | ||
| handleShriekingHat(p, (ShriekingHat) firstDeathPreventionCard); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| private static void handleOldBook(AbstractPlayer p) { | ||
| OldBookPower po; | ||
| if (p.hasPower(OldBookPower.POWER_ID + OldBookPower.NORMAL)) | ||
| po = (OldBookPower) p.getPower(OldBookPower.POWER_ID + OldBookPower.NORMAL); | ||
| else | ||
| po = (OldBookPower) p.getPower(OldBookPower.POWER_ID + OldBookPower.UPGRADED); | ||
|
|
||
| AbstractCard c = po.getCard(); | ||
| if (c.upgraded) { | ||
| if (!p.hasRelic(MarkOfTheBloom.ID)) { | ||
| float percent = (float) c.magicNumber / 100.0F; | ||
| int healAmt = (int) ((float) p.maxHealth * percent); | ||
| if (healAmt < 1) { | ||
| healAmt = 1; | ||
| } | ||
|
|
||
| p.heal(healAmt, true); | ||
| } else { | ||
| p.getRelic(MarkOfTheBloom.ID).flash(); | ||
| } | ||
| } | ||
|
|
||
| po.reducePower(1); | ||
| if (po.amount <= 0) | ||
| AbstractDungeon.actionManager.addToBottom(new RemoveSpecificPowerAction(p, p, po)); | ||
|
|
||
| CardMetaUtils.destroyCardPermanently(c); | ||
| } | ||
|
|
||
| private static void handleShriekingHat(AbstractPlayer p, ShriekingHat shriekingHatCard) { | ||
| shriekingHatCard.use(p, null); | ||
| } | ||
|
|
||
| @SpirePatch(clz = AbstractPlayer.class, method = "damage") | ||
| public static class AbstractPlayer_damage { | ||
| public static ExprEditor Instrument() { | ||
| return new ExprEditor() { | ||
| @Override | ||
| public void edit(FieldAccess fieldAccess) throws CannotCompileException { | ||
| if (fieldAccess.getClassName().equals(AbstractPlayer.class.getName()) && | ||
| fieldAccess.getFieldName().equals("lastDamageTaken")) { | ||
| fieldAccess.replace(String.format("{ if (%1$s.triggerDeathPrevention($0, damageAmount)) { damageAmount = 0; lastDamageTaken = 0; }" + | ||
| "else { $proceed($$); }; } ", | ||
| DeathPreventionPatch.class.getName())); | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.