Compare commits

..
Author SHA1 Message Date
koka 00d175cb98 Deliver beta.235 as a verified reversible Pause layout experiment
Build Sanctuary / build (push) Waiting to run
2026-10-06 14:21:31 +02:00
koka a7bdfe6998 Try parallel Pause groups before Atlas and a narrow stack beside the map 2026-10-06 14:16:40 +02:00
8 changed files with 196 additions and 40 deletions
+12
View File
@@ -6,6 +6,18 @@ 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.235 — Essai réversible du menu Pause
[Disposition adaptative](docs/pause-columns-beta235.md) : avant Atlas, Aventure
à gauche et Communauté à droite, avec Partie / Game juste dessous. Avec Atlas,
une colonne étroite à gauche, Communauté → Aventure → Game, carte au centre.
Bloc centré verticalement, libellés de navigation entiers et ordre clavier
adapté. Parcours Pause et Story Vulkan FR/EN GUI 2/3/4, `check build assemblePack`
et quatre GameTests ciblés réussis.
Le commit de disposition `a7bdfe6` peut être annulé indépendamment de la
livraison. [MRpack local beta.235](build/Sanctuary-beta.235.mrpack) vérifié,
sans publication ni mise à jour personnelle. Le rendu reste à apprécier.
## beta.234 — Story, recherche et filtres
[Premier menu Story](docs/story-beta234.md), sous Progression dans Aventure :
+10
View File
@@ -1,5 +1,15 @@
# Backlog Sanctuary
## PAUSE-235 — groupes côte à côte avant Atlas — essai livré localement
Branche `codex/pause-columns-beta235`, base beta.234. Avant Atlas : Aventure à
gauche, Communauté à droite et Game juste dessous, bloc centré. Avec Atlas :
une colonne étroite à gauche dans l'ordre Communauté → Aventure → Game,
carte centrale et fils à droite. [Contrat et vérifications](pause-columns-beta235.md),
parcours Vulkan Pause et Story FR/EN GUI 2/3/4, build, quatre GameTests ciblés
et MRpack local vérifiés ; rendu à apprécier.
Commit de disposition `a7bdfe6` séparé des métadonnées pour permettre son retour.
## STORY-234 — recueil personnel avec recherche — livré localement
Branche `codex/story-beta234`, base beta.233. Story sous Progression dans
+97
View File
@@ -0,0 +1,97 @@
# PAUSE-235 — Aventure et Communauté côte à côte
Branche d'essai `codex/pause-columns-beta235`, base beta.234, commit `f9c2ce8`,
Minecraft 26.3. Le rendu reste à apprécier par le créateur.
## Disposition et réversibilité
Avant Atlas, Aventure et Communauté occupent deux colonnes au même niveau :
Discovery, Progression et Story à gauche ; Habitant, Faction et Battle à droite.
Partie / Game suit juste dessous avec Options, World Options et la sortie
native. L'ensemble est centré verticalement dans le corps de Pause, avec de
l'espace sous Game ; Reprendre la partie reste dans le pied de page.
Sans Atlas, ce bloc est centré entre Gazette à gauche et Notice Board à droite.
Avec Atlas, une seule colonne étroite est serrée à gauche : Communauté, puis
Aventure, puis Partie / Game, selon l'ordre confirmé par le créateur. La carte
garde la région centrale et les deux fils communautaires restent à droite.
Le bloc complet reste centré verticalement. La largeur minimale des boutons
de navigation tient compte des libellés localisés et de leur marge native.
Aux petites hauteurs, boutons et espacements se compactent. Tab suit l'ordre
visuel : par rangée de gauche à droite avant Atlas, puis de haut en bas avec
Atlas. Les libellés FR/EN existants sont réutilisés.
Le changement de disposition et les assertions correspondantes sont isolés
dans le commit `a7bdfe6`, séparé de la livraison et de ses métadonnées. La branche
`codex/story-beta234` conserve la disposition précédente. Un retour s'effectue
avec `git revert a7bdfe6` ; une éventuelle nouvelle
livraison doit prendre le numéro beta suivant, sans diminuer le compteur.
L'application inverse du patch a été vérifiée avec `git apply --reverse --check`,
sans modifier les fichiers de travail.
Ce ticket ne change ni génération, sauvegarde, données communautaires, protocole,
conditions de progression ou contenus Story. Aucune installation personnelle
ou publication n'est demandée.
## Vérifications
```sh
./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true -PsanctuaryPause233ClientTests=true -PsanctuaryClientGraphicsBackend=vulkan
./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true -PsanctuaryStory234ClientTests=true -PsanctuaryClientGraphicsBackend=vulkan
./gradlew check build assemblePack -PsanctuaryFocusedTests=menus -PsanctuaryAtlasOnly=true
```
Le parcours Pause final réussit en 1 min 22 s (`PAUSE233_PASS`), dans un monde
plat neuf de développement, Survie sans commandes. FR/EN, GUI 2/3/4, fenêtre
1280 × 960, avant et après Atlas : six actions Sanctuary, alignement et absence
de chevauchement, largeur des libellés avec la marge native, ordre de Tab et
Game sous les groupes, avec de l'espace avant le pied. Le test attend désormais
un créneau de requête communautaire pour son amorçage et envoie le code de
raccourci natif de Tab, requis par Minecraft 26.3.
Le parcours communautaire existant passe également : publication et photo,
retours des sélecteurs, brouillons, édition d'une ancienne catégorie, inventaire
réel conservé et aucun changement du fichier communautaire pendant les lectures.
La configuration temporaire est restaurée à la fin du banc.
Log : `build/pause235-client-vulkan-final.log`. 38 captures finales copiées dans
`build/pause235-screenshots/`. Captures inspectées : sans Atlas FR/4, avec Atlas
FR/2 et EN/4. Vulkan confirmé : MoltenVK 1.4.2 sur Apple M1.
Le parcours Story final réussit en 29 s (`STORY234_PASS`), avec les mêmes
langues, GUI et états Atlas. Accès depuis les deux dispositions de Pause,
100 entrées désactivées, recherche/filtres, saisie continue et focus, molette,
redimensionnement à 1344 × 1008 et retours Échap / Terminé vérifiés. Le record
de progression serveur ne change pas. Log :
`build/pause235-story-client-vulkan-final.log` ; 21 captures copiées dans
`build/pause235-story-screenshots/`.
Aux GUI étroits, les longs libellés natifs des options/sortie et les titres des
fils gardent leur défilement ou découpage existant ; les boutons de navigation
Sanctuary montrent leur libellé entier. Les infobulles des actions Game sont
conservées. Aucun essai OpenGL, Windows ou monde personnel n'est revendiqué.
Le contrôle `check build assemblePack` réussit en 3 min 1 s : quatre GameTests
requis et contrôles purs du dépôt, dont les quatre contrôles communautaires
fichier/photos/demandes/recherche. Log : `build/pause235-check-build-pack.log`.
Le banc menus conserve sa sélection Atlas existante ; le conflit de générateur
sur le chemin natif par défaut, documenté en beta.232, reste hors de ce ticket.
## Livraison locale
`mod_version`, `pack_version` et `packwiz/pack.toml` sont alignés sur beta.235.
Le commit de livraison conserve ces métadonnées et cette documentation séparées
du commit de disposition.
Export [Sanctuary-beta.235.mrpack](../build/Sanctuary-beta.235.mrpack),
12 776 222 octets : intégrité ZIP, Minecraft 26.3 et Fabric Loader 0.19.5,
un seul JAR Sanctuary embarqué, identique au JAR construit, sans monde ni module
de test. Les 3 303 fichiers d'assets, données et resourcepacks sont identiques
à beta.234, traductions incluses. Story reste dans le JAR de production et les
classes des bancs client en sont absentes. Reçu :
`build/pause235-mrpack-receipt.json`.
- JAR : `aad94732c749161d775ee634bdd85de70de511ef875c6c00fa167d230d0d600b`.
- MRpack : `5f1bd18aa8a4145b7e708da0abfff22068365847dddbc95ae2b490b88bbb305e`.
Aucune publication distante, modification du canal packwiz, synchronisation
Prism ou ouverture d'un monde personnel effectuée.
+2 -2
View File
@@ -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.234
pack_version=beta.234
mod_version=beta.235
pack_version=beta.235
resource_pack_version=beta.121
maven_group=fr.koka.sanctuary
jei_version=30.32.0-sanctuary.3
@@ -1,6 +1,7 @@
package fr.koka.sanctuary.client;
import com.google.gson.Gson;
import com.mojang.blaze3d.platform.InputConstants;
import fr.koka.sanctuary.community.*;
import fr.koka.sanctuary.gametest.Starter039TestSupport;
import fr.koka.sanctuary.progression.*;
@@ -15,6 +16,7 @@ import net.minecraft.client.gui.components.*;
import net.minecraft.client.gui.screens.*;
import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.client.input.KeyEvent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.*;
import static fr.koka.sanctuary.client.Community154ClientChecks.*;
@@ -46,7 +48,8 @@ public final class Pause233ClientChecks implements FabricClientGameTest {
var server=world.getServer();
server.runOnServer(s->{var p=s.getPlayerList().getPlayers().getFirst();p.getAbilities().invulnerable=true;p.getInventory().setItem(6,new ItemStack(Items.OAK_LOG,17));p.getInventory().setItem(7,new ItemStack(Items.DIAMOND,3));});
String legacy=UUID.randomUUID().toString();
c.runOnClient(m->check(CommunityClient.request(null,"create",legacy,"","notice","event","Ancienne annonce","Texte historique",0,0,r->check(!r.has("error"),"Legacy-compatible notice seeded through the server")),"Seed request dispatched"));
// Wait and dispatch together so the periodic following request cannot take the slot between them.
c.waitFor(m->CommunityClient.request(null,"create",legacy,"","notice","event","Ancienne annonce","Texte historique",0,0,r->check(!r.has("error"),"Legacy-compatible notice seeded through the server")),200);
c.waitFor(m->!CommunityClient.pending(),200);c.waitTicks(24);
c.runOnClient(m->{m.getWindow().setWindowed(1280,960);m.options.guiScale().set(2);m.resizeGui();CommunityClient.open(new PauseScreen(true),"notice","",true);edit(m.gui.screen()).setValue("Du bois pour le pont");body(m.gui.screen()).setValue("Une annonce commune, avec une demande facultative.");composer(m.gui.screen(),"notice");press(m.gui.screen(),"sanctuary.community.task_configure");press(m.gui.screen(),"sanctuary.community.location_current");press(m.gui.screen(),"sanctuary.community.materials");edit(m.gui.screen()).setValue("minecraft:oak_log");});
@@ -86,7 +89,7 @@ public final class Pause233ClientChecks implements FabricClientGameTest {
}
}
server.runOnServer(s->checked(()->{check(Arrays.equals(saved,Files.readAllBytes(s.getWorldPath(net.minecraft.world.level.storage.LevelResource.ROOT).resolve("data/sanctuary-community.json"))),"Reading/resizing old and new posts never rewrites stored categories");return null;}));
System.out.println("PAUSE233_PASS: FR/EN GUI 2/3/4, identity/faction/battle at top, discovery/progression centered, options below, unified notice composer, descriptive task and photo publication, legacy category preservation");
System.out.println("PAUSE233_PASS: FR/EN GUI 2/3/4, parallel Adventure/Community before Atlas, narrow Community/Adventure stack beside Atlas, Game directly below, centered navigation block, visual Tab order, unified notice composer, descriptive task and photo publication, legacy category preservation");
}finally {intro.set(false);checked(()->{if(previous==null)Files.deleteIfExists(config);else Files.write(config,previous);Files.deleteIfExists(fixture);return null;});}
}
private FileCommunityStore store(net.minecraft.server.MinecraftServer s)throws Exception{return new FileCommunityStore(s.getWorldPath(net.minecraft.world.level.storage.LevelResource.ROOT).resolve("data/sanctuary-community.json"),serverId);}
@@ -105,19 +108,40 @@ public final class Pause233ClientChecks implements FabricClientGameTest {
check((((PauseAtlasAccess)s).sanctuary$pauseMap()!=null)==unlocked,"Pause map appears only after Atlas purchase");
var keys=Set.of("sanctuary.progression.blocks","sanctuary.progression.progress","sanctuary.story.title","sanctuary.progression.identity","sanctuary.cycle.faction","sanctuary.arena.title");
var nav=s.children().stream().filter(Button.class::isInstance).map(Button.class::cast).filter(b->keys.contains(key(b))).toList();check(nav.size()==6,"Six Sanctuary navigation actions");
int top=nav.stream().mapToInt(AbstractWidget::getY).min().orElseThrow(),bottom=nav.stream().mapToInt(AbstractWidget::getBottom).max().orElseThrow();
int options=s.children().stream().filter(Button.class::isInstance).map(Button.class::cast).filter(b->key(b).equals("menu.options")).findFirst().orElseThrow().getY();
check(bottom+8<=options,"Navigation stays separated from Options/World Options/Quit");
var options=s.children().stream().filter(Button.class::isInstance).map(Button.class::cast).filter(b->key(b).equals("menu.options")).findFirst().orElseThrow();
var worldOptions=s.children().stream().filter(Button.class::isInstance).map(Button.class::cast).filter(b->key(b).equals("options.worldOptions.button")).findFirst().orElseThrow();
var quit=s.children().stream().filter(Button.class::isInstance).map(Button.class::cast).filter(b->Set.of("menu.returnToMenu","menu.disconnect").contains(key(b))).findFirst().orElseThrow();
var community=widget(s,"pause_group_community");var adventure=widget(s,"pause_group_adventure");var game=widget(s,"pause_group_game");
check(community.getY()==(s.height<280?46:50)&&community.getBottom()<nav.get(0).getY()&&adventure.getBottom()<nav.get(3).getY()&&game.getBottom()<options,"All three headings precede their buttons");
check(nav.stream().map(CommunityPausePanels::key).toList().equals(List.of("sanctuary.progression.identity","sanctuary.cycle.faction","sanctuary.arena.title","sanctuary.progression.blocks","sanctuary.progression.progress","sanctuary.story.title")),"Same visual and keyboard order with or without the map");
check(nav.get(0).getBottom()<nav.get(1).getY()&&nav.get(1).getBottom()<nav.get(2).getY()&&nav.get(2).getBottom()<adventure.getY()&&nav.get(3).getBottom()<nav.get(4).getY()&&nav.get(4).getBottom()<nav.get(5).getY()&&nav.get(5).getBottom()<game.getY(),"Three distinct titled groups without overlap");
double center=(nav.get(3).getY()+nav.get(5).getBottom())/2.;check(Math.abs(center-s.height/2.)<=.5,"Discovery/progression/story are vertically centered");
check(nav.stream().map(CommunityPausePanels::key).toList().equals(unlocked
?List.of("sanctuary.progression.identity","sanctuary.cycle.faction","sanctuary.arena.title","sanctuary.progression.blocks","sanctuary.progression.progress","sanctuary.story.title")
:List.of("sanctuary.progression.blocks","sanctuary.progression.identity","sanctuary.progression.progress","sanctuary.cycle.faction","sanctuary.story.title","sanctuary.arena.title")),"Navigation follows visual order in both layouts");
for(var button:nav)check(s.getFont().width(button.getMessage())+8<=button.getWidth(),"Navigation labels fit with native button padding");
check(nav.stream().allMatch(b->b.getBottom()<game.getY())&&game.getBottom()<options.getY()&&options.getBottom()<worldOptions.getY()&&worldOptions.getBottom()<quit.getY(),"Game follows below Sanctuary navigation");
check(options.getX()==nav.get(0).getX()&&worldOptions.getX()==options.getX()&&quit.getX()==options.getX()&&worldOptions.getWidth()==options.getWidth()&&quit.getWidth()==options.getWidth(),"Native Game actions share the navigation block width");
int bodyTop=s.height<280?46:50,bodyBottom=s.height-39;
check(Math.abs((Math.min(adventure.getY(),community.getY())+quit.getBottom())/2.-(bodyTop+bodyBottom)/2.)<=.5&&quit.getBottom()+8<=bodyBottom,"Complete block centered with clear space below Game");
// KeyEvent carries the physical key and the SDL shortcut key separately.
s.setFocused(nav.get(0));s.keyPressed(new KeyEvent(InputConstants.KEY_TAB,'\t',0));check(s.getFocused()==nav.get(1),"Tab follows the second visible action");
s.keyPressed(new KeyEvent(InputConstants.KEY_TAB,'\t',0));check(s.getFocused()==nav.get(2),"Tab follows the third visible action");
if(!unlocked){
for(var b:nav)check(Math.abs(b.getX()+b.getWidth()/2.-s.width/2.)<=.5,"Navigation centered horizontally");
check(widget(s,"article").getX()==8&&widget(s,"notice").getX()>nav.getFirst().getRight(),"Gazette remains left and Notice Board right");
check(adventure.getY()==community.getY()&&adventure.getX()==nav.get(0).getX()&&community.getX()==nav.get(1).getX()&&adventure.getBottom()<nav.get(0).getY(),"Both headings align above their columns");
for(int row=0;row<3;row++) {
var left=nav.get(2*row);var right=nav.get(2*row+1);
check(left.getY()==right.getY()&&left.getHeight()==right.getHeight()&&left.getRight()<right.getX(),"Two parallel columns without overlap");
if(row>0)check(nav.get(2*row-2).getBottom()<left.getY(),"Rows remain separated");
}
check(options.getRight()==nav.get(1).getRight(),"Game spans both columns before Atlas");
check(Math.abs((nav.get(0).getX()+nav.get(1).getRight())/2.-s.width/2.)<=.5,"Two-column block centered horizontally");
check(widget(s,"article").getX()==8&&widget(s,"article").getRight()<nav.get(0).getX()&&widget(s,"notice").getX()>nav.get(1).getRight(),"Gazette remains left and Notice Board right");
}
else {
check(community.getBottom()<nav.get(0).getY()&&nav.get(2).getBottom()<adventure.getY()&&adventure.getBottom()<nav.get(3).getY(),"Community then Adventure stacked above Game with Atlas");
for(int i=0;i<nav.size();i++) {
var button=nav.get(i);check(button.getX()==8&&button.getWidth()==options.getWidth()&&button.getWidth()<=132,"All actions use one narrow left column");
if(i>0)check(nav.get(i-1).getBottom()<button.getY(),"Stacked buttons remain separated");
}
var map=((PauseAtlasAccess)s).sanctuary$pauseMap();check(nav.get(0).getRight()<map.getX()&&map.getRight()<widget(s,"article").getX()&&widget(s,"article").getX()==widget(s,"notice").getX(),"Narrow navigation left, map center and community feeds right");
}
else check(nav.getFirst().getX()==8&&widget(s,"article").getX()==widget(s,"notice").getX(),"Unlocked map retains left navigation and community feeds on the right");
}
private void shot(ClientGameTestContext c,String name){var path=c.takeScreenshot("pause233-"+name);checked(()->{var dir=game.resolve("../../../../../build/pause233-screenshots").normalize();Files.createDirectories(dir);Files.copy(path,dir.resolve(name+".png"),StandardCopyOption.REPLACE_EXISTING);return null;});}
private interface Checked<T>{T run()throws Exception;}
@@ -54,7 +54,8 @@ public final class Solo232ClientChecks implements FabricClientGameTest {
var article=heading(screen,"sanctuary.community.article");var notice=heading(screen,"sanctuary.community.notice");
if(!unlocked){check(article.getX()==8&&article.getY()==(screen.height<280?46:50)&&notice.getY()==article.getY(),"Locked map uses full-height Gazette on the left and Notice Board on the right");for(var button:buttons)check(article.getRight()<button.getX()&&notice.getX()>button.getRight(),"Community columns do not overlap centered navigation");}
else check(article.getX()==notice.getX()&&article.getY()<notice.getY(),"Unlocked map retains both community feeds stacked on the right");
for(var button:buttons){check(button.getY()>=50&&button.getBottom()<=screen.height-33,"Button stays in pause body");if(!unlocked)check(Math.abs(button.getX()+button.getWidth()/2.-screen.width/2.)<=.5,"Locked-map navigation is horizontally centered");else check(button.getX()==8,"Unlocked layout retains original left column");}
for(var button:buttons)check(button.getY()>=50&&button.getBottom()<=screen.height-33,"Button stays in pause body");
Pause233ClientChecks.pause(screen,unlocked);
});
String shot="solo232-pause-"+(unlocked?"unlocked-":"locked-")+language+"-"+scale;
var image=c.takeScreenshot(shot);checked(()->{var dir=net.fabricmc.loader.api.FabricLoader.getInstance().getGameDir().resolve("../../../../../build/solo232-screenshots").normalize();java.nio.file.Files.createDirectories(dir);java.nio.file.Files.copy(image,dir.resolve(shot+".png"),java.nio.file.StandardCopyOption.REPLACE_EXISTING);});
@@ -24,35 +24,44 @@ public final class CommunityPausePanels implements AutoCloseable {
public CommunityPausePanels(Screen screen,Consumer<AbstractWidget> add,List<Button> nativeButtons) {
this.screen=screen;
boolean compact=screen.height<280;
int margin=8,gap=10,top=compact?46:50,bottom=screen.height-39;
int left=Math.min(132,Math.max(76,screen.width/5)),right=Math.min(244,Math.max(104,screen.width*31/100));
int centerX=margin+left+gap,rightX=screen.width-margin-right,centerWidth=rightX-gap-centerX;
boolean unlocked=ProgressionClient.progress.atlas();
int menuWidth=unlocked?left:Math.max(left,Math.min(200,2*(rightX-gap)-screen.width));
int margin=8,gap=10,top=compact?46:50,bottom=screen.height-39;
String[] sections={"blocks","identity","progress","faction","story","battle"};
int columnGap=compact?4:8;
int minimumWidth=Arrays.stream(sections).map(CommunityPausePanels::sectionLabel).mapToInt(Minecraft.getInstance().font::width).max().orElse(0)+8;
int columnWidth=Math.max(minimumWidth,(Math.min(264,screen.width/3)-columnGap)/2);
int menuWidth=unlocked?Math.max(minimumWidth,Math.min(132,Math.max(76,screen.width/5))):2*columnWidth+columnGap;
int right=unlocked?Math.min(244,Math.max(104,screen.width*31/100))
:Math.min(244,(screen.width-2*margin-menuWidth-2*gap)/2);
int centerX=margin+menuWidth+gap,rightX=screen.width-margin-right,centerWidth=rightX-gap-centerX;
int menuX=unlocked?margin:(screen.width-menuWidth)/2;
int optionHeight=compact?14:20,navGap=compact?1:4,groupGap=compact?2:12,titleHeight=compact?10:12;
int optionsTop=bottom-(3*optionHeight+8);
int navHeight=Math.min(20,Math.max(11,(optionsTop-top-3*titleHeight-4*navGap-2*groupGap)/6));
// Leave room for Community and its heading above the centered Adventure group.
navHeight=Math.min(navHeight,Math.max(11,(screen.height-2*top-4*titleHeight-6*navGap-2*groupGap)/9));
int communityX=unlocked?menuX:menuX+columnWidth+columnGap;
int navGap=compact?(unlocked?1:2):4,groupGap=compact?(unlocked?2:8):12,titleHeight=compact&&unlocked?10:12;
int groupCount=unlocked?3:2;
int navHeight=Math.min(compact?14:20,Math.max(11,(bottom-top-16-groupCount*titleHeight-(groupCount-1)*groupGap-2*groupCount*navGap)/(3*groupCount)));
int step=navHeight+navGap;
int upperTop=top+titleHeight;
int adventureSize=3*navHeight+2*navGap;
int adventureTop=Math.max(upperTop+2*step+navHeight+groupGap+titleHeight,Math.min((screen.height-adventureSize)/2,optionsTop-titleHeight-adventureSize-groupGap));
int sectionHeight=titleHeight+3*navHeight+2*navGap;
int menuHeight=groupCount*sectionHeight+(groupCount-1)*groupGap;
// Use a narrow stack beside the map, or parallel groups before Atlas.
int menuTop=top+Math.max(0,(bottom-top-menuHeight)/2);
int adventureTop=unlocked?menuTop+sectionHeight+groupGap:menuTop;
int communityTop=menuTop;
int gameTop=menuTop+(groupCount-1)*(sectionHeight+groupGap);
int optionsTop=gameTop+titleHeight;
add.accept(new Frame(screen.width,screen.height));
date=new Calendar(margin,3,left);
date=new Calendar(margin,3,Math.min(132,Math.max(76,screen.width/5)));
add.accept(date);
bulletin=new PauseTextButton(centerX,3,screen.width-margin-centerX,38,text("bulletin"),()->open(screen,"bulletin",items("bulletins").isEmpty()?"":str(items("bulletins").get(0).getAsJsonObject(),"id"),false));
add.accept(bulletin);
add.accept(new StringWidget(menuX,top,menuWidth,9,text("pause_group_community").withStyle(net.minecraft.ChatFormatting.GRAY),Minecraft.getInstance().font));
add.accept(new StringWidget(menuX,adventureTop-titleHeight,menuWidth,9,text("pause_group_adventure").withStyle(net.minecraft.ChatFormatting.GRAY),Minecraft.getInstance().font));
add.accept(new StringWidget(menuX,optionsTop-titleHeight,menuWidth,9,text("pause_group_game").withStyle(net.minecraft.ChatFormatting.GRAY),Minecraft.getInstance().font));
String[] sections={"identity","faction","battle","blocks","progress","story"};
add.accept(new StringWidget(menuX,adventureTop,unlocked?menuWidth:columnWidth,9,text("pause_group_adventure").withStyle(net.minecraft.ChatFormatting.GRAY),Minecraft.getInstance().font));
add.accept(new StringWidget(communityX,communityTop,unlocked?menuWidth:columnWidth,9,text("pause_group_community").withStyle(net.minecraft.ChatFormatting.GRAY),Minecraft.getInstance().font));
add.accept(new StringWidget(menuX,gameTop,menuWidth,9,text("pause_group_game").withStyle(net.minecraft.ChatFormatting.GRAY),Minecraft.getInstance().font));
if(unlocked)sections=new String[]{"identity","faction","battle","blocks","progress","story"};
for(String target:sections) {
int y=switch(target){case "blocks"->adventureTop;case "progress"->adventureTop+step;case "story"->adventureTop+2*step;case "identity"->upperTop;case "faction"->upperTop+step;default->upperTop+2*step;};
Component label=switch(target){case "story"->StoryScreen.label("title");case "faction"->fr.koka.sanctuary.cycle.CycleService.label("faction");case "battle"->fr.koka.sanctuary.arena.Arenas.text("title");default->fr.koka.sanctuary.progression.ProgressionService.label(target);};
add.accept(Button.builder(label,b->{switch(target){case "faction"->CycleClient.open(screen,"faction");case "battle"->ArenaClient.open(screen);default->ProgressionClient.open(screen,target);}})
.bounds(menuX,y,menuWidth,navHeight).build());
boolean adventure=target.equals("blocks")||target.equals("progress")||target.equals("story");
int y=(adventure?adventureTop:communityTop)+titleHeight+step*switch(target){case "blocks","identity"->0;case "progress","faction"->1;default->2;};
add.accept(Button.builder(sectionLabel(target),b->{switch(target){case "faction"->CycleClient.open(screen,"faction");case "battle"->ArenaClient.open(screen);default->ProgressionClient.open(screen,target);}})
.bounds(adventure?menuX:communityX,y,unlocked?menuWidth:columnWidth,navHeight).build());
}
map=unlocked?new PauseAtlasWidget(screen,centerX,top,centerWidth,Math.max(30,bottom-top)):null;
if(map!=null)add.accept(map);
@@ -61,13 +70,16 @@ public final class CommunityPausePanels implements AutoCloseable {
int feedHeight=(bottom-top-gap)/2;
articles=new PauseCommunityFeed(screen,add,unlocked?rightX:margin,top,right,unlocked?feedHeight:bottom-top,"article");
notices=new PauseCommunityFeed(screen,add,rightX,unlocked?top+feedHeight+gap:top,right,unlocked?bottom-top-feedHeight-gap:bottom-top,"notice");
place(nativeButtons,add,"menu.options",menuX,optionsTop,menuWidth,optionHeight);
place(nativeButtons,add,"options.worldOptions.button",menuX,optionsTop+optionHeight+4,menuWidth,optionHeight);
place(nativeButtons,add,"menu.options",menuX,optionsTop,menuWidth,navHeight);
place(nativeButtons,add,"options.worldOptions.button",menuX,optionsTop+step,menuWidth,navHeight);
for(var button:nativeButtons) if(key(button).equals("menu.returnToMenu") || key(button).equals("menu.disconnect")) {
button.setRectangle(menuWidth,optionHeight,menuX,optionsTop+2*(optionHeight+4));button.setTooltip(Tooltip.create(button.getMessage()));add.accept(button);
button.setRectangle(menuWidth,navHeight,menuX,optionsTop+2*step);button.setTooltip(Tooltip.create(button.getMessage()));add.accept(button);
}
updateHeader();
}
private static Component sectionLabel(String target) {
return switch(target){case "story"->StoryScreen.label("title");case "faction"->fr.koka.sanctuary.cycle.CycleService.label("faction");case "battle"->fr.koka.sanctuary.arena.Arenas.text("title");default->fr.koka.sanctuary.progression.ProgressionService.label(target);};
}
public static String key(Button button) { return button.getMessage().getContents() instanceof TranslatableContents t?t.getKey():""; }
private static void place(List<Button> buttons,Consumer<AbstractWidget> add,String key,int x,int y,int width) {
place(buttons,add,key,x,y,width,20);
+1 -1
View File
@@ -1,6 +1,6 @@
name = "Sanctuary"
author = "KOKA99CAB"
version = "beta.234"
version = "beta.235"
pack-format = "packwiz:1.1.0"
[index]