feat(sanctuary): nommer tous les zombies façon Xbox 360 (#33) #64

Merged
koka merged 1 commits from feature/zombie-xbox-gamertags into main 2026-08-29 10:37:23 +00:00
4 changed files with 74 additions and 3 deletions
+3 -1
View File
@@ -4,7 +4,9 @@
Les noms sont déterministes à partir de l'UUID de la créature ; les répétitions sont autorisées.
- villageois, zombies, moutons, vaches, cochons et poules : prénoms humains comme Patrick, Henri ou Hervé ;
- villageois, moutons, vaches, cochons et poules : prénoms humains comme Patrick, Henri ou Hervé ;
- toute la famille Zombie, vanilla ou modée : gamertags volontairement « Xbox 360 » comme
`xXDarkSasuke69Xx`, `iTzKevin360` ou `xXNoScope420Xx` ;
- Endermen : sonorités extraterrestres rendues avec la police alien vanilla `minecraft:alt` ;
- Creepers : prénoms et mots en S, avec quelques sifflements ;
- squelettes : rythmes et onomatopées musicales ;
+9 -1
View File
@@ -70,6 +70,14 @@ tasks.register("shopHeaderLayoutSmoke", JavaExec) {
mainClass = "fr.koka99cab.sanctuary26.sanctuary.client.ShopDailyHeaderLayoutSmoke"
}
tasks.register("zombieNamesSmoke", JavaExec) {
group = "verification"
description = "Checks deterministic Xbox-360-style names for every vanilla Zombie subtype."
dependsOn tasks.named("testClasses")
classpath = sourceSets.test.runtimeClasspath
mainClass = "fr.koka99cab.sanctuary26.sanctuary.client.SanctuaryZombieNamesSmoke"
}
tasks.register("verifyBlackMarket") {
group = "verification"
description = "Checks the physical sign market, flash Shop, escrow ledger and mailbox delivery."
@@ -220,7 +228,7 @@ tasks.register("verifySanctuary") {
inputs.files(fileTree("tools"))
dependsOn tasks.named("progressionModelSmoke"), tasks.named("serverIdentityModelSmoke"),
tasks.named("shopDeliveryTimingSmoke"), tasks.named("blackMarketModelSmoke"),
tasks.named("questBoardModelSmoke"), tasks.named("shopHeaderLayoutSmoke"),
tasks.named("questBoardModelSmoke"), tasks.named("shopHeaderLayoutSmoke"), tasks.named("zombieNamesSmoke"),
tasks.named("verifyBlackMarket")
doLast {
@@ -33,6 +33,15 @@ public final class SanctuaryMobNames {
"Raymond", "Colette", "Norbert", "Mireille", "Didier", "Chantal", "Marcel", "Brigitte",
"Jean-Michel", "Françoise", "Pascal", "Sylvie", "René", "Martine", "Gilbert", "Odette"
};
private static final String[] ZOMBIE_GAMERTAGS = {
"xXDarkSasuke69Xx", "xXSniperDu59Xx", "OoKilleur93oO", "iTzKevin360",
"xXNoScope420Xx", "DarkKiller2009", "LeBGduMinecraft26", "xXShadowNinja76Xx",
"MLGRaptor67", "FazeKevinDu13", "QuickScope360", "NarutoPro59",
"xXGhostRider666Xx", "TheDarkCreeper42", "L3G3NDKILL3R", "xXEnderBoss77Xx",
"SkyZzOfficiel62", "xXPikaKiller25Xx", "DarkAngel666", "iiTzWarrior360ii",
"xXDragonFeu974Xx", "ProGamerDu59", "xXHeadShot360Xx", "BoGoss2008",
"RoiDuNoScope93", "xXDeathNinja13Xx", "TeamDark59", "KevinLeBoss360"
};
private static final String[] ENDER = {
"Xyr-9", "Vaeluun", "Orryx", "Nhal", "Zyphra", "Qor'eth", "Uulma", "Thess-7",
"Ixxa", "Vhoor", "Aen-Null", "Kryss", "Omnû", "Yl'zar", "Nexil", "Rhaum"
@@ -92,6 +101,7 @@ public final class SanctuaryMobNames {
}
static String generate(Entity entity) {
if (usesZombieGamertags(entity.getClass())) return zombieName(entity.getUUID());
String[] family = switch (entity) {
case EnderMan ignored -> ENDER;
case Creeper ignored -> CREEPERS;
@@ -99,7 +109,6 @@ public final class SanctuaryMobNames {
case Wolf ignored -> DOGS;
case Cat ignored -> CATS;
case Villager ignored -> HUMANS;
case Zombie ignored -> HUMANS;
case Cow ignored -> HUMANS;
case Pig ignored -> HUMANS;
case Sheep ignored -> HUMANS;
@@ -112,6 +121,14 @@ public final class SanctuaryMobNames {
return pick(family, entity.getUUID());
}
static boolean usesZombieGamertags(Class<?> entityClass) {
return entityClass != null && Zombie.class.isAssignableFrom(entityClass);
}
static String zombieName(UUID uuid) {
return pick(ZOMBIE_GAMERTAGS, uuid);
}
private static String pick(String[] values, UUID uuid) {
long mixed = uuid.getMostSignificantBits() ^ Long.rotateLeft(uuid.getLeastSignificantBits(), 23);
mixed = (mixed ^ (mixed >>> 30)) * 0xBF58476D1CE4E5B9L;
@@ -0,0 +1,44 @@
package fr.koka99cab.sanctuary26.sanctuary.client;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import net.minecraft.world.entity.monster.Creeper;
import net.minecraft.world.entity.monster.zombie.Drowned;
import net.minecraft.world.entity.monster.zombie.Husk;
import net.minecraft.world.entity.monster.zombie.Zombie;
import net.minecraft.world.entity.monster.zombie.ZombieVillager;
import net.minecraft.world.entity.monster.zombie.ZombifiedPiglin;
/** Executable coverage for deterministic Xbox-360-style names across the complete Zombie family. */
public final class SanctuaryZombieNamesSmoke {
private SanctuaryZombieNamesSmoke() {
}
public static void main(String[] args) {
for (Class<?> zombieClass : new Class<?>[] {
Zombie.class, Husk.class, Drowned.class, ZombieVillager.class, ZombifiedPiglin.class
}) {
require(SanctuaryMobNames.usesZombieGamertags(zombieClass),
"Zombie subtype missed gamertag family: " + zombieClass.getSimpleName());
}
require(!SanctuaryMobNames.usesZombieGamertags(Creeper.class),
"Non-zombie monster entered gamertag family");
UUID stableId = UUID.fromString("12345678-1234-5678-9abc-def012345678");
require(SanctuaryMobNames.zombieName(stableId).equals(SanctuaryMobNames.zombieName(stableId)),
"Zombie gamertag is not deterministic");
Set<String> observed = new HashSet<>();
for (int index = 0; index < 4_096; index++) {
String name = SanctuaryMobNames.zombieName(new UUID(index * 31L, ~((long) index * 97L)));
require(name.matches("[A-Za-z0-9_]+"), "Gamertag contains an invalid character: " + name);
require(name.chars().anyMatch(Character::isDigit), "Gamertag has no Xbox-style digits: " + name);
observed.add(name);
}
require(observed.size() >= 24, "Zombie gamertag pool is too small: " + observed.size());
}
private static void require(boolean condition, String message) {
if (!condition) throw new IllegalStateException(message);
}
}