Merge pull request #593 from PlayersCouncil/bug/march-bugs

Yet Even More Bugs Once Again
This commit is contained in:
Christian McCarty
2025-04-06 23:02:51 -05:00
committed by GitHub
15 changed files with 229 additions and 63 deletions

View File

@@ -473,7 +473,7 @@ public class AdminRequestHandler extends LotroServerRequestHandler implements Ur
List<String> maxMatchesStr = getFormMultipleParametersSafely(postDecoder, "maxMatches[]");
Throw400IfStringNull("name", name);
Throw400IfValidationFails("name", name, name.length() > 45, "League name must be 45 characters or less.");
Throw400IfValidationFails("name", name, name.length() <= 45, "League name must be 45 characters or less.");
int cost = Throw400IfNullOrNonInteger("cost", costStr);
if(startStr.length() != 8)
throw new HttpProcessingException(400, "Parameter 'start' must be exactly 8 digits long: YYYYMMDD");
@@ -629,7 +629,7 @@ public class AdminRequestHandler extends LotroServerRequestHandler implements Ur
Throw400IfStringNull("name", name);
Throw400IfValidationFails("name", name, name.length() > 45, "League name must be 45 characters or less.");
Throw400IfValidationFails("name", name, name.length() <= 45, "League name must be 45 characters or less.");
int cost = Throw400IfNullOrNonInteger("cost", costStr);
if(startStr.length() != 8)
throw new HttpProcessingException(400, "Parameter 'start' must be exactly 8 digits long: YYYYMMDD");
@@ -780,7 +780,7 @@ public class AdminRequestHandler extends LotroServerRequestHandler implements Ur
Throw400IfStringNull("name", name);
Throw400IfValidationFails("name", name, name.length() > 45, "League name must be 45 characters or less.");
Throw400IfValidationFails("name", name, name.length() <= 45, "League name must be 45 characters or less.");
int cost = Throw400IfNullOrNonInteger("cost", costStr);
if(startStr.length() != 8)
throw new HttpProcessingException(400, "Parameter 'start' must be exactly 8 digits long: YYYYMMDD");
@@ -941,7 +941,7 @@ public class AdminRequestHandler extends LotroServerRequestHandler implements Ur
var type = Tournament.TournamentType.parse(typeStr);
Throw400IfValidationFails("type", typeStr, type != null);
Throw400IfStringNull("name", name);
Throw400IfValidationFails("name", name, name.length() > 45, "Tournament name must be 45 characters or less.");
Throw400IfValidationFails("name", name, name.length() <= 45, "Tournament name must be 45 characters or less.");
boolean wc = ParseBoolean("wc", wcStr, false);
Throw400IfStringNull("tournamentId", tournamentId);
Throw400IfStringNull("format", formatStr);

View File

@@ -165,7 +165,7 @@ var GempLotrCommunication = Class.extend({
};
},
getStatus:function (callback) {
extractStatus:function (callback) {
var that = this;
return function (xml, status, request) {
callback(xml, request.status);
@@ -1314,7 +1314,7 @@ var GempLotrCommunication = Class.extend({
login:login,
password:password,
participantId:getUrlParam("participantId")},
success:this.getStatus(callback),
success:this.extractStatus(callback),
error:this.errorCheck(errorMap),
dataType:"html"
});

View File

@@ -310,7 +310,7 @@
type: addKeyword
select: memory(playedMan)
keyword: damage
amount: 1
amount: 2
until: regroup
}
]

View File

@@ -521,7 +521,7 @@
}
effect: {
type: playCardFromDiscard
select: choose(culture(wraith),minion)
select: choose(culture(wraith),minion,not(self))
}
}
{

View File

@@ -379,7 +379,7 @@
culture: Gandalf
twilight: 0
type: Condition
target: companion,not(hasAttached(name(Salve)))
target: companion,OnePerBearer
keywords: Spell
effects: [
{
@@ -395,10 +395,17 @@
type: aboutToTakeWound
filter: exhausted,bearer
}
cost: {
type: discard
select: self
}
cost: [
{
type: memorize
filter: bearer
memory: bearer
}
{
type: discard
select: self
}
]
effect: {
type: preventWound
select: memory(bearer)

View File

@@ -449,7 +449,7 @@
requires: [
{
type: CanSpot
filter: name(Frodo,AssignableToSkirmishAgainst(minion))
filter: name(Frodo),AssignableToSkirmishAgainst(minion)
}
{
type: CanSpot

View File

@@ -717,7 +717,7 @@
]
effect: {
type: StackCardsFromDiscard
select: choose(tentacle)
select: choose(other,tentacle)
count: 2
where: memory(stacker)
}

View File

@@ -855,7 +855,7 @@
{
type: modifier
modifier: {
type: cancelKeywordBonus
type: RemoveKeyword
filter: companion,inSkirmishAgainst(culture(isengard),orc)
keyword: damage
}

View File

@@ -115,4 +115,15 @@ public enum Keyword implements Filterable {
public boolean isRealKeyword() {
return realKeyword;
}
public static Keyword parse(String name) {
String nameCaps = name.toUpperCase().trim().replace(' ', '_').replace('-', '_');
String nameLower = name.toLowerCase();
for (Keyword keyword : values()) {
if (keyword.getHumanReadable().toLowerCase().equals(nameLower) || keyword.toString().equals(nameCaps))
return keyword;
}
return null;
}
}

View File

@@ -50,6 +50,12 @@ public class ResolveSkirmishEffect extends AbstractEffect {
multiplier = game.getModifiersQuerying().getOverwhelmMultiplier(game, fellowshipCharacter);
}
int shadowMult = 2;
for(var minion : skirmish.getShadowCharacters()) {
var mult = game.getModifiersQuerying().getOverwhelmMultiplier(game, minion);
shadowMult = Math.max(shadowMult, mult);
}
if (fpStrength == 0 && shadowStrength == 0) {
return Result.FELLOWSHIP_LOSES;
} else if (multiplier < CantBeOverwhelmedModifier.ImmuneToOverwhelmThreshold
@@ -57,7 +63,7 @@ public class ResolveSkirmishEffect extends AbstractEffect {
return Result.FELLOWSHIP_OVERWHELMED;
} else if (fpStrength <= shadowStrength) {
return Result.FELLOWSHIP_LOSES;
} else if (fpStrength >= 2 * shadowStrength) {
} else if (fpStrength >= shadowMult * shadowStrength) {
return Result.SHADOW_OVERWHELMED;
} else {
return Result.SHADOW_LOSES;

View File

@@ -17,8 +17,11 @@ public class Card_12_032_Tests
return new GenericCardTestHelper(
new HashMap<>()
{{
put("card", "12_32");
// put other cards in here as needed for the test case
put("salve", "12_32");
put("aragorn", "1_89");
put("marksman", "1_176");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
@@ -38,12 +41,13 @@ public class Card_12_032_Tests
* Twilight Cost: 0
* Type: Condition
* Subtype:
* Game Text: <b>Spell</b>. To play, spot a [gandalf] Wizard. Bearer must be a companion. Limit 1 per bearer.<br><b>Response:</b> If bearer is about to take a wound that would kill him or her, discard this condition to prevent that wound.
* Game Text: <b>Spell</b>. To play, spot a [gandalf] Wizard. Bearer must be a companion. Limit 1 per bearer.
* <b>Response:</b> If bearer is about to take a wound that would kill him or her, discard this condition to prevent that wound.
*/
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
var card = scn.GetFreepsCard("salve");
assertEquals("Salve", card.getBlueprint().getTitle());
assertNull(card.getBlueprint().getSubtitle());
@@ -55,18 +59,34 @@ public class Card_12_032_Tests
assertEquals(0, card.getBlueprint().getTwilightCost());
}
// Uncomment any @Test markers below once this is ready to be used
//@Test
public void SalveTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void SalveProtectsBearerFromLethalDamage() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var salve = scn.GetFreepsCard("salve");
var aragorn = scn.GetFreepsCard("aragorn");
scn.FreepsMoveCharToTable(aragorn);
scn.FreepsAttachCardsTo(aragorn, salve);
scn.ShadowMoveCharToTable("marksman");
scn.StartGame();
scn.FreepsPlayCard(card);
scn.AddWoundsToChar(aragorn, 3);
scn.SkipToPhase(Phase.ARCHERY);
scn.PassCurrentPhaseActions();
scn.FreepsChooseCard(aragorn);
assertEquals(1, scn.GetVitality(aragorn));
assertEquals(Zone.ATTACHED, salve.getZone());
assertEquals(Zone.FREE_CHARACTERS, aragorn.getZone());
assertTrue(scn.FreepsHasOptionalTriggerAvailable());
scn.FreepsAcceptOptionalTrigger();
assertEquals(Zone.DISCARD, salve.getZone());
assertEquals(Zone.FREE_CHARACTERS, aragorn.getZone());
assertEquals(1, scn.GetVitality(aragorn));
assertEquals(0, scn.GetTwilight());
}
}

View File

@@ -18,8 +18,8 @@ public class Card_17_096_Tests
new HashMap<>()
{{
put("eowyn", "17_96");
put("scout", "1_270");
put("soldier", "1_271");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,

View File

@@ -17,8 +17,9 @@ public class Card_17_100_Tests
return new GenericCardTestHelper(
new HashMap<>()
{{
put("card", "17_100");
// put other cards in here as needed for the test case
put("caves", "17_100");
put("sauron", "9_48");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
@@ -38,12 +39,14 @@ public class Card_17_100_Tests
* Twilight Cost: 1
* Type: Condition
* Subtype: Support area
* Game Text: To play, spot 3 [rohan] companions.<br><b>Assignment:</b> Assign a minion to Frodo to make that minion lose all game text keywords of your choice and unable to gain game text keywords until the regroup phase.
* Game Text: To play, spot 3 [rohan] companions.
* <b>Assignment:</b> Assign a minion to Frodo to make that minion lose all game text keywords of your choice
* and unable to gain game text keywords until the regroup phase.
*/
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
var card = scn.GetFreepsCard("caves");
assertEquals("Into the Caves", card.getBlueprint().getTitle());
assertNull(card.getBlueprint().getSubtitle());
@@ -55,18 +58,48 @@ public class Card_17_100_Tests
assertEquals(1, card.getBlueprint().getTwilightCost());
}
// Uncomment any @Test markers below once this is ready to be used
//@Test
public void IntotheCavesTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void IntotheCavesAssignsFrodoToMinionToRemoveKeywords() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var frodo = scn.GetRingBearer();
var caves = scn.GetFreepsCard("caves");
scn.FreepsMoveCardToSupportArea(caves);
var sauron = scn.GetShadowCard("sauron");
scn.ShadowMoveCharToTable(sauron);
scn.StartGame();
scn.FreepsPlayCard(card);
assertEquals(1, scn.GetTwilight());
scn.SkipToPhase(Phase.ARCHERY);
scn.SkipToPhase(Phase.ASSIGNMENT);
assertTrue(scn.hasKeyword(sauron, Keyword.DAMAGE));
assertTrue(scn.hasKeyword(sauron, Keyword.ENDURING));
assertTrue(scn.hasKeyword(sauron, Keyword.FIERCE));
assertFalse(scn.IsCharAssigned(frodo));
assertFalse(scn.IsCharAssigned(sauron));
assertTrue(scn.FreepsActionAvailable(caves));
scn.FreepsUseCardAction(caves);
assertTrue(scn.IsCharAssigned(frodo));
assertTrue(scn.IsCharAssigned(sauron));
assertTrue(scn.FreepsDecisionAvailable("keyword"));
scn.FreepsChooseMultipleChoiceOption("DAMAGE");
assertFalse(scn.hasKeyword(sauron, Keyword.DAMAGE));
assertTrue(scn.hasKeyword(sauron, Keyword.ENDURING));
assertTrue(scn.hasKeyword(sauron, Keyword.FIERCE));
scn.FreepsChooseYes();
scn.FreepsChooseMultipleChoiceOption("ENDURING");
assertFalse(scn.hasKeyword(sauron, Keyword.DAMAGE));
assertFalse(scn.hasKeyword(sauron, Keyword.ENDURING));
assertTrue(scn.hasKeyword(sauron, Keyword.FIERCE));
scn.FreepsChooseNo();
assertFalse(scn.hasKeyword(sauron, Keyword.DAMAGE));
assertFalse(scn.hasKeyword(sauron, Keyword.ENDURING));
assertTrue(scn.hasKeyword(sauron, Keyword.FIERCE));
assertTrue(scn.ShadowAnyDecisionsAvailable());
}
}

View File

@@ -3,7 +3,6 @@ package com.gempukku.lotro.cards.unofficial.pc.vsets.set_v02;
import com.gempukku.lotro.cards.GenericCardTestHelper;
import com.gempukku.lotro.common.*;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.PhysicalCardImpl;
import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
import org.junit.Test;
@@ -18,8 +17,10 @@ public class Card_V2_026_Tests
return new GenericCardTestHelper(
new HashMap<>()
{{
put("card", "102_26");
// put other cards in here as needed for the test case
put("grunt", "102_26");
put("worker", "3_62");
put("mithrandir", "6_30");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
@@ -47,7 +48,7 @@ public class Card_V2_026_Tests
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
var card = scn.GetFreepsCard("grunt");
assertEquals("Isengard Grunt", card.getBlueprint().getTitle());
assertNull(card.getBlueprint().getSubtitle());
@@ -62,18 +63,36 @@ public class Card_V2_026_Tests
assertEquals(4, card.getBlueprint().getSiteNumber());
}
// Uncomment any @Test markers below once this is ready to be used
//@Test
public void IsengardGruntTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void IsengardGruntCanExertToPreventIsenorcOverwhelm() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var grunt = scn.GetShadowCard("grunt");
var worker = scn.GetShadowCard("worker");
scn.ShadowMoveCharToTable(grunt, worker);
var mithrandir = scn.GetFreepsCard("mithrandir");
scn.FreepsMoveCharToTable(mithrandir);
scn.StartGame();
scn.FreepsPlayCard(card);
scn.SkipToAssignments();
assertEquals(2, scn.GetTwilight());
scn.FreepsAssignToMinions(mithrandir, worker);
scn.ShadowDeclineAssignments();
scn.FreepsResolveSkirmish(mithrandir);
scn.FreepsPassCurrentPhaseAction();
assertEquals(0, scn.GetWoundsOn(grunt));
assertTrue(scn.ShadowActionAvailable(grunt));
scn.ShadowUseCardAction(grunt);
assertEquals(1, scn.GetWoundsOn(grunt));
assertEquals(0, scn.GetWoundsOn(worker));
assertEquals(Zone.SHADOW_CHARACTERS, worker.getZone());
scn.PassCurrentPhaseActions();
assertEquals(Zone.SHADOW_CHARACTERS, worker.getZone());
assertEquals(2, scn.GetWoundsOn(worker)); //Mithrandir is damage +1
}
}

View File

@@ -3,13 +3,13 @@ package com.gempukku.lotro.cards.unofficial.pc.vsets.set_v02;
import com.gempukku.lotro.cards.GenericCardTestHelper;
import com.gempukku.lotro.common.*;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.PhysicalCardImpl;
import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException;
import org.junit.Test;
import java.util.HashMap;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class Card_V2_029_Tests
{
@@ -18,8 +18,12 @@ public class Card_V2_029_Tests
return new GenericCardTestHelper(
new HashMap<>()
{{
put("card", "102_29");
// put other cards in here as needed for the test case
put("saruman", "102_29");
put("worker", "3_62");
put("legolas", "1_50");
put("gimli", "1_13");
put("axe", "1_14");
}},
GenericCardTestHelper.FellowshipSites,
GenericCardTestHelper.FOTRFrodo,
@@ -49,7 +53,7 @@ public class Card_V2_029_Tests
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
var card = scn.GetFreepsCard("saruman");
assertEquals("Saruman", card.getBlueprint().getTitle());
assertEquals("Mind of Metal and Wheels", card.getBlueprint().getSubtitle());
@@ -64,18 +68,84 @@ public class Card_V2_029_Tests
assertEquals(4, card.getBlueprint().getSiteNumber());
}
// Uncomment any @Test markers below once this is ready to be used
//@Test
public void SarumanTest1() throws DecisionResultInvalidException, CardNotFoundException {
@Test
public void SarumanCannotBeAssignedToSkirmishes() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var card = scn.GetFreepsCard("card");
scn.FreepsMoveCardToHand(card);
var saruman = scn.GetShadowCard("saruman");
scn.ShadowMoveCharToTable(saruman);
scn.ShadowMoveCharToTable("worker");
scn.StartGame();
scn.FreepsPlayCard(card);
scn.SkipToAssignments();
//Worker but not Saruman
assertEquals(1, scn.FreepsGetShadowAssignmentTargetCount());
}
assertEquals(4, scn.GetTwilight());
@Test
public void Saruman_CAN_TakeArcheryWounds() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var saruman = scn.GetShadowCard("saruman");
scn.ShadowMoveCharToTable(saruman);
scn.FreepsMoveCharToTable("legolas");
scn.StartGame();
scn.SkipToArcheryWounds();
assertEquals(1, scn.GetWoundsOn(saruman));
}
@Test
public void SarumanNegatesAllDamageBonusesOnCompanionsSkirmishingIsenorcs() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var saruman = scn.GetShadowCard("saruman");
var worker = scn.GetShadowCard("worker");
scn.ShadowMoveCharToTable(saruman, worker);
var gimli = scn.GetFreepsCard("gimli");
var axe = scn.GetFreepsCard("axe");
scn.FreepsMoveCharToTable(gimli);
scn.FreepsAttachCardsTo(gimli, axe);
scn.StartGame();
scn.SkipToAssignments();
assertEquals(2, scn.GetKeywordCount(gimli, Keyword.DAMAGE));
scn.FreepsAssignToMinions(gimli, worker);
scn.FreepsResolveSkirmish(gimli);
assertEquals(0, scn.GetKeywordCount(gimli, Keyword.DAMAGE));
scn.PassCurrentPhaseActions();
scn.ShadowDeclineOptionalTrigger();
assertEquals(1, scn.GetWoundsOn(worker));
}
@Test
public void SarumanCanExertToPreventWoundsToIsenorcs() throws DecisionResultInvalidException, CardNotFoundException {
//Pre-game setup
var scn = GetScenario();
var saruman = scn.GetShadowCard("saruman");
var worker = scn.GetShadowCard("worker");
scn.ShadowMoveCharToTable(saruman, worker);
scn.FreepsMoveCharToTable("legolas");
scn.StartGame();
scn.SkipToArcheryWounds();
scn.ShadowChooseCard(worker); //archery wound
assertTrue(scn.ShadowHasOptionalTriggerAvailable());
assertEquals(0, scn.GetWoundsOn(saruman));
assertEquals(0, scn.GetWoundsOn(worker));
scn.ShadowAcceptOptionalTrigger();
assertEquals(1, scn.GetWoundsOn(saruman));
assertEquals(0, scn.GetWoundsOn(worker));
}
}