Добавить испытания и прогрессию по LevelID
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package com.projectsacrifice.game;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public final class ChallengeResultTest {
|
||||
@Test
|
||||
public void targetBoundariesEarnAllThreeIndependentStars() {
|
||||
LevelDefinition base = LevelCatalog.get(0);
|
||||
LevelDefinition level = new LevelDefinition(
|
||||
base.formatVersion, base.physicsVersion, base.levelId, base.worldId,
|
||||
base.number, base.title, base.hint, base.titleRu, base.hintRu,
|
||||
base.worldWidth, base.worldHeight, base.spawnX, base.spawnY, base.goal,
|
||||
base.targetDeaths, base.targetSeconds,
|
||||
new LevelDefinition.Box(500f, 700f, 50f, 50f), base.objects,
|
||||
base.platforms, base.hazards, base.switches, base.doors);
|
||||
|
||||
ChallengeResult result = ChallengeResult.evaluate(level,
|
||||
level.targetDeaths, level.targetSeconds * 1_000L, true);
|
||||
|
||||
assertTrue(result.casualtyEarned());
|
||||
assertTrue(result.timeEarned());
|
||||
assertTrue(result.collectibleEarned());
|
||||
assertTrue(result.collectibleAvailable());
|
||||
assertEquals(3, result.stars());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missedGoalsRemainIndependentAndLegacyLevelHasNoImpossibleStar() {
|
||||
LevelDefinition level = LevelCatalog.get(0);
|
||||
|
||||
ChallengeResult slowAndCostly = ChallengeResult.evaluate(level,
|
||||
level.targetDeaths + 1, level.targetSeconds * 1_000L + 1L, false);
|
||||
|
||||
assertFalse(slowAndCostly.casualtyEarned());
|
||||
assertFalse(slowAndCostly.timeEarned());
|
||||
assertFalse(slowAndCostly.collectibleAvailable());
|
||||
assertTrue(slowAndCostly.collectibleEarned());
|
||||
assertEquals(1, slowAndCostly.stars());
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,35 @@ public final class CompletionResultTest {
|
||||
LevelDefinition last = LevelCatalog.get(LevelCatalog.all().size() - 1);
|
||||
CompletionResult lastResult = CompletionResult.create(last,
|
||||
last.targetDeaths, last.targetSeconds * 900L,
|
||||
last.targetDeaths, last.targetSeconds * 900L, false);
|
||||
last.targetDeaths, last.targetSeconds * 900L,
|
||||
true, true, false);
|
||||
assertEquals(CompletionResult.NextGoal.PERSONAL_BEST, lastResult.nextGoal());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void collectibleIsThirdExplicitGoalAndStarsExposeCurrentAndBest() {
|
||||
LevelDefinition base = LevelCatalog.get(0);
|
||||
LevelDefinition level = new LevelDefinition(
|
||||
base.formatVersion, base.physicsVersion, "foundations.result-lab", "foundations",
|
||||
base.number, base.title, base.hint, base.titleRu, base.hintRu,
|
||||
base.worldWidth, base.worldHeight, base.spawnX, base.spawnY, base.goal,
|
||||
base.targetDeaths, base.targetSeconds,
|
||||
new LevelDefinition.Box(500f, 700f, 50f, 50f), base.objects,
|
||||
base.platforms, base.hazards, base.switches, base.doors);
|
||||
|
||||
CompletionResult result = CompletionResult.create(level, base.targetDeaths,
|
||||
base.targetSeconds * 1_000L, base.targetDeaths,
|
||||
base.targetSeconds * 900L, false, false, true);
|
||||
|
||||
assertEquals(2, result.challengeResult().stars());
|
||||
assertEquals(2, result.bestStars());
|
||||
assertEquals(CompletionResult.NextGoal.TARGET_COLLECTIBLE, result.nextGoal());
|
||||
|
||||
CompletionResult mastered = CompletionResult.create(level, base.targetDeaths,
|
||||
base.targetSeconds * 1_000L, base.targetDeaths,
|
||||
base.targetSeconds * 900L, true, true, true);
|
||||
assertEquals(3, mastered.challengeResult().stars());
|
||||
assertEquals(3, mastered.bestStars());
|
||||
assertEquals(CompletionResult.NextGoal.NEXT_LEVEL, mastered.nextGoal());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ public final class GameStringsTest {
|
||||
assertEquals("Добровольцы 2 Время 00:07", russian.hudStats(2, 7_000L));
|
||||
assertEquals("✓ 2 добровольца · 00:07", russian.levelBestStats(2, 7_000L));
|
||||
assertEquals("★ 2 / 3 добровольца", russian.completionDeaths(true, 2, 3));
|
||||
assertEquals("★★☆", english.starRow(2));
|
||||
assertEquals("★ Тайник найден", russian.completionCollectible(true, true));
|
||||
assertEquals("☆ Тайник не найден", russian.completionCollectible(false, true));
|
||||
assertEquals("Следующая цель · найти тайник", russian.nextGoalCollectible());
|
||||
assertEquals("Смерть — это инструмент", russian.tutorialWelcomeTitle());
|
||||
assertFalse(russian.tutorialSacrificeBody().trim().isEmpty());
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.projectsacrifice.game;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -26,4 +28,26 @@ public final class LevelCatalogLoaderTest {
|
||||
assertEquals(1, levels.get(0).number);
|
||||
assertEquals("The Test", levels.get(0).title);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void duplicateStableLevelIdsFailBeforeProgressCanCollide() {
|
||||
Map<String, String> resources = new HashMap<>();
|
||||
resources.put("levels/index.json", "{\"formatVersion\":1,\"physicsVersion\":1,"
|
||||
+ "\"levels\":[\"level-01.json\",\"level-02.json\"]}");
|
||||
resources.put("levels/level-01.json", LevelJsonLoaderTest.validLevelJson(1)
|
||||
.replace("\"number\":1,", "\"number\":1,\"id\":\"shared.level\","));
|
||||
resources.put("levels/level-02.json", LevelJsonLoaderTest.validLevelJson(1)
|
||||
.replace("\"number\":1,", "\"number\":2,\"id\":\"shared.level\","));
|
||||
|
||||
try {
|
||||
LevelCatalogLoader.load(path -> {
|
||||
String value = resources.get(path);
|
||||
return value == null ? null : new StringReader(value);
|
||||
}, "levels/index.json");
|
||||
fail("Duplicate stable IDs must fail before they share one progress record");
|
||||
} catch (IllegalArgumentException error) {
|
||||
assertTrue(error.getMessage(), error.getMessage().contains(
|
||||
"levels/index.json.levels[1] duplicates level id 'shared.level'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import java.util.Set;
|
||||
|
||||
public final class LevelCatalogTest {
|
||||
@Test
|
||||
public void productionCatalogContainsHazardsAndTwoCorpseStateTeachingLevels() {
|
||||
public void productionCatalogContainsMechanicsAndChallengeTeachingLevels() {
|
||||
List<LevelDefinition> levels = LevelCatalog.all();
|
||||
assertEquals(17, levels.size());
|
||||
assertEquals(18, levels.size());
|
||||
Set<String> titles = new HashSet<>();
|
||||
for (int i = 0; i < levels.size(); i++) {
|
||||
LevelDefinition level = levels.get(i);
|
||||
@@ -40,6 +40,10 @@ public final class LevelCatalogTest {
|
||||
objectTypes);
|
||||
assertEquals(9, weighted.switches.get(0).requiredMass);
|
||||
assertFalse(weighted.doors.isEmpty());
|
||||
LevelDefinition challenge = levels.get(17);
|
||||
assertEquals("foundations.threefold-proof", challenge.levelId);
|
||||
assertEquals("foundations", challenge.worldId);
|
||||
assertTrue(challenge.collectible != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.projectsacrifice.game;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public final class LevelIdentityTest {
|
||||
@Test
|
||||
public void everyCatalogEntryHasStableUniqueLevelAndWorldIds() {
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (LevelDefinition level : LevelCatalog.all()) {
|
||||
assertNotNull(level.levelId);
|
||||
assertNotNull(level.worldId);
|
||||
assertTrue(level.levelId.matches("[a-z0-9][a-z0-9._-]{2,63}"));
|
||||
assertTrue(level.worldId.matches("[a-z0-9][a-z0-9._-]{2,31}"));
|
||||
assertTrue("duplicate levelId " + level.levelId, ids.add(level.levelId));
|
||||
}
|
||||
assertEquals(LevelCatalog.all().size(), ids.size());
|
||||
}
|
||||
}
|
||||
@@ -251,6 +251,36 @@ public final class LevelJsonLoaderTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitLevelIdentityAndCollectibleLoadFromData() {
|
||||
String json = validLevelJson(1)
|
||||
.replace("\"number\":1,", "\"number\":1,"
|
||||
+ "\"id\":\"foundations.hidden-cache\","
|
||||
+ "\"worldId\":\"foundations\",")
|
||||
.replace("\"targets\":", "\"collectible\":{\"x\":840,\"y\":700,"
|
||||
+ "\"width\":52,\"height\":52},\"targets\":");
|
||||
|
||||
LevelDefinition level = LevelJsonLoader.load(new StringReader(json), "challenge.json");
|
||||
|
||||
assertEquals("foundations.hidden-cache", level.levelId);
|
||||
assertEquals("foundations", level.worldId);
|
||||
assertEquals(840f, level.collectible.x, 0f);
|
||||
assertEquals(52f, level.collectible.width, 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidExplicitLevelIdFailsWithSourcePath() {
|
||||
String json = validLevelJson(1).replace(
|
||||
"\"number\":1,", "\"number\":1,\"id\":\"Not Stable!\",");
|
||||
|
||||
try {
|
||||
LevelJsonLoader.load(new StringReader(json), "invalid-id.json");
|
||||
fail("Unstable level IDs must fail closed");
|
||||
} catch (IllegalArgumentException error) {
|
||||
assertTrue(error.getMessage(), error.getMessage().contains("invalid-id.json.id"));
|
||||
}
|
||||
}
|
||||
|
||||
static String validLevelJson(int formatVersion) {
|
||||
return "{"
|
||||
+ "\"formatVersion\":" + formatVersion + ","
|
||||
|
||||
@@ -8,8 +8,8 @@ import org.junit.Test;
|
||||
|
||||
public final class LevelPageTest {
|
||||
@Test
|
||||
public void seventeenLevelsUseTwoBoundedTenCardPages() {
|
||||
LevelPage page = new LevelPage(17, 10);
|
||||
public void eighteenLevelsUseTwoBoundedTenCardPages() {
|
||||
LevelPage page = new LevelPage(18, 10);
|
||||
|
||||
assertEquals(2, page.pageCount());
|
||||
assertEquals(0, page.firstIndex());
|
||||
@@ -19,7 +19,7 @@ public final class LevelPageTest {
|
||||
|
||||
page.next();
|
||||
assertEquals(10, page.firstIndex());
|
||||
assertEquals(17, page.endExclusive());
|
||||
assertEquals(18, page.endExclusive());
|
||||
assertTrue(page.hasPrevious());
|
||||
assertFalse(page.hasNext());
|
||||
|
||||
@@ -31,5 +31,7 @@ public final class LevelPageTest {
|
||||
assertEquals(1, page.pageIndex());
|
||||
page.selectIndex(16);
|
||||
assertEquals(1, page.pageIndex());
|
||||
page.selectIndex(17);
|
||||
assertEquals(1, page.pageIndex());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ public final class VersionedSessionCodecTest {
|
||||
assertEquals(LevelDefinition.PhysicalObjectType.BOX,
|
||||
restored.objects().get(0).type());
|
||||
assertEquals(3, restored.objects().get(1).mass());
|
||||
assertTrue(restored.collectibleFound());
|
||||
assertEquals(Arrays.asList(1.25f, 3.5f), restored.platformMotionTimes());
|
||||
assertTrue(restored.toggleSignals().get("gate"));
|
||||
assertEquals(2.25f, restored.timedSignals().get("clock"), 0f);
|
||||
@@ -76,6 +77,16 @@ public final class VersionedSessionCodecTest {
|
||||
assertTrue(decoded.snapshot().objects().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void legacyV4MigratesWithCollectibleNotInvented() {
|
||||
VersionedSessionCodec.DecodeResult decoded = VersionedSessionCodec.decode(
|
||||
VersionedSessionCodec.encodeLegacyV4(sampleSnapshot()));
|
||||
|
||||
assertEquals(VersionedSessionCodec.Status.MIGRATED, decoded.status());
|
||||
assertEquals(2, decoded.snapshot().objects().size());
|
||||
assertFalse(decoded.snapshot().collectibleFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void corruptAndFutureSnapshotsFailClosed() {
|
||||
VersionedSessionCodec.DecodeResult corrupt =
|
||||
@@ -109,6 +120,7 @@ public final class VersionedSessionCodecTest {
|
||||
return GameSessionSnapshot.builder(5)
|
||||
.elapsedSeconds(12.75f)
|
||||
.deaths(2)
|
||||
.collectibleFound(true)
|
||||
.respawnSeconds(0.4f)
|
||||
.player(new GameSessionSnapshot.PlayerData(
|
||||
640f, 780f, 120f, -40f, false, false,
|
||||
|
||||
Reference in New Issue
Block a user