Implementing Daybound/Nightbound mechanic (#8200)

* adding initial day/night support in game state

* remove card exclusion for testing

* added functional implementation to abilities from main branch

* functionally implemented NightCondition

* updated DayNightHint

* added support for nightbound entering transformed at night

* [MID] Implemented Unnatural Moonrise

* [MID] Implemented The Celestus

* added some docs

* changed access for state day/night methods

* added transformation to day/night switch

* re-added unfinished filter, removed day/night cards

* fixed some errors with transforming

* added hints to all day/night cards

* added transformation prevention plus a test

* added Immerwolf test

* [MID] Implemented Tovolar, Dire Overlord / Tovolar, The Midnight Scourge

* refactored some cards to not use isTransformable

* removed transformable parameter

* simplified some transform code

* fixed null pointer exception

* removed unnecessary canTransform method

* fixed a small error

* reworked implementation of rule 701.28f

* small change in transform logic

* fixed failiing test

* fixed verify failure

* small merge change

* added support for day/night switching based on spells cast

* [MID] Implemented Curse of Leeches / Leeching Lurkers

* moved day/night handling to untap step

* added tests for cards which set day and trigger from a change

* [MID] Implemented Ludevic, Necrogenius / Olag, Ludevic's Hubris

* added support for creatures transforming to match day/night when necessary

* fixed verify failures

* fixed another verify failure

* remove temporary verify skip

* added transform message

* removed unnecessary transform message

* [MID] Implemented Angelic Enforcer / Enduring Angel

* updated DayNightHint with more information

* fixed verify failure

* merge fix

* fixed Startled Awake / Persistent Nightmare / Moonmist interaction

* added another test for Moonmist

* merge fix

* merge fix

* [MID] Implemented Baneblade Scoundrel / Baneclaw Marauder

* merge fix

* [MID] various text fixes

* [MID] a few more text fixes

* Merge fix

* Improved transform game logs (hints, source), fixed day/night logs, fixed miss game param (due code style);

* fixed a test failure

* Merge fix

Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
Evan Kranzler 2021-11-05 15:11:23 -04:00 committed by GitHub
parent 6d4e5672c3
commit 30afb11cd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
305 changed files with 2174 additions and 1064 deletions

View file

@ -14,10 +14,7 @@ import mage.abilities.effects.Effect;
import mage.abilities.effects.PreventionEffectData;
import mage.abilities.effects.common.CopyEffect;
import mage.abilities.effects.common.InfoEffect;
import mage.abilities.keyword.BestowAbility;
import mage.abilities.keyword.CompanionAbility;
import mage.abilities.keyword.MorphAbility;
import mage.abilities.keyword.TransformAbility;
import mage.abilities.keyword.*;
import mage.abilities.mana.DelayedTriggeredManaAbility;
import mage.abilities.mana.TriggeredManaAbility;
import mage.actions.impl.MageAction;
@ -552,6 +549,35 @@ public abstract class GameImpl implements Game {
fireEvent(GameEvent.getEvent(GameEvent.EventType.VENTURED, playerId, null, playerId));
}
@Override
public boolean hasDayNight() {
return state.isHasDayNight();
}
@Override
public void setDaytime(boolean daytime) {
if (!state.isHasDayNight()) {
informPlayers("It has become " + (daytime ? "day" : "night"));
}
if (!state.setDaytime(daytime)) {
return;
}
// TODO: add day/night sound effect
informPlayers("It has become " + (daytime ? "day" : "night"));
fireEvent(GameEvent.getEvent(GameEvent.EventType.BECOMES_DAY_NIGHT, null, null, null));
for (Permanent permanent : state.getBattlefield().getAllPermanents()) {
if ((daytime && permanent.getAbilities(this).containsClass(NightboundAbility.class))
|| (!daytime && permanent.getAbilities(this).containsClass(DayboundAbility.class))) {
permanent.transform(null, this, true);
}
}
}
@Override
public boolean checkDayNight(boolean daytime) {
return state.isHasDayNight() && state.isDaytime() == daytime;
}
@Override
public UUID getOwnerId(UUID objectId) {
return getOwnerId(getObject(objectId));
@ -1933,6 +1959,9 @@ public abstract class GameImpl implements Game {
if (newAbility.getSourceObjectZoneChangeCounter() == 0) {
newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(ability.getSourceId()));
}
if (!(newAbility instanceof DelayedTriggeredAbility)) {
newAbility.setSourcePermanentTransformCount(this);
}
newAbility.setTriggerEvent(triggeringEvent);
state.addTriggeredAbility(newAbility);
}
@ -1949,6 +1978,7 @@ public abstract class GameImpl implements Game {
newAbility.newId();
if (source != null) {
newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId()));
newAbility.setSourcePermanentTransformCount(this);
}
newAbility.initOnAdding(this);
// ability.init is called as the ability triggeres not now.
@ -2585,6 +2615,17 @@ public abstract class GameImpl implements Game {
}
}
// Daybound/Nightbound permanents should be transformed according to day/night
// This is not a state-based action but it's unclear where else to put it
if (hasDayNight()) {
for (Permanent permanent : getBattlefield().getAllActivePermanents()) {
if ((permanent.getAbilities(this).containsClass(DayboundAbility.class) && !state.isDaytime())
|| (permanent.getAbilities(this).containsClass(NightboundAbility.class) && state.isDaytime())) {
somethingHappened = permanent.transform(null, this, true) || somethingHappened;
}
}
}
//TODO: implement the rest
return somethingHappened;
}
@ -2757,6 +2798,8 @@ public abstract class GameImpl implements Game {
@Override
public void informPlayers(String message) {
// Uncomment to print game messages
// System.out.println(message.replaceAll("\\<.*?\\>", ""));
if (simulation) {
return;
}