Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
309d0ba6f3 |
@@ -6,6 +6,17 @@ Créer un monde **Sanctuary** en Petit, Moyen ou Grand, puis activer les ancres
|
||||
correspondantes. Le module Sanctuary Test n’est pas requis.
|
||||
[Intégration et vérification du pack beta.224](docs/four-islands-base224.md).
|
||||
|
||||
## beta.238 — Forgerie
|
||||
|
||||
[Aptitude Forgerie](docs/forgerie-beta238.md) dans Arts et métiers : quatre
|
||||
niveaux pour débloquer les enclumes et la table de forge, achat définitif.
|
||||
Les réparations, noms, améliorations en netherite et ornements conservent
|
||||
leurs règles natives. L'achat est contrôlé côté serveur, y compris pour les
|
||||
postes portés sur la tête. Icône fournie et libellés FR/EN intégrés.
|
||||
Build, 21 GameTests requis et parcours Vulkan FR/EN GUI 2/3/4 réussis.
|
||||
[MRpack local beta.238](build/Sanctuary-beta.238.mrpack) vérifié, sans
|
||||
publication ni mise à jour personnelle.
|
||||
|
||||
## beta.237 — Aptitudes acquises définitivement
|
||||
|
||||
[Aptitudes par thème et achats permanents](docs/aptitudes-beta237.md) : seuls
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# Backlog Sanctuary
|
||||
|
||||
## APT-238 — Forgerie — livré localement
|
||||
|
||||
Branche `codex/forgerie-beta238`, base beta.237. Aptitude permanente des Arts
|
||||
et métiers ; quatre niveaux pour les trois variantes d'enclume et la table
|
||||
de forge, y compris les postes portés. [Contrat et persistance](forgerie-beta238.md).
|
||||
Réparations, noms, améliorations et ornements gardent leurs règles natives.
|
||||
Build, 21 GameTests requis, Vulkan FR/EN GUI 2/3/4 et MRpack vérifiés.
|
||||
|
||||
## APT-237 — achats permanents et intégration Pause — livré localement
|
||||
|
||||
Branche `codex/progression-aptitudes`, travaux beta.235 et beta.236 réunis.
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# APT-238 — Forgerie
|
||||
|
||||
Branche `codex/forgerie-beta238`, base beta.237 (`5dee981`).
|
||||
|
||||
## Contrat avant implémentation
|
||||
|
||||
Une aptitude Forgerie est ajoutée dans Arts et métiers de Progression, avec
|
||||
l'icône `aptitudes_forgerie.png` fournie. Comme Enchantement et Alchimie,
|
||||
elle coûte quatre niveaux, est achetée une seule fois et reste acquise
|
||||
sans interrupteur. Elle débloque l'usage personnel de toutes les variantes
|
||||
d'enclume et de la table de forge. Avant l'achat, les menus natifs de ces
|
||||
postes sont refusés côté serveur, y compris les postes fonctionnels portés
|
||||
sur la tête. Les réparations, noms, améliorations et ornements natifs restent
|
||||
fonctionnels après achat. Équipements déjà obtenus et recettes conservés.
|
||||
|
||||
La meule, les fours et les autres postes restent sous leurs règles actuelles.
|
||||
Les deux aides World Map / Mob Names restent les seuls choix désactivables.
|
||||
Les interfaces ajoutées sont traduites en français et en anglais.
|
||||
|
||||
## Persistance et compatibilité
|
||||
|
||||
L'attachement booléen additif `sanctuary:smithing_learned` conserve l'achat.
|
||||
Absent : false. Copie à la mort et conservation au New Game+. Aucun schéma,
|
||||
aucune preuve ni aucun attachement existant n'est réécrit. Les personnages
|
||||
existants avec progression doivent aussi acquérir Forgerie. Les joueurs
|
||||
hors progression Sanctuary gardent l'accès vanilla. Aucun monde personnel,
|
||||
aucune génération ni aucun terrain n'est modifié.
|
||||
|
||||
La capacité `sanctuary:smithing_v1` et l'état `sanctuary:smithing_state_v1`
|
||||
transmettent l'achat sans modifier
|
||||
les paquets v1 existants. Le serveur exige un client compatible et contrôle
|
||||
la session, le joueur, la révision et les niveaux avant l'achat. Un retour
|
||||
à une version ancienne peut perdre cet attachement inconnu à la sauvegarde ;
|
||||
conserver une sauvegarde avant ce retour.
|
||||
|
||||
## Vérifications et livraison
|
||||
|
||||
```sh
|
||||
./gradlew check build assemblePack \
|
||||
-PsanctuaryFocusedTests=forgerie238,aptitudes236,headblocks \
|
||||
-PsanctuaryAtlasOnly=true
|
||||
./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true \
|
||||
-PsanctuaryAptitudes236ClientTests=true -PsanctuaryClientGraphicsBackend=vulkan
|
||||
```
|
||||
|
||||
`check build assemblePack` réussi en **2 min 44 s**, **21 GameTests requis**
|
||||
réussis : cinq nouveaux scénarios Forgerie, six scénarios d'aptitudes et dix
|
||||
scénarios de postes portés. Contrôles purs du dépôt réussis. Le banc Atlas
|
||||
évite le chemin de génération par défaut déjà documenté en beta.232.
|
||||
Log `build/forgerie238-check-build-pack.log`.
|
||||
|
||||
- Achat refusé avec trois niveaux ou une révision périmée ; quatre niveaux
|
||||
dépensés une seule fois. Autres achats et preuves inchangés. Requête de
|
||||
désactivation refusée ; conservation native NBT, à la mort et au New Game+.
|
||||
- Trois variantes d'enclume refusées avant achat puis ouvertes après achat.
|
||||
**Réparation et renommage réels d'une épée diamant** sur chacune ; XP et
|
||||
diamant consommés selon le coût natif du menu.
|
||||
- Table de forge refusée avant achat puis ouverte : **épée netherite et
|
||||
plastron diamant avec ornement Sentry réellement produits**, entrées
|
||||
consommées une fois, coût d'XP natif conservé et accès aux visites suivantes.
|
||||
- Menus construits en dehors de l'ouverture normale : validité et prise de
|
||||
résultat refusées avant acquisition, sans dépense ni perte d'entrées.
|
||||
Joueurs sans progression Sanctuary autorisés comme en vanilla.
|
||||
- Enclumes et table de forge sur la tête : contrôle de l'achat du visiteur,
|
||||
indépendamment du porteur. La fixture générale des postes natifs possède
|
||||
explicitement les trois aptitudes des Arts avant de tester leurs menus.
|
||||
|
||||
Sources et bytecode du **Minecraft 26.3 local** vérifiés pour les méthodes
|
||||
héritées de `ItemCombinerMenu`, l'override de prise d'`AnvilMenu` et les recettes
|
||||
natives de forge. Aucun changement de recette ni de dépendance externe.
|
||||
|
||||
Parcours graphique en **34 s**, Vulkan / MoltenVK 1.4.2 sur Apple M1,
|
||||
FR/EN GUI 2/3/4. La suite d'aptitudes existante contrôle désormais treize
|
||||
aptitudes, dont onze achats permanents. Trois Arts achetés depuis les boutons
|
||||
natifs : douze niveaux au total, Forgerie synchronisée depuis le serveur et
|
||||
présentée comme acquise sans interrupteur. Deux seules aides désactivables,
|
||||
XP et progression conservées lors de douze paires de changement, disposition
|
||||
Pause intégrée, défilement et retour Terminé conservés. Log
|
||||
`build/forgerie238-client-vulkan.log`, marqueur `FORGERIE238_PASS`.
|
||||
Vingt-quatre captures dans `build/forgerie238-screenshots/` ; Arts FR GUI 3
|
||||
et EN GUI 4 inspectés, texte et icône contenus dans le cadre.
|
||||
|
||||
Versions du mod, du pack et du manifeste alignées sur **beta.238**.
|
||||
[MRpack local](../build/Sanctuary-beta.238.mrpack) vérifié : 12802487 octets,
|
||||
intégrité ZIP, Minecraft 26.3 / Fabric Loader 0.19.5, un seul JAR Sanctuary
|
||||
identique au build final, sans classe de test, monde ou module de laboratoire.
|
||||
Les trois icônes fournies des Arts sont identiques octet pour octet aux
|
||||
originaux dans le JAR. Reçu `build/forgerie238-artifact.json`.
|
||||
|
||||
- SHA-256 JAR : `e29ea6f7f56c57290d0307076f641f2e714ec72a80be858a5fd9c5f08e27eb8e`.
|
||||
- SHA-256 MRpack : `2fbd605acf555dd8b192c0c0ad7171414a13f5820e5d440ba54debcd4cb897a7`.
|
||||
|
||||
Tests dans des mondes de développement neufs. Pas de validation Windows ni
|
||||
multijoueur distant. Les sauvegardes et installations personnelles restent
|
||||
intactes ; aucune publication ni avancée du canal packwiz.
|
||||
+2
-2
@@ -9,8 +9,8 @@ loom_version=1.17.20
|
||||
fabric_api_version=0.160.5+26.3
|
||||
|
||||
# Release counter: beta.001, .002, .003, ... (see docs/versioning.md).
|
||||
mod_version=beta.237
|
||||
pack_version=beta.237
|
||||
mod_version=beta.238
|
||||
pack_version=beta.238
|
||||
resource_pack_version=beta.121
|
||||
maven_group=fr.koka.sanctuary
|
||||
jei_version=30.32.0-sanctuary.3
|
||||
|
||||
@@ -228,7 +228,7 @@ tasks.named('processGametestResources') {
|
||||
|
||||
def villageOnly = providers.gradleProperty('sanctuaryVillageOnly').map { it.toBoolean() }.getOrElse(false)
|
||||
def focused = providers.gradleProperty('sanctuaryFocusedTests').getOrElse('').split(',').findAll { !it.isEmpty() }
|
||||
def focusClasses = [aptitudes236: 'Aptitudes236GameTests', solo232: 'Solo232GameTests', diagonal231: 'Diagonal231GameTests', diagonal230: 'Diagonal230GameTests', diagonal229: 'Diagonal229GameTests', diagonal228: 'Diagonal228GameTests', diagonal227: 'Diagonal227GameTests', diagonal226: 'Diagonal226GameTests', diagonal225: 'Diagonal225GameTests', east224: 'East224GameTests', east223: 'East223GameTests', rock222: 'YellowRock222GameTests', west221: 'West221GameTests', west220: 'West220GameTests', west219: 'West219GameTests', west218: 'West218GameTests', west217: 'West217GameTests', south216: 'South216GameTests', relays215: 'Relays215GameTests', south214: 'South214GameTests', south213: 'South213GameTests', south212: 'South212GameTests', south211: 'South211GameTests', south210: 'South210GameTests', south209: 'South209GameTests', north208: 'North208GameTests', north207: 'North207GameTests', north206: 'North206GameTests', north205: 'North205GameTests', base204: 'Base204GameTests', palace173: 'Palace173GameTests', horde172: 'Horde172GameTests', horde171: 'Horde171GameTests', horde170: 'Horde170GameTests', horde169: 'Horde169GameTests', horde168: 'Horde168GameTests', horde167: 'Horde167GameTests', plans072: 'Plans072GameTests', starter052: 'Starter052GameTests', dispenser051: 'Dispenser051GameTests', head050: 'Head050GameTests', boatshead: 'BoatsHead049GameTests', poultry: 'Poultry048GameTests', starinspect: 'StarInspection047GameTests', skyflight: 'SkyFlight046GameTests', eggaudit: 'Companion044GameTests', headblocks: 'HeadBlocks043GameTests', operator: 'OperatorStars040GameTests', starter: 'Starter039GameTests', gravelayout: 'GraveInventory039GameTests', foodknowledge: 'FoodKnowledge039GameTests', graves: 'GravesFood038GameTests', realtime: 'Realtime037GameTests', corrections: 'Corrections036GameTests', collections: 'Collections035GameTests', notifications: 'Notifications034GameTests', menus: 'Menus033GameTests', aerial: 'Aerial24WorldGameTests', transit: 'Transit29WorldGameTests', village: 'Village306GameTests', drained: 'Drained307GameTests', expeditions: 'AncientExpeditions001GameTests', progression: 'Progression003GameTests', map: 'Atlas005GameTests', demeure: 'Demeure006GameTests', mining: 'Mining008GameTests', building: 'Building009GameTests', inventory: 'Inventory010GameTests', cycle: 'Cycle011GameTests', inventoryflow: 'Inventory012GameTests', sorting: 'Inventory014GameTests', tutorial: 'Tutorial016GameTests', movement: 'Movement017GameTests', accessories: 'Accessory018GameTests', companions: 'Companion019GameTests', familiar: 'Familiar020GameTests', carry: 'Carry027GameTests', familiarhit: 'Familiar029GameTests', refonte: 'Companion032GameTests', lights: 'Dynamic030GameTests', cosmetics: 'HeadCosmetic021GameTests', startup: 'StartupPerformanceGameTests', recipes: 'Recipes024GameTests']
|
||||
def focusClasses = [forgerie238: 'Forgerie238GameTests', aptitudes236: 'Aptitudes236GameTests', solo232: 'Solo232GameTests', diagonal231: 'Diagonal231GameTests', diagonal230: 'Diagonal230GameTests', diagonal229: 'Diagonal229GameTests', diagonal228: 'Diagonal228GameTests', diagonal227: 'Diagonal227GameTests', diagonal226: 'Diagonal226GameTests', diagonal225: 'Diagonal225GameTests', east224: 'East224GameTests', east223: 'East223GameTests', rock222: 'YellowRock222GameTests', west221: 'West221GameTests', west220: 'West220GameTests', west219: 'West219GameTests', west218: 'West218GameTests', west217: 'West217GameTests', south216: 'South216GameTests', relays215: 'Relays215GameTests', south214: 'South214GameTests', south213: 'South213GameTests', south212: 'South212GameTests', south211: 'South211GameTests', south210: 'South210GameTests', south209: 'South209GameTests', north208: 'North208GameTests', north207: 'North207GameTests', north206: 'North206GameTests', north205: 'North205GameTests', base204: 'Base204GameTests', palace173: 'Palace173GameTests', horde172: 'Horde172GameTests', horde171: 'Horde171GameTests', horde170: 'Horde170GameTests', horde169: 'Horde169GameTests', horde168: 'Horde168GameTests', horde167: 'Horde167GameTests', plans072: 'Plans072GameTests', starter052: 'Starter052GameTests', dispenser051: 'Dispenser051GameTests', head050: 'Head050GameTests', boatshead: 'BoatsHead049GameTests', poultry: 'Poultry048GameTests', starinspect: 'StarInspection047GameTests', skyflight: 'SkyFlight046GameTests', eggaudit: 'Companion044GameTests', headblocks: 'HeadBlocks043GameTests', operator: 'OperatorStars040GameTests', starter: 'Starter039GameTests', gravelayout: 'GraveInventory039GameTests', foodknowledge: 'FoodKnowledge039GameTests', graves: 'GravesFood038GameTests', realtime: 'Realtime037GameTests', corrections: 'Corrections036GameTests', collections: 'Collections035GameTests', notifications: 'Notifications034GameTests', menus: 'Menus033GameTests', aerial: 'Aerial24WorldGameTests', transit: 'Transit29WorldGameTests', village: 'Village306GameTests', drained: 'Drained307GameTests', expeditions: 'AncientExpeditions001GameTests', progression: 'Progression003GameTests', map: 'Atlas005GameTests', demeure: 'Demeure006GameTests', mining: 'Mining008GameTests', building: 'Building009GameTests', inventory: 'Inventory010GameTests', cycle: 'Cycle011GameTests', inventoryflow: 'Inventory012GameTests', sorting: 'Inventory014GameTests', tutorial: 'Tutorial016GameTests', movement: 'Movement017GameTests', accessories: 'Accessory018GameTests', companions: 'Companion019GameTests', familiar: 'Familiar020GameTests', carry: 'Carry027GameTests', familiarhit: 'Familiar029GameTests', refonte: 'Companion032GameTests', lights: 'Dynamic030GameTests', cosmetics: 'HeadCosmetic021GameTests', startup: 'StartupPerformanceGameTests', recipes: 'Recipes024GameTests']
|
||||
focused.each { if (!focusClasses.containsKey(it)) throw new GradleException('Unknown focused native test: ' + it) }
|
||||
inputs.property('focusedNative', focused)
|
||||
def yellowRock222Client = providers.gradleProperty('sanctuaryYellowRock222ClientTests').map { it.toBoolean() }.getOrElse(false)
|
||||
|
||||
+8
-8
@@ -25,7 +25,7 @@ public final class Aptitudes236ClientChecks implements FabricClientGameTest {
|
||||
Map.entry("sorting","sanctuary.progression.sorting"),Map.entry("swimming","sanctuary.progression.swimming"),
|
||||
Map.entry("resting","sanctuary.progression.resting"),Map.entry("active_bond","sanctuary.companion.link"),
|
||||
Map.entry("familiar_study","sanctuary.companion.study"),Map.entry("catalogue","sanctuary.recipes.catalogue"),
|
||||
Map.entry("zoom","sanctuary.zoom.title"),Map.entry("enchanting","sanctuary.progression.enchanting"),Map.entry("alchemy","sanctuary.progression.alchemy"));
|
||||
Map.entry("zoom","sanctuary.zoom.title"),Map.entry("enchanting","sanctuary.progression.enchanting"),Map.entry("alchemy","sanctuary.progression.alchemy"),Map.entry("smithing","sanctuary.progression.smithing"));
|
||||
@Override public void runTest(ClientGameTestContext c) {
|
||||
var intro=new AtomicBoolean(true);fr.koka.sanctuary.testing.QuickTestWorld.enable();
|
||||
c.runOnClient(m->{m.options.renderDistance().set(5);m.options.simulationDistance().set(5);m.options.pauseOnLostFocus=false;});
|
||||
@@ -34,17 +34,17 @@ public final class Aptitudes236ClientChecks implements FabricClientGameTest {
|
||||
screen.children().stream().filter(Button.class::isInstance).map(Button.class::cast).filter(b->b.getClass().getSimpleName().equals("ColorButton")).findFirst().orElseThrow().onPress(enter());
|
||||
press(screen,"sanctuary.progression.enter");intro.set(false);
|
||||
}});
|
||||
try(var world=c.worldBuilder().adjustSettings(s->{s.setName("Aptitudes237 "+UUID.randomUUID());s.setSeed("237");s.setAllowCommands(false);s.setGameMode(WorldCreationUiState.SelectedGameMode.SURVIVAL);s.setWorldType(s.getNormalPresetList().stream().filter(e->e.preset()!=null&&e.preset().getRegisteredName().equals("minecraft:flat")).findFirst().orElseThrow());}).create()) {
|
||||
try(var world=c.worldBuilder().adjustSettings(s->{s.setName("Forgerie238 "+UUID.randomUUID());s.setSeed("238");s.setAllowCommands(false);s.setGameMode(WorldCreationUiState.SelectedGameMode.SURVIVAL);s.setWorldType(s.getNormalPresetList().stream().filter(e->e.preset()!=null&&e.preset().getRegisteredName().equals("minecraft:flat")).findFirst().orElseThrow());}).create()) {
|
||||
c.waitFor(m->ProgressionClient.active() && m.player!=null && m.gui.screen()==null && !IntroSpawnFlash.active(),1200);
|
||||
var server=world.getServer();
|
||||
server.runOnServer(s->{var p=s.getPlayerList().getPlayers().getFirst();p.getAbilities().invulnerable=true;p.setExperienceLevels(100);ProgressionService.send(p,"");});
|
||||
c.waitFor(m->m.player.experienceLevel==100,200);
|
||||
c.runOnClient(m->{m.gui.setScreen(new ProgressionScreen(new PauseScreen(true)));check(!CraftAptitudes.learned(m.player,"enchanting") && !CraftAptitudes.learned(m.player,"alchemy"),"New workstations start locked");});
|
||||
for(String craft:List.of("enchanting","alchemy")) {
|
||||
c.runOnClient(m->{m.gui.setScreen(new ProgressionScreen(new PauseScreen(true)));check(!CraftAptitudes.learned(m.player,"enchanting") && !CraftAptitudes.learned(m.player,"alchemy") && !CraftAptitudes.learned(m.player,"smithing"),"New workstations start locked");});
|
||||
for(String craft:List.of("enchanting","alchemy","smithing")) {
|
||||
c.runOnClient(m->{var button=afterTitle(m.gui.screen(),TITLES.get(craft));check(key(button).equals("sanctuary.progression.unlock_cost")&&button.active,"Locked craft offers its normal purchase button");button.onPress(enter());});
|
||||
c.waitFor(m->CraftAptitudes.learned(m.player,craft) && key(afterTitle(m.gui.screen(),TITLES.get(craft))).equals("sanctuary.progression.aptitude_acquired"),200);
|
||||
}
|
||||
c.runOnClient(m->check(m.player.experienceLevel==92,"Two UI purchases cost eight levels"));
|
||||
c.runOnClient(m->check(m.player.experienceLevel==88,"Three UI purchases cost twelve levels"));
|
||||
server.runOnServer(s->{var p=s.getPlayerList().getPlayers().getFirst();p.setExperienceLevels(100);
|
||||
for(String target:ProgressRecord.APTITUDES)check(ProgressionService.purchase(p,target,ProgressEffects.get(p).revision()),"Server acquires "+target);
|
||||
check(CompanionService.buy(p),"Active Bond bought");check(CompanionStudy.buy(p),"Familiar Study bought");
|
||||
@@ -81,14 +81,14 @@ public final class Aptitudes236ClientChecks implements FabricClientGameTest {
|
||||
c.waitFor(m->AptitudeClient.enabled(target) && key(afterTitle(m.gui.screen(),TITLES.get(target))).equals("sanctuary.progression.aptitude_enabled"),200);
|
||||
}
|
||||
c.runOnClient(m->{m.gui.setScreen(new PauseScreen(true));pauseLayout(m.gui.screen(),true);});shot(c,"pause-map-enabled-"+language+"-"+scale);
|
||||
c.runOnClient(m->{m.gui.setScreen(new ProgressionScreen(new PauseScreen(true)));reveal(m.gui.screen(),afterTitle(m.gui.screen(),TITLES.get("alchemy")));});
|
||||
c.runOnClient(m->{m.gui.setScreen(new ProgressionScreen(new PauseScreen(true)));reveal(m.gui.screen(),afterTitle(m.gui.screen(),TITLES.get("smithing")));});
|
||||
shot(c,"crafts-"+language+"-"+scale);
|
||||
c.runOnClient(m->{var screen=m.gui.screen();var area=aptitudes(screen);area.setScrollAmount(100);});
|
||||
c.runOnClient(m->{m.getWindow().setWindowed(1344,1008);m.resizeGui();});c.waitTicks(3);
|
||||
c.runOnClient(m->{check(aptitudes(m.gui.screen()).scrollAmount()==100,"Resize retains aptitude scroll");layout(m.gui.screen());press(m.gui.screen(),"gui.done");check(m.gui.screen() instanceof PauseScreen,"Done returns to Pause");m.gui.setScreen(null);});
|
||||
}
|
||||
server.runOnServer(s->{var p=s.getPlayerList().getPlayers().getFirst();check(p.experienceLevel==xp && p.getAttached(ProgressionService.RECORD).equals(purchaseProof),"Twelve disable/reactivate pairs preserve XP and purchases");});
|
||||
System.out.println("APTITUDES237_PASS: Vulkan FR/EN GUI 2/3/4, five themes, two real server toggles, ten permanent purchases, legacy preferences and merged Pause layout, two native UI purchases, preserved XP/purchases, scroll/resize and Done");
|
||||
System.out.println("FORGERIE238_PASS: Vulkan FR/EN GUI 2/3/4, five themes, two real server toggles, eleven permanent purchases, legacy preferences and merged Pause layout, three native UI purchases, preserved XP/purchases, scroll/resize and Done");
|
||||
}finally{intro.set(false);}
|
||||
}
|
||||
private static void pauseLayout(Screen screen,boolean enabled) {Pause233ClientChecks.pause(screen,enabled);}
|
||||
@@ -101,5 +101,5 @@ public final class Aptitudes236ClientChecks implements FabricClientGameTest {
|
||||
private static AbstractScrollArea aptitudes(Screen screen){return widgets(screen).filter(AbstractScrollArea.class::isInstance).map(AbstractScrollArea.class::cast).max(Comparator.comparingInt(AbstractWidget::getX)).orElseThrow();}
|
||||
private static void reveal(Screen screen,AbstractWidget widget){var area=aptitudes(screen);area.setScrollAmount(Math.clamp(area.scrollAmount()+widget.getBottom()-area.getBottom()+4,0,area.maxScrollAmount()));}
|
||||
private static void layout(Screen screen){var area=aptitudes(screen);check(area.getX()>=0&&area.getRight()<=screen.width&&area.getBottom()<screen.height-26,"Aptitudes viewport stays inside native frame");for(var button:widgets(screen).filter(Button.class::isInstance).map(Button.class::cast).toList())check(button.getX()>=0&&button.getRight()<=screen.width,"Buttons remain within the screen horizontally");}
|
||||
private static void shot(ClientGameTestContext c,String name){var source=c.takeScreenshot("aptitudes237-"+name);try{var dir=FabricLoader.getInstance().getGameDir().resolve("../../../../../build/aptitudes237-screenshots").normalize();Files.createDirectories(dir);Files.copy(source,dir.resolve(name+".png"),StandardCopyOption.REPLACE_EXISTING);}catch(Exception e){throw new AssertionError(e);}}
|
||||
private static void shot(ClientGameTestContext c,String name){var source=c.takeScreenshot("forgerie238-"+name);try{var dir=FabricLoader.getInstance().getGameDir().resolve("../../../../../build/forgerie238-screenshots").normalize();Files.createDirectories(dir);Files.copy(source,dir.resolve(name+".png"),StandardCopyOption.REPLACE_EXISTING);}catch(Exception e){throw new AssertionError(e);}}
|
||||
}
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package fr.koka.sanctuary.gametest;
|
||||
|
||||
import fr.koka.sanctuary.progression.*;
|
||||
import fr.koka.sanctuary.inventory.*;
|
||||
import fr.koka.sanctuary.cosmetics.HeadBlockService;
|
||||
import java.util.UUID;
|
||||
import net.fabricmc.fabric.api.gametest.v1.GameTest;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.inventory.*;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.storage.*;
|
||||
import net.minecraft.util.ProblemReporter;
|
||||
|
||||
/** Purchase persistence and real native repairs/upgrades, including worn workstations. */
|
||||
public final class Forgerie238GameTests {
|
||||
@GameTest public void purchaseIsPermanentAndSurvivesSaveDeathAndCycle(GameTestHelper h) {
|
||||
var r=ProgressRecord.INITIAL;
|
||||
for(String skill:ProgressRecord.SKILLS)while(r.rank(skill)<ProgressRecord.cap(skill))r=r.buy(skill,r.cost(skill,ProgressionConfig.DEFAULT));
|
||||
var p=player(h,r);String proof=p.getAttached(ProgressionService.RECORD);
|
||||
h.assertTrue(!p.hasAttached(CraftAptitudes.SMITHING) && !CraftAptitudes.learned(p,"smithing"),"Existing players start without the new purchase");
|
||||
p.setExperienceLevels(3);h.assertTrue(!CraftAptitudes.buy(p,"smithing",r.revision()) && p.experienceLevel==3,"Three levels cannot unlock Forgerie");
|
||||
p.setExperienceLevels(4);h.assertTrue(!CraftAptitudes.buy(p,"smithing",r.revision()-1) && p.experienceLevel==4,"Stale purchase cannot spend XP");
|
||||
h.assertTrue(CraftAptitudes.buy(p,"smithing",r.revision()) && p.experienceLevel==0,"Four levels acquire Forgerie once");
|
||||
p.setExperienceLevels(8);h.assertTrue(!CraftAptitudes.buy(p,"smithing",r.revision()) && p.experienceLevel==8,"Repeat purchase costs nothing");
|
||||
h.assertTrue(!AptitudeService.set(p,new AptitudeService.Set("smithing",false,0)) && CraftAptitudes.allowed(p,"smithing"),"Bought Forgerie has no disable request");
|
||||
h.assertTrue(!CraftAptitudes.learned(p,"enchanting") && !CraftAptitudes.learned(p,"alchemy") && proof.equals(p.getAttached(ProgressionService.RECORD)),"Other purchases and progression proofs stay independent");
|
||||
var output=TagValueOutput.createWithContext(ProblemReporter.DISCARDING,h.getLevel().registryAccess());p.saveWithoutId(output);
|
||||
var loaded=Progression003GameTests.player(h);loaded.load(TagValueInput.create(ProblemReporter.DISCARDING,h.getLevel().registryAccess(),output.buildResult()));
|
||||
h.assertTrue(CraftAptitudes.learned(loaded,"smithing") && loaded.experienceLevel==8 && proof.equals(loaded.getAttached(ProgressionService.RECORD)),"Native NBT reload preserves the purchase and XP");
|
||||
var respawn=Progression003GameTests.player(h);respawn.setUUID(p.getUUID());respawn.restoreFrom(p,false);
|
||||
ServerPlayerEvents.AFTER_RESPAWN.invoker().afterRespawn(p,respawn,false);
|
||||
h.assertTrue(CraftAptitudes.learned(respawn,"smithing"),"Forgerie survives ordinary death");
|
||||
bind(p,r.nextCycle());h.assertTrue(CraftAptitudes.learned(p,"smithing") && CraftAptitudes.allowed(p,"smithing"),"Forgerie survives New Game+");h.succeed();
|
||||
}
|
||||
@GameTest public void allAnvilsRequirePurchaseThenRepairAndRename(GameTestHelper h) {
|
||||
var p=player(h,ProgressRecord.INITIAL);var at=p.blockPosition();h.getLevel().setBlockAndUpdate(at.below(),Blocks.STONE.defaultBlockState());
|
||||
for(var block:new net.minecraft.world.level.block.Block[]{Blocks.ANVIL,Blocks.CHIPPED_ANVIL,Blocks.DAMAGED_ANVIL}) {
|
||||
h.getLevel().setBlockAndUpdate(at,block.defaultBlockState());
|
||||
h.assertTrue(p.openMenu(h.getLevel().getBlockState(at).getMenuProvider(h.getLevel(),at)).isEmpty(),"Every anvil variant is locked before purchase");
|
||||
}
|
||||
p.setExperienceLevels(40);h.assertTrue(CraftAptitudes.buy(p,"smithing",0) && p.experienceLevel==36,"Anvil unlock charges four levels");
|
||||
for(var block:new net.minecraft.world.level.block.Block[]{Blocks.ANVIL,Blocks.CHIPPED_ANVIL,Blocks.DAMAGED_ANVIL}) {
|
||||
h.getLevel().setBlockAndUpdate(at,block.defaultBlockState());
|
||||
h.assertTrue(p.openMenu(h.getLevel().getBlockState(at).getMenuProvider(h.getLevel(),at)).isPresent(),"Every acquired anvil variant opens");
|
||||
var menu=(AnvilMenu)p.containerMenu;var sword=new ItemStack(Items.DIAMOND_SWORD);sword.setDamageValue(100);
|
||||
menu.getSlot(AnvilMenu.INPUT_SLOT).set(sword);menu.getSlot(AnvilMenu.ADDITIONAL_SLOT).set(new ItemStack(Items.DIAMOND));
|
||||
h.assertTrue(menu.setItemName("Forgerie238") && menu.stillValid(p),"Native anvil accepts a repair and name");
|
||||
int cost=menu.getCost(),xp=p.experienceLevel;
|
||||
var result=menu.getSlot(AnvilMenu.RESULT_SLOT).safeTake(1,1,p);
|
||||
h.assertTrue(result.is(Items.DIAMOND_SWORD) && result.getDamageValue()==0 && result.get(DataComponents.CUSTOM_NAME).getString().equals("Forgerie238"),"Native result is repaired and renamed");
|
||||
h.assertTrue(cost>0 && p.experienceLevel==xp-cost && menu.getSlot(AnvilMenu.ADDITIONAL_SLOT).getItem().isEmpty(),"Native operation consumes its normal XP and repair material");p.closeContainer();
|
||||
}
|
||||
h.succeed();
|
||||
}
|
||||
@GameTest public void smithingTableUpgradesAndTrimsAfterPurchase(GameTestHelper h) {
|
||||
var p=player(h,ProgressRecord.INITIAL);var at=p.blockPosition();h.getLevel().setBlockAndUpdate(at,Blocks.SMITHING_TABLE.defaultBlockState());
|
||||
var provider=h.getLevel().getBlockState(at).getMenuProvider(h.getLevel(),at);
|
||||
h.assertTrue(p.openMenu(provider).isEmpty(),"Smithing table is locked before purchase");
|
||||
p.setExperienceLevels(4);h.assertTrue(CraftAptitudes.buy(p,"smithing",0) && p.openMenu(provider).isPresent(),"Same purchase opens smithing table");
|
||||
var menu=(SmithingMenu)p.containerMenu;
|
||||
inputs(menu,Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE,Items.DIAMOND_SWORD,Items.NETHERITE_INGOT);
|
||||
var upgrade=menu.getSlot(SmithingMenu.RESULT_SLOT).safeTake(1,1,p);
|
||||
h.assertTrue(upgrade.is(Items.NETHERITE_SWORD) && p.experienceLevel==0,"Real netherite upgrade works without extra XP");
|
||||
h.assertTrue(menu.getSlot(0).getItem().isEmpty() && menu.getSlot(1).getItem().isEmpty() && menu.getSlot(2).getItem().isEmpty(),"Upgrade consumes each native input once");
|
||||
inputs(menu,Items.SENTRY_ARMOR_TRIM_SMITHING_TEMPLATE,Items.DIAMOND_CHESTPLATE,Items.REDSTONE);
|
||||
var trimmed=menu.getSlot(SmithingMenu.RESULT_SLOT).safeTake(1,1,p);
|
||||
h.assertTrue(trimmed.is(Items.DIAMOND_CHESTPLATE) && trimmed.has(DataComponents.TRIM),"Native armor trim remains available");
|
||||
p.closeContainer();h.assertTrue(p.openMenu(provider).isPresent(),"Forgerie remains available on later visits");p.closeContainer();h.succeed();
|
||||
}
|
||||
@GameTest public void forgedMenusCannotYieldAnUnownedResult(GameTestHelper h) {
|
||||
var p=player(h,ProgressRecord.INITIAL);p.setExperienceLevels(20);var at=p.blockPosition();
|
||||
h.getLevel().setBlockAndUpdate(at,Blocks.ANVIL.defaultBlockState());
|
||||
var anvil=new AnvilMenu(7,p.getInventory(),ContainerLevelAccess.create(h.getLevel(),at));
|
||||
anvil.getSlot(0).set(new ItemStack(Items.DIAMOND_SWORD));anvil.setItemName("Forbidden");
|
||||
h.assertTrue(anvil.getSlot(AnvilMenu.RESULT_SLOT).hasItem() && !anvil.stillValid(p) && !anvil.getSlot(AnvilMenu.RESULT_SLOT).mayPickup(p),"Anvil constructed outside normal opening is still locked");
|
||||
h.assertTrue(anvil.getSlot(AnvilMenu.RESULT_SLOT).safeTake(1,1,p).isEmpty() && p.experienceLevel==20 && anvil.getSlot(0).hasItem(),"Rejected anvil take changes neither XP nor inputs");anvil.removed(p);
|
||||
h.getLevel().setBlockAndUpdate(at,Blocks.SMITHING_TABLE.defaultBlockState());
|
||||
var forge=new SmithingMenu(8,p.getInventory(),ContainerLevelAccess.create(h.getLevel(),at));inputs(forge,Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE,Items.DIAMOND_SWORD,Items.NETHERITE_INGOT);
|
||||
h.assertTrue(forge.getSlot(SmithingMenu.RESULT_SLOT).hasItem() && !forge.stillValid(p) && !forge.getSlot(SmithingMenu.RESULT_SLOT).mayPickup(p),"Smithing's inherited result gate rejects an unowned menu");
|
||||
h.assertTrue(forge.getSlot(SmithingMenu.RESULT_SLOT).safeTake(1,1,p).isEmpty() && forge.getSlot(1).hasItem(),"Rejected smithing take never consumes its base item");forge.removed(p);
|
||||
var vanilla=Progression003GameTests.player(h);h.assertTrue(CraftAptitudes.allowed(vanilla,"smithing"),"Players outside Sanctuary progression retain vanilla access");h.succeed();
|
||||
}
|
||||
@GameTest public void wornWorkstationsRespectTheVisitorsPurchase(GameTestHelper h) {
|
||||
var owner=player(h,ProgressRecord.INITIAL);var actor=player(h,ProgressRecord.INITIAL);actor.setPos(owner.position().add(1,0,0));
|
||||
Item[] items={Items.ANVIL,Items.CHIPPED_ANVIL,Items.DAMAGED_ANVIL,Items.SMITHING_TABLE};
|
||||
for(var item:items) {
|
||||
AccessoryAccess.of(owner).setItem(AccessoryContainer.HEAD,new ItemStack(item));
|
||||
HeadBlockService.use(actor,owner,false);h.assertTrue(actor.containerMenu==actor.inventoryMenu,"Worn workstation cannot bypass the visitor's locked aptitude: "+item);
|
||||
}
|
||||
actor.setExperienceLevels(4);h.assertTrue(CraftAptitudes.buy(actor,"smithing",0),"Visitor purchases Forgerie");
|
||||
for(var item:items) {
|
||||
AccessoryAccess.of(owner).setItem(AccessoryContainer.HEAD,new ItemStack(item));
|
||||
h.assertTrue(HeadBlockService.use(actor,owner,false) && actor.containerMenu!=actor.inventoryMenu,"Visitor uses acquired worn workstation: "+item);
|
||||
var menu=actor.containerMenu;h.assertTrue(HeadBlockService.validMenu(menu,actor,()->menu.stillValid(actor)),"Native worn menu stays valid for learned visitor");actor.closeContainer();
|
||||
}
|
||||
AccessoryAccess.of(owner).removeItemNoUpdate(AccessoryContainer.HEAD);h.succeed();
|
||||
}
|
||||
private static void inputs(SmithingMenu menu,Item template,Item base,Item addition) {menu.getSlot(0).set(new ItemStack(template));menu.getSlot(1).set(new ItemStack(base));menu.getSlot(2).set(new ItemStack(addition));}
|
||||
private static ServerPlayer player(GameTestHelper h,ProgressRecord record) {
|
||||
var p=Progression003GameTests.player(h);p.setUUID(UUID.randomUUID());
|
||||
try {var identities=ProgressionService.session(h.getLevel().getServer()).inhabitants;identities.create(p.getUUID(),"forge"+p.getUUID().toString().substring(0,8),"",identities.available().getFirst());}
|
||||
catch(Exception e){throw new AssertionError(e);}
|
||||
bind(p,record);return p;
|
||||
}
|
||||
private static void bind(ServerPlayer p,ProgressRecord record){p.setAttached(ProgressionService.RECORD,record.json());ProgressEffects.apply(p,record);}
|
||||
}
|
||||
+1
@@ -68,6 +68,7 @@ public class HeadBlocks043GameTests {
|
||||
}
|
||||
@GameTest public void nativeWorkstationsOpenAndCloseAfterMovement(GameTestHelper h){
|
||||
var owner=player(h);var actor=player(h);actor.setPos(owner.position().add(1,0,0));
|
||||
for(var target:new String[]{"enchanting","alchemy","smithing"}){actor.setExperienceLevels(4);h.assertTrue(CraftAptitudes.buy(actor,target,0),"Native workstation fixture owns "+target);}
|
||||
Item[] items={Items.CRAFTING_TABLE,Items.CHEST,Items.TRAPPED_CHEST,Items.BARREL,Items.ENDER_CHEST,Items.SHULKER_BOX,
|
||||
Items.FURNACE,Items.BLAST_FURNACE,Items.SMOKER,Items.BREWING_STAND,Items.HOPPER,Items.DISPENSER,Items.DROPPER,
|
||||
Items.CRAFTER,Items.STONECUTTER,Items.SMITHING_TABLE,Items.LOOM,Items.CARTOGRAPHY_TABLE,Items.GRINDSTONE,Items.ANVIL,Items.ENCHANTING_TABLE,Items.BEACON};
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"entrypoints": {
|
||||
"client": ["fr.koka.sanctuary.gametest.Starter039TestSupport"],
|
||||
"preLaunch": ["fr.koka.sanctuary.gametest.Inventory010MixinAudit"],
|
||||
"fabric-gametest": ["fr.koka.sanctuary.gametest.Aptitudes236GameTests","fr.koka.sanctuary.gametest.Diagonal231GameTests", "fr.koka.sanctuary.gametest.Diagonal230GameTests", "fr.koka.sanctuary.gametest.Diagonal229GameTests", "fr.koka.sanctuary.gametest.Diagonal228GameTests", "fr.koka.sanctuary.gametest.Diagonal227GameTests", "fr.koka.sanctuary.gametest.Diagonal226GameTests", "fr.koka.sanctuary.gametest.Diagonal225GameTests", "fr.koka.sanctuary.gametest.East224GameTests", "fr.koka.sanctuary.gametest.East223GameTests", "fr.koka.sanctuary.gametest.YellowRock222GameTests", "fr.koka.sanctuary.gametest.West221GameTests", "fr.koka.sanctuary.gametest.West220GameTests", "fr.koka.sanctuary.gametest.West219GameTests", "fr.koka.sanctuary.gametest.West218GameTests", "fr.koka.sanctuary.gametest.West217GameTests", "fr.koka.sanctuary.gametest.Base204GameTests", "fr.koka.sanctuary.gametest.North205GameTests", "fr.koka.sanctuary.gametest.Palace173GameTests", "fr.koka.sanctuary.gametest.Plans072GameTests", "fr.koka.sanctuary.gametest.Starter052GameTests", "fr.koka.sanctuary.gametest.Dispenser051GameTests", "fr.koka.sanctuary.gametest.Head050GameTests", "fr.koka.sanctuary.gametest.Poultry048GameTests", "fr.koka.sanctuary.gametest.StarInspection047GameTests", "fr.koka.sanctuary.gametest.SkyFlight046GameTests", "fr.koka.sanctuary.gametest.GravesFood038GameTests","fr.koka.sanctuary.gametest.Realtime037GameTests","fr.koka.sanctuary.gametest.Corrections036GameTests","fr.koka.sanctuary.gametest.Collections035GameTests","fr.koka.sanctuary.gametest.Notifications034GameTests","fr.koka.sanctuary.gametest.Menus033GameTests","${gametest_entrypoint}", "fr.koka.sanctuary.gametest.Swamp27WorldGameTests", "fr.koka.sanctuary.gametest.Caves28WorldGameTests", "fr.koka.sanctuary.gametest.Terraces29WorldGameTests", "fr.koka.sanctuary.gametest.River30WorldGameTests", "fr.koka.sanctuary.gametest.Transit29WorldGameTests", "fr.koka.sanctuary.gametest.Alpha28NativeStructuresGameTests", "fr.koka.sanctuary.gametest.Village306GameTests", "fr.koka.sanctuary.gametest.Drained307GameTests", "fr.koka.sanctuary.gametest.BlockKnowledgeGameTests", "fr.koka.sanctuary.gametest.CensusGameTestsTerrain", "fr.koka.sanctuary.gametest.CensusStockGameTests", "fr.koka.sanctuary.gametest.MaterialActivityGameTests", "fr.koka.sanctuary.gametest.AncientExpeditions001GameTests", "fr.koka.sanctuary.gametest.Progression003GameTests", "fr.koka.sanctuary.gametest.Atlas005GameTests", "fr.koka.sanctuary.gametest.Demeure006GameTests", "fr.koka.sanctuary.gametest.Mining008GameTests", "fr.koka.sanctuary.gametest.Building009GameTests", "fr.koka.sanctuary.gametest.Inventory010GameTests", "fr.koka.sanctuary.gametest.Cycle011GameTests", "fr.koka.sanctuary.gametest.Inventory012GameTests", "fr.koka.sanctuary.gametest.Inventory014GameTests", "fr.koka.sanctuary.gametest.Tutorial016GameTests", "fr.koka.sanctuary.gametest.Movement017GameTests", "fr.koka.sanctuary.gametest.Accessory018GameTests", "fr.koka.sanctuary.gametest.Companion019GameTests", "fr.koka.sanctuary.gametest.Companion032GameTests", "fr.koka.sanctuary.gametest.Recipes024GameTests", "fr.koka.sanctuary.gametest.Carry027GameTests", "fr.koka.sanctuary.gametest.Familiar029GameTests", "fr.koka.sanctuary.gametest.Dynamic030GameTests", "fr.koka.sanctuary.gametest.GraveInventory039GameTests", "fr.koka.sanctuary.gametest.FoodKnowledge039GameTests", "fr.koka.sanctuary.gametest.OperatorStars040GameTests", "fr.koka.sanctuary.gametest.Starter039GameTests", "fr.koka.sanctuary.gametest.Horde167GameTests", "fr.koka.sanctuary.gametest.Horde168GameTests", "fr.koka.sanctuary.gametest.Horde169GameTests", "fr.koka.sanctuary.gametest.Horde170GameTests", "fr.koka.sanctuary.gametest.Horde171GameTests", "fr.koka.sanctuary.gametest.Horde172GameTests"],
|
||||
"fabric-gametest": ["fr.koka.sanctuary.gametest.Forgerie238GameTests","fr.koka.sanctuary.gametest.Aptitudes236GameTests","fr.koka.sanctuary.gametest.Diagonal231GameTests", "fr.koka.sanctuary.gametest.Diagonal230GameTests", "fr.koka.sanctuary.gametest.Diagonal229GameTests", "fr.koka.sanctuary.gametest.Diagonal228GameTests", "fr.koka.sanctuary.gametest.Diagonal227GameTests", "fr.koka.sanctuary.gametest.Diagonal226GameTests", "fr.koka.sanctuary.gametest.Diagonal225GameTests", "fr.koka.sanctuary.gametest.East224GameTests", "fr.koka.sanctuary.gametest.East223GameTests", "fr.koka.sanctuary.gametest.YellowRock222GameTests", "fr.koka.sanctuary.gametest.West221GameTests", "fr.koka.sanctuary.gametest.West220GameTests", "fr.koka.sanctuary.gametest.West219GameTests", "fr.koka.sanctuary.gametest.West218GameTests", "fr.koka.sanctuary.gametest.West217GameTests", "fr.koka.sanctuary.gametest.Base204GameTests", "fr.koka.sanctuary.gametest.North205GameTests", "fr.koka.sanctuary.gametest.Palace173GameTests", "fr.koka.sanctuary.gametest.Plans072GameTests", "fr.koka.sanctuary.gametest.Starter052GameTests", "fr.koka.sanctuary.gametest.Dispenser051GameTests", "fr.koka.sanctuary.gametest.Head050GameTests", "fr.koka.sanctuary.gametest.Poultry048GameTests", "fr.koka.sanctuary.gametest.StarInspection047GameTests", "fr.koka.sanctuary.gametest.SkyFlight046GameTests", "fr.koka.sanctuary.gametest.GravesFood038GameTests","fr.koka.sanctuary.gametest.Realtime037GameTests","fr.koka.sanctuary.gametest.Corrections036GameTests","fr.koka.sanctuary.gametest.Collections035GameTests","fr.koka.sanctuary.gametest.Notifications034GameTests","fr.koka.sanctuary.gametest.Menus033GameTests","${gametest_entrypoint}", "fr.koka.sanctuary.gametest.Swamp27WorldGameTests", "fr.koka.sanctuary.gametest.Caves28WorldGameTests", "fr.koka.sanctuary.gametest.Terraces29WorldGameTests", "fr.koka.sanctuary.gametest.River30WorldGameTests", "fr.koka.sanctuary.gametest.Transit29WorldGameTests", "fr.koka.sanctuary.gametest.Alpha28NativeStructuresGameTests", "fr.koka.sanctuary.gametest.Village306GameTests", "fr.koka.sanctuary.gametest.Drained307GameTests", "fr.koka.sanctuary.gametest.BlockKnowledgeGameTests", "fr.koka.sanctuary.gametest.CensusGameTestsTerrain", "fr.koka.sanctuary.gametest.CensusStockGameTests", "fr.koka.sanctuary.gametest.MaterialActivityGameTests", "fr.koka.sanctuary.gametest.AncientExpeditions001GameTests", "fr.koka.sanctuary.gametest.Progression003GameTests", "fr.koka.sanctuary.gametest.Atlas005GameTests", "fr.koka.sanctuary.gametest.Demeure006GameTests", "fr.koka.sanctuary.gametest.Mining008GameTests", "fr.koka.sanctuary.gametest.Building009GameTests", "fr.koka.sanctuary.gametest.Inventory010GameTests", "fr.koka.sanctuary.gametest.Cycle011GameTests", "fr.koka.sanctuary.gametest.Inventory012GameTests", "fr.koka.sanctuary.gametest.Inventory014GameTests", "fr.koka.sanctuary.gametest.Tutorial016GameTests", "fr.koka.sanctuary.gametest.Movement017GameTests", "fr.koka.sanctuary.gametest.Accessory018GameTests", "fr.koka.sanctuary.gametest.Companion019GameTests", "fr.koka.sanctuary.gametest.Companion032GameTests", "fr.koka.sanctuary.gametest.Recipes024GameTests", "fr.koka.sanctuary.gametest.Carry027GameTests", "fr.koka.sanctuary.gametest.Familiar029GameTests", "fr.koka.sanctuary.gametest.Dynamic030GameTests", "fr.koka.sanctuary.gametest.GraveInventory039GameTests", "fr.koka.sanctuary.gametest.FoodKnowledge039GameTests", "fr.koka.sanctuary.gametest.OperatorStars040GameTests", "fr.koka.sanctuary.gametest.Starter039GameTests", "fr.koka.sanctuary.gametest.Horde167GameTests", "fr.koka.sanctuary.gametest.Horde168GameTests", "fr.koka.sanctuary.gametest.Horde169GameTests", "fr.koka.sanctuary.gametest.Horde170GameTests", "fr.koka.sanctuary.gametest.Horde171GameTests", "fr.koka.sanctuary.gametest.Horde172GameTests"],
|
||||
"fabric-client-gametest": ["fr.koka.sanctuary.gametest.SanctuaryClientRenderTests"]
|
||||
},
|
||||
"mixins": ["sanctuary-gametest.mixins.json"],
|
||||
|
||||
@@ -18,6 +18,13 @@ public final class AptitudeClient {
|
||||
public static void register() {
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(AptitudeService.Capability.TYPE,(p,c)->{});
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(AptitudeService.PermanentCapability.TYPE,(p,c)->{});
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(AptitudeService.SmithingCapability.TYPE,(p,c)->{});
|
||||
ClientPlayNetworking.registerGlobalReceiver(AptitudeService.SmithingState.TYPE,(state,c)->c.client().execute(()->{
|
||||
var player=c.client().player;
|
||||
if(player==null || player.getId()!=state.entity())return;
|
||||
player.setAttached(CraftAptitudes.SMITHING,state.learned());
|
||||
if(c.client().gui.screen() instanceof ProgressionScreen screen)screen.accept("");
|
||||
}));
|
||||
ClientPlayNetworking.registerGlobalReceiver(AptitudeService.State.TYPE,(state,c)->c.client().execute(()->{
|
||||
var player=c.client().player;
|
||||
if(player==null || player.getId()!=state.entity())return;
|
||||
|
||||
@@ -97,7 +97,7 @@ public final class ProgressionScreen extends BlocodexFrame {
|
||||
List.of("inventory_crafting","sorting","catalogue"),
|
||||
List.of("familiars","active_bond","familiar_study"),
|
||||
List.of("exploration","atlas","names","zoom"),
|
||||
List.of("crafts","enchanting","alchemy"))) {
|
||||
List.of("crafts","enchanting","alchemy","smithing"))) {
|
||||
contents.addChild(new MultiLineTextWidget(ProgressionService.label("theme_"+theme.getFirst()).copy()
|
||||
.withStyle(net.minecraft.ChatFormatting.AQUA),font).setMaxWidth(innerWidth));
|
||||
for(String target:theme.subList(1,theme.size())) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package fr.koka.sanctuary.mixin;
|
||||
|
||||
import fr.koka.sanctuary.progression.CraftAptitudes;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.*;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
/** Anvil overrides the combiner pickup check; Smithing inherits it. */
|
||||
@Mixin({ItemCombinerMenu.class,AnvilMenu.class})
|
||||
abstract class CraftAptitudeCombinerMixin {
|
||||
@Inject(method="mayPickup",at=@At("HEAD"),cancellable=true)
|
||||
private void sanctuary$forgePickup(Player player,boolean hasItem,CallbackInfoReturnable<Boolean> ci) {
|
||||
if(!CraftAptitudes.allowed(player,(AbstractContainerMenu)(Object)this))ci.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin({EnchantmentMenu.class,BrewingStandMenu.class})
|
||||
@Mixin({EnchantmentMenu.class,BrewingStandMenu.class,ItemCombinerMenu.class})
|
||||
abstract class CraftAptitudeMenuMixin {
|
||||
@Inject(method="stillValid",at=@At("HEAD"),cancellable=true)
|
||||
private void sanctuary$craftAccess(Player player,CallbackInfoReturnable<Boolean> ci) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.List;
|
||||
public record AptitudePreferences(int disabledMask) {
|
||||
public static final List<String> TOGGLEABLE=List.of("names","atlas");
|
||||
public static final List<String> PERMANENT=List.of("sorting","swimming","resting",
|
||||
"active_bond","familiar_study","catalogue","zoom","enchanting","alchemy","food_knowledge");
|
||||
"active_bond","familiar_study","catalogue","zoom","enchanting","alchemy","smithing","food_knowledge");
|
||||
public static final int MAX_MASK=2047;
|
||||
public static final int ACTIVE_MASK=3;
|
||||
public static final AptitudePreferences DEFAULT=new AptitudePreferences(0);
|
||||
|
||||
@@ -26,6 +26,17 @@ public final class AptitudeService {
|
||||
public static final StreamCodec<FriendlyByteBuf,PermanentCapability> CODEC=StreamCodec.unit(new PermanentCapability());
|
||||
public Type<PermanentCapability> type(){return TYPE;}
|
||||
}
|
||||
public record SmithingCapability() implements CustomPacketPayload {
|
||||
public static final Type<SmithingCapability> TYPE=new Type<>(SanctuaryMod.id("smithing_v1"));
|
||||
public static final StreamCodec<FriendlyByteBuf,SmithingCapability> CODEC=StreamCodec.unit(new SmithingCapability());
|
||||
public Type<SmithingCapability> type(){return TYPE;}
|
||||
}
|
||||
public record SmithingState(int entity,boolean learned) implements CustomPacketPayload {
|
||||
public static final Type<SmithingState> TYPE=new Type<>(SanctuaryMod.id("smithing_state_v1"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf,SmithingState> CODEC=CustomPacketPayload.codec(
|
||||
(p,b)->{b.writeVarInt(p.entity);b.writeBoolean(p.learned);},b->new SmithingState(b.readVarInt(),b.readBoolean()));
|
||||
public Type<SmithingState> type(){return TYPE;}
|
||||
}
|
||||
public record Set(String target,boolean enabled,int expectedMask) implements CustomPacketPayload {
|
||||
public static final Type<Set> TYPE=new Type<>(SanctuaryMod.id("aptitude_set_v1"));
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf,Set> CODEC=CustomPacketPayload.codec(
|
||||
@@ -64,12 +75,16 @@ public final class AptitudeService {
|
||||
int mask=preferences(p).disabledMask();
|
||||
if(p.getAttachedOrElse(PREFERENCES,0)!=mask)p.setAttached(PREFERENCES,mask);
|
||||
if(ServerPlayNetworking.canSend(p,State.TYPE))ServerPlayNetworking.send(p,new State(p.getId(),preferences(p).disabledMask(),CraftAptitudes.learned(p,"enchanting"),CraftAptitudes.learned(p,"alchemy")));
|
||||
if(ServerPlayNetworking.canSend(p,SmithingState.TYPE))ServerPlayNetworking.send(p,new SmithingState(p.getId(),CraftAptitudes.learned(p,"smithing")));
|
||||
}
|
||||
public static void register() {
|
||||
java.util.Objects.requireNonNull(CraftAptitudes.ENCHANTING);
|
||||
java.util.Objects.requireNonNull(CraftAptitudes.ALCHEMY);
|
||||
java.util.Objects.requireNonNull(CraftAptitudes.SMITHING);
|
||||
PayloadTypeRegistry.clientboundConfiguration().register(Capability.TYPE,Capability.CODEC);
|
||||
PayloadTypeRegistry.clientboundConfiguration().register(PermanentCapability.TYPE,PermanentCapability.CODEC);
|
||||
PayloadTypeRegistry.clientboundConfiguration().register(SmithingCapability.TYPE,SmithingCapability.CODEC);
|
||||
PayloadTypeRegistry.clientboundPlay().register(SmithingState.TYPE,SmithingState.CODEC);
|
||||
PayloadTypeRegistry.serverboundPlay().register(Set.TYPE,Set.CODEC);
|
||||
PayloadTypeRegistry.clientboundPlay().register(State.TYPE,State.CODEC);
|
||||
ServerPlayNetworking.registerGlobalReceiver(Set.TYPE,(request,c)->c.server().execute(()->{
|
||||
|
||||
@@ -14,13 +14,15 @@ public final class CraftAptitudes {
|
||||
b->b.persistent(Codec.BOOL).copyOnDeath());
|
||||
public static final AttachmentType<Boolean> ALCHEMY=AttachmentRegistry.create(SanctuaryMod.id("alchemy_learned"),
|
||||
b->b.persistent(Codec.BOOL).copyOnDeath());
|
||||
public static final AttachmentType<Boolean> SMITHING=AttachmentRegistry.create(SanctuaryMod.id("smithing_learned"),
|
||||
b->b.persistent(Codec.BOOL).copyOnDeath());
|
||||
private CraftAptitudes() {}
|
||||
public static boolean target(String target){return target.equals("enchanting") || target.equals("alchemy");}
|
||||
public static boolean target(String target){return target.equals("enchanting") || target.equals("alchemy") || target.equals("smithing");}
|
||||
private static AttachmentType<Boolean> attachment(String target) {
|
||||
return switch(target){case "enchanting"->ENCHANTING;case "alchemy"->ALCHEMY;default->throw new IllegalArgumentException("Unknown craft aptitude");};
|
||||
return switch(target){case "enchanting"->ENCHANTING;case "alchemy"->ALCHEMY;case "smithing"->SMITHING;default->throw new IllegalArgumentException("Unknown craft aptitude");};
|
||||
}
|
||||
public static boolean learned(Player p,String target){return p.getAttachedOrElse(attachment(target),false);}
|
||||
public static String target(AbstractContainerMenu menu){return menu instanceof EnchantmentMenu ? "enchanting" : menu instanceof BrewingStandMenu ? "alchemy" : "";}
|
||||
public static String target(AbstractContainerMenu menu){return menu instanceof EnchantmentMenu ? "enchanting" : menu instanceof BrewingStandMenu ? "alchemy" : menu instanceof AnvilMenu || menu instanceof SmithingMenu ? "smithing" : "";}
|
||||
public static boolean allowed(Player p,String target) {
|
||||
if(ProgressEffects.get(p)==null)return true;
|
||||
if(p instanceof ServerPlayer server) {
|
||||
|
||||
@@ -121,6 +121,9 @@ public final class ProgressionService {
|
||||
|| !ServerConfigurationNetworking.canSend(handler,AptitudeService.PermanentCapability.TYPE)) {
|
||||
handler.disconnect(label("aptitude_version_required"));return;
|
||||
}
|
||||
if (!ServerConfigurationNetworking.canSend(handler,AptitudeService.SmithingCapability.TYPE)) {
|
||||
handler.disconnect(label("smithing_version_required"));return;
|
||||
}
|
||||
if (!ServerConfigurationNetworking.canSend(handler,ProgressionPayloads.SwimmingCapability.TYPE)) {
|
||||
handler.disconnect(label("swimming_version_required"));return;
|
||||
}
|
||||
|
||||
@@ -2639,5 +2639,9 @@
|
||||
"sanctuary.progression.aptitude_disable_hint": "Click to disable.",
|
||||
"sanctuary.progression.aptitude_enable_hint": "Click to enable.",
|
||||
"sanctuary.progression.aptitude_acquired": "Acquired",
|
||||
"sanctuary.progression.aptitude_acquired_hint": "This aptitude remains permanently available after purchase."
|
||||
"sanctuary.progression.aptitude_acquired_hint": "This aptitude remains permanently available after purchase.",
|
||||
"sanctuary.progression.smithing": "Smithing",
|
||||
"sanctuary.progression.smithing_description": "Use anvils and the smithing table to repair, rename and upgrade equipment.",
|
||||
"sanctuary.progression.smithing_locked": "Acquire Smithing in Progression to use an anvil or smithing table.",
|
||||
"sanctuary.progression.smithing_version_required": "Smithing requires Sanctuary beta.238 or later on both client and server."
|
||||
}
|
||||
|
||||
@@ -2639,5 +2639,9 @@
|
||||
"sanctuary.progression.aptitude_disable_hint": "Cliquer pour désactiver.",
|
||||
"sanctuary.progression.aptitude_enable_hint": "Cliquer pour activer.",
|
||||
"sanctuary.progression.aptitude_acquired": "Acquise",
|
||||
"sanctuary.progression.aptitude_acquired_hint": "Cette aptitude est acquise définitivement après achat."
|
||||
"sanctuary.progression.aptitude_acquired_hint": "Cette aptitude est acquise définitivement après achat.",
|
||||
"sanctuary.progression.smithing": "Forgerie",
|
||||
"sanctuary.progression.smithing_description": "Utiliser les enclumes et la table de forge pour réparer, renommer et améliorer les équipements.",
|
||||
"sanctuary.progression.smithing_locked": "Acquérez Forgerie dans Progression pour utiliser une enclume ou une table de forge.",
|
||||
"sanctuary.progression.smithing_version_required": "Forgerie nécessite Sanctuary beta.238 ou plus récent côté client et serveur."
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 162 B |
@@ -6,6 +6,7 @@
|
||||
"CraftAptitudeOpenMixin",
|
||||
"CraftAptitudeMenuMixin",
|
||||
"CraftAptitudeEnchantMixin",
|
||||
"CraftAptitudeCombinerMixin",
|
||||
"IslandChestLootMixin",
|
||||
"BadlandsBands211Accessor",
|
||||
"WestMonument217Accessor",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
name = "Sanctuary"
|
||||
author = "KOKA99CAB"
|
||||
version = "beta.237"
|
||||
version = "beta.238"
|
||||
pack-format = "packwiz:1.1.0"
|
||||
|
||||
[index]
|
||||
|
||||
Reference in New Issue
Block a user