Use only the active hotbar in the clay workshop for beta.112
Build Sanctuary / build (push) Canceled after 0s
Build Sanctuary / build (push) Canceled after 0s
This commit is contained in:
@@ -20,10 +20,9 @@ La [vision complète](docs/vision.md) conserve les intentions ; le
|
||||
L’économie, les claims et les dimensions décrits dans la vision restent
|
||||
à implémenter.
|
||||
|
||||
La version **beta.111** reprend ce socle et réorganise l’atelier d’argile avec un
|
||||
cadre Minecraft nine-slice ; les statuaires suivent le regard à la pose.
|
||||
Le [ticket](docs/clay-workshop-ui-beta111.md) décrit la compatibilité, les essais,
|
||||
la publication et la mise à jour de l’instance **Sanctuary Beta**.
|
||||
La version **beta.112** limite l’atelier d’argile à la hotbar active du joueur,
|
||||
dans un cadre compact. Les statuaires suivent toujours le regard à la pose.
|
||||
Le [ticket](docs/clay-workshop-hotbar-beta112.md) décrit le comportement et les essais.
|
||||
La [livraison précédente](docs/clay-workshop-integration-beta110.md) réunit déjà
|
||||
l’atelier, les œufs et la neige saisonnière dans l’instance **Sanctuary Beta**.
|
||||
Les [synchronisations antérieures](docs/git-sync-beta092.md) restent documentées.
|
||||
@@ -34,6 +33,14 @@ La [carte native beta.005](docs/atlas-beta005.md) prend le relais après
|
||||
l’étude de Xaero et des alternatives libres.
|
||||
|
||||
|
||||
## beta.112 — Atelier d’argile et hotbar
|
||||
|
||||
Neuf cases de hotbar et deux emplacements de fabrication, dans un cadre fixe.
|
||||
Les transferts restent dans la hotbar ; une barre pleine ne consomme pas d’argile.
|
||||
[Contrat et vérifications](docs/clay-workshop-hotbar-beta112.md) ·
|
||||
[Pack normal](build/Sanctuary-beta.112.mrpack) ·
|
||||
[Pack de test](build/Sanctuary-Test-beta.112.mrpack).
|
||||
|
||||
## beta.111 — Atelier d’argile et pose orientée
|
||||
|
||||
Cadre nine-slice, aperçu, fabrication et inventaire dans un menu commun.
|
||||
@@ -2064,7 +2071,7 @@ Depuis beta.090, la cible est **Minecraft 26.3 finale**, publiée le 15 septembr
|
||||
| Fabric API | 0.160.5+26.3 |
|
||||
| Fabric Loom | 1.17.20 |
|
||||
| Gradle Wrapper | 9.5.1, distribution vérifiée par SHA-256 |
|
||||
| Sanctuary / pack | beta.111 |
|
||||
| Sanctuary / pack | beta.112 |
|
||||
|
||||
Java 25 et Python 3.11 ou plus récent sont nécessaires. Le script pack utilise
|
||||
uniquement la bibliothèque standard et repère aussi une installation Python
|
||||
@@ -2093,7 +2100,7 @@ décrits dans [Validation](docs/testing.md).
|
||||
|
||||
Résultats :
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.111.jar` : mod à installer avec
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.112.jar` : mod à installer avec
|
||||
Fabric API sur la version Minecraft indiquée.
|
||||
- `build/packwiz/` : pack de développement complet, avec le mod construit et
|
||||
l'index vérifié. Voir [Installation packwiz](packwiz/README.md).
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Backlog Sanctuary
|
||||
|
||||
## STAT-03 — Atelier d’argile et hotbar — beta.112
|
||||
|
||||
Branche `codex/clay-workshop-hotbar-beta112`. [Contrat et vérifications](clay-workshop-hotbar-beta112.md).
|
||||
Onze cases : argile, résultat et hotbar active ; hauteur fixe et transferts
|
||||
limités aux neuf cases, même avec un inventaire agrandi.
|
||||
|
||||
## STAT-02 — Atelier d’argile et orientation — beta.111
|
||||
|
||||
Branche `codex/clay-workshop-ui-beta111`. [Contrat et vérifications](clay-workshop-ui-beta111.md).
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# STAT-03 — Atelier d’argile limité à la hotbar — beta.112
|
||||
|
||||
Contrat du 17 septembre 2026, branche `codex/clay-workshop-hotbar-beta112`.
|
||||
|
||||
Le menu utilise uniquement les neuf cases de la hotbar active du joueur,
|
||||
avec les deux emplacements de fabrication. Les rangées de réserve, y compris
|
||||
les extensions Sanctuary, ne sont ni ajoutées au menu ni utilisées par ses
|
||||
transferts. La rangée choisie avant l’ouverture reste la hotbar courante.
|
||||
La touche Tab retrouve la navigation des boutons dans ce menu.
|
||||
|
||||
Le cadre nine-slice tient dans une hauteur fixe de 202 pixels d’interface.
|
||||
Il ne grandit plus avec la capacité de l’inventaire. Le Maj-clic du résultat
|
||||
cherche uniquement une place dans la hotbar ; sans place, il ne consomme pas
|
||||
d’argile. Les touches 1–9 restent utilisables, pas l’échange avec la seconde
|
||||
main. À la fermeture, la restitution native des objets restants est conservée.
|
||||
|
||||
Les identifiants, sauvegardes, données de sculpture et orientations beta.111
|
||||
restent inchangés. Aucun monde existant n’est ouvert ou réécrit. Client et
|
||||
serveur doivent utiliser ensemble beta.112 pour les nouveaux indices du menu
|
||||
temporaire (11 cases au total).
|
||||
|
||||
## Vérifications natives
|
||||
|
||||
Le parcours `Statuary106ClientChecks`, étendu à beta.112, passe en **1 min 13 s**
|
||||
sur Minecraft 26.3 avec serveur intégré et monde plat jetable de graine 106 :
|
||||
|
||||
- Menu FR/EN aux échelles 2 et 3 : hauteur fixe, neuf cases de hotbar native,
|
||||
deux cases de fabrication et absence de panneau d’inventaire évolutif.
|
||||
- Fabrication par Maj-clic depuis la hotbar, avec de l’argile aussi présente
|
||||
dans la réserve, l’extension et la seconde main : ces stocks restent intacts.
|
||||
- Rejet des indices de cases absents et de l’échange avec la seconde main.
|
||||
- Hotbar remplie de neuf piles : Maj-clic sans fabrication ni consommation.
|
||||
- Une seule place dans une pile de 63 statuaires : production d’un seul objet,
|
||||
coût d’une argile, aucune statue transférée dans une case cachée.
|
||||
- Restitution de l’argile restante à la fermeture, seize teintes, quatre
|
||||
orientations, butin/repose et sauvegarde/reconnexion sans fichier GLB.
|
||||
- Régression du Métabli et annulation d’import conservées.
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary:runClientGameTest \
|
||||
-PsanctuaryClientTests=true -PsanctuaryStatuary106ClientTests=true \
|
||||
-PsanctuaryClientNoVsync=true -PsanctuaryQuickTests=true
|
||||
```
|
||||
|
||||
Marqueur `STATUARY112_PASS` dans `build/clay-hotbar112-client.log` ; captures
|
||||
relues dans `build/clay-hotbar112-evidence/`. Le GameTest dédié reste exclu
|
||||
conformément au refus antérieur de son EULA. Pas de test Windows ni depuis
|
||||
deux ordinateurs ; aucun monde personnel n’a été ouvert.
|
||||
|
||||
## Livraison vérifiée
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack -x :sanctuary:runGameTest`
|
||||
passe en **3 min 6 s**, 125 tâches. Les sources JAR correspondent aux sources
|
||||
Java du dépôt. Les classes et ressources hors de ce ticket sont identiques
|
||||
à beta.111, notamment les données et le rendu orienté des statuaires.
|
||||
Les deux packs contiennent le même JAR, sans monde, GLB ni classes de tests.
|
||||
|
||||
- Pack normal : 10 169 337 octets, SHA-256
|
||||
`55723a5d7cb99e350ad8fe150e92e0567430b5261ca4685abd900be34068658f`.
|
||||
- Pack Test : 10 188 262 octets, SHA-256
|
||||
`987818bf817ad80fd055b97e84bf4775fca54820d7c3055d6fd4089dc7a5a3c0`.
|
||||
- JAR : SHA-256
|
||||
`9007a770bbe69db5846f93140d910514f10839cc34ec7c6315a9381d8850adaa`.
|
||||
|
||||
Reçu local : `build/clay-hotbar112-artifact.json`. Publication et mise à jour
|
||||
de l’instance stable préparées après ces contrôles.
|
||||
+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.111
|
||||
pack_version=beta.111
|
||||
mod_version=beta.112
|
||||
pack_version=beta.112
|
||||
resource_pack_version=beta.090
|
||||
maven_group=fr.koka.sanctuary
|
||||
jei_version=30.32.0-sanctuary.3
|
||||
|
||||
+38
-10
@@ -23,6 +23,7 @@ import net.minecraft.network.chat.contents.TranslatableContents;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.inventory.ContainerInput;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.level.GameType;
|
||||
@@ -71,19 +72,27 @@ public final class Statuary106ClientChecks implements FabricClientGameTest {
|
||||
c.runOnClient(m->{m.options.guiScale().set(2);m.getWindow().setWindowed(1280,800);m.resizeGui();});
|
||||
open(c);c.runOnClient(m->((ClayWorkshopScreen)m.gui.screen()).onFilesDrop(List.of(file)));
|
||||
c.waitFor(m->!((ClayWorkshopScreen)m.gui.screen()).busy()&&((ClayWorkshopScreen)m.gui.screen()).selected()!=null,200);c.waitTicks(10);
|
||||
server.runOnServer(s->{var p=player(s);var menu=(ClayWorkshopMenu)p.containerMenu;check(menu.getSlot(1).getItem().isEmpty(),"No clay, no result");menu.getSlot(0).set(new ItemStack(Items.CLAY,3));menu.broadcastChanges();});
|
||||
server.runOnServer(s->{var p=player(s);var menu=(ClayWorkshopMenu)p.containerMenu;check(menu.getSlot(1).getItem().isEmpty(),"No clay, no result");
|
||||
menu.getSlot(ClayWorkshopMenu.HOTBAR_START).set(new ItemStack(Items.CLAY,3));
|
||||
p.getInventory().setItem(18,new ItemStack(Items.CLAY,64));p.getInventory().setItem(Inventory.SLOT_OFFHAND,new ItemStack(Items.CLAY,12));
|
||||
fr.koka.sanctuary.inventory.SanctuaryOverflowAccess.of(p).setItem(17,new ItemStack(Items.CLAY,64));menu.broadcastChanges();});
|
||||
c.waitFor(m->m.player.containerMenu.getSlot(ClayWorkshopMenu.HOTBAR_START).getItem().getCount()==3,100);
|
||||
c.runOnClient(m->m.gameMode.handleContainerInput(m.player.containerMenu.containerId,ClayWorkshopMenu.HOTBAR_START,0,ContainerInput.QUICK_MOVE,m.player));
|
||||
c.waitFor(m->m.player.containerMenu.getSlot(1).getItem().is(Statuary.ITEM),100);
|
||||
c.takeScreenshot("statuary106-workshop-en");
|
||||
checkWorkshopLayout(c);
|
||||
c.runOnClient(m->{m.options.guiScale().set(3);m.resizeGui();});c.waitTicks(3);checkWorkshopLayout(c);c.takeScreenshot("statuary111-workshop-en-scale3");
|
||||
// Six recovery rows still fit a small GUI and remain scrollable.
|
||||
server.runOnServer(s->{var p=player(s);fr.koka.sanctuary.inventory.SanctuaryOverflowAccess.of(p).setItem(17,new ItemStack(Items.STONE));p.containerMenu.broadcastChanges();});
|
||||
c.waitFor(m->SanctuaryInventoryClient.visibleRows()==6,100);c.runOnClient(m->button(m.gui.screen(),"sanctuary.statuary.refresh").onPress(enter()));checkWorkshopLayout(c);
|
||||
c.runOnClient(m->{var screen=(ClayWorkshopScreen)m.gui.screen();var geometry=(SanctuaryContainerScreenGeometry)(Object)screen;var refresh=button(screen,"sanctuary.statuary.refresh");
|
||||
check(screen.mouseScrolled(refresh.getX()-10+geometry.sanctuary$panelLeft()+30,refresh.getY()-127+geometry.sanctuary$panelTop()+35,0,1),"Extended workshop inventory can scroll");});
|
||||
c.takeScreenshot("statuary111-workshop-en-six-rows");
|
||||
server.runOnServer(s->{var p=player(s);fr.koka.sanctuary.inventory.SanctuaryOverflowAccess.of(p).setItem(17,ItemStack.EMPTY);p.containerMenu.broadcastChanges();});
|
||||
c.waitFor(m->SanctuaryInventoryClient.visibleRows()==1,100);
|
||||
// Hidden inventory, overflow and offhand must never join this menu or feed its input.
|
||||
c.runOnClient(m->check(!((SanctuaryContainerScreenGeometry)(Object)m.gui.screen()).sanctuary$hasIntegratedInventory(),"No expanding player-inventory panel"));
|
||||
server.runOnServer(s->{var p=player(s);var menu=(ClayWorkshopMenu)p.containerMenu;
|
||||
check(menu.slots.size()==11&&menu.getSlot(0).getItem().getCount()==3,"Two crafting slots plus exactly nine hotbar slots");
|
||||
check(!((fr.koka.sanctuary.inventory.SanctuaryExtendedInventoryMenu)(Object)menu).sanctuary$hasExtendedInventory(),"No hidden overflow destination");
|
||||
menu.clicked(0,Inventory.SLOT_OFFHAND,ContainerInput.SWAP,p);menu.clicked(11,0,ContainerInput.PICKUP,p);menu.clicked(-2,0,ContainerInput.PICKUP,p);
|
||||
check(menu.quickMoveStack(p,11).isEmpty()&&menu.getCarried().isEmpty(),"Out-of-menu slot rejected");
|
||||
check(menu.getSlot(0).getItem().getCount()==3&&p.getOffhandItem().getCount()==12,"Offhand swap refused");
|
||||
check(p.getInventory().getItem(18).getCount()==64&&fr.koka.sanctuary.inventory.SanctuaryOverflowAccess.of(p).getItem(17).getCount()==64,"Reserve clay untouched");
|
||||
p.getInventory().setItem(18,ItemStack.EMPTY);p.getInventory().setItem(Inventory.SLOT_OFFHAND,ItemStack.EMPTY);fr.koka.sanctuary.inventory.SanctuaryOverflowAccess.of(p).setItem(17,ItemStack.EMPTY);
|
||||
});
|
||||
c.runOnClient(m->{m.options.guiScale().set(2);m.resizeGui();});
|
||||
server.runOnServer(s->{var menu=(ClayWorkshopMenu)player(s).containerMenu;ORIGINAL.set(StatuaryEntity.sculpture(menu.getSlot(1).getItem()));check(ORIGINAL.get().equals(source),"Plain clay keeps exact colors and model ID");
|
||||
for(var family:ColoredBricks.families()){var colored=ClayWorkshopMenu.material(source,new ItemStack(family.clay()));check(colored.modelId().equals(source.modelId())&&colored.cells().size()==source.cells().size()&&colored.cells().stream().map(VoxelSculpture.Cell::color).distinct().count()==1,"All 16 clays keep geometry and ID, recolor uniformly");}});
|
||||
@@ -143,8 +152,24 @@ public final class Statuary106ClientChecks implements FabricClientGameTest {
|
||||
c.runOnClient(m->button(m.gui.screen(),"sanctuary.plans.generate").onPress(enter()));c.waitFor(m->!PlanClient.busy&&PlanClient.plan!=null&&m.gui.screen()==null,400);
|
||||
c.runOnClient(m->check(PlanClient.plan.height()==32&&PlanClient.plan.cells().size()>source.cells().size(),"Same source produces real-block statue at selected height"));
|
||||
server.runOnServer(s->check(((StatuaryEntity)player(s).level().getBlockEntity(SCULPTURE)).sculpture().equals(source),"Plan generation leaves existing miniature intact"));
|
||||
// Saturated hotbar must not spill crafted statues into hidden inventory or consume unpaid clay.
|
||||
c.runOnClient(m->PlanClient.clear());open(c);c.runOnClient(m->((ClayWorkshopScreen)m.gui.screen()).load(file));
|
||||
c.waitFor(m->!((ClayWorkshopScreen)m.gui.screen()).busy()&&((ClayWorkshopScreen)m.gui.screen()).selected()!=null,200);c.waitTicks(10);
|
||||
var savedHotbar=new AtomicReference<List<ItemStack>>();
|
||||
server.runOnServer(s->{var p=player(s);var menu=(ClayWorkshopMenu)p.containerMenu;savedHotbar.set(java.util.stream.IntStream.range(0,9).mapToObj(i->p.getInventory().getItem(i).copy()).toList());
|
||||
for(int i=0;i<9;i++)p.getInventory().setItem(i,new ItemStack(Items.STONE,64));menu.getSlot(0).set(new ItemStack(Items.CLAY,2));menu.broadcastChanges();});
|
||||
c.waitFor(m->m.player.containerMenu.getSlot(1).getItem().is(Statuary.ITEM)&&m.player.containerMenu.getSlot(2).getItem().is(Items.STONE),100);
|
||||
c.runOnClient(m->m.gameMode.handleContainerInput(m.player.containerMenu.containerId,1,0,ContainerInput.QUICK_MOVE,m.player));c.waitTicks(6);
|
||||
server.runOnServer(s->{var p=player(s);var menu=(ClayWorkshopMenu)p.containerMenu;check(menu.getSlot(0).getItem().getCount()==2&&count(p,Statuary.ITEM)==0,"Full hotbar consumes nothing and does not fill reserve");
|
||||
var nearlyFull=StatuaryEntity.item(source);nearlyFull.setCount(63);p.getInventory().setItem(8,nearlyFull);menu.broadcastChanges();});
|
||||
c.waitFor(m->m.player.containerMenu.getSlot(10).getItem().getCount()==63,100);
|
||||
c.runOnClient(m->m.gameMode.handleContainerInput(m.player.containerMenu.containerId,1,0,ContainerInput.QUICK_MOVE,m.player));c.waitTicks(6);
|
||||
server.runOnServer(s->{var p=player(s);var menu=(ClayWorkshopMenu)p.containerMenu;check(p.getInventory().getItem(8).getCount()==64&&count(p,Statuary.ITEM)==64&&menu.getSlot(0).getItem().getCount()==1,"Partial merge crafts only the one statue that fits");
|
||||
p.getInventory().setItem(0,ItemStack.EMPTY);menu.broadcastChanges();});
|
||||
close(c);server.waitFor(s->player(s).containerMenu==player(s).inventoryMenu,100);
|
||||
server.runOnServer(s->{var p=player(s);check(p.getInventory().getItem(0).is(Items.CLAY)&&p.getInventory().getItem(0).getCount()==1,"Closing returns the unused clay");for(int i=0;i<9;i++)p.getInventory().setItem(i,savedHotbar.get().get(i));p.inventoryMenu.broadcastFullState();});
|
||||
// Cancel an import by closing the native menu; late worker completion cannot craft in the next session.
|
||||
c.runOnClient(m->PlanClient.clear());open(c);c.runOnClient(m->{((ClayWorkshopScreen)m.gui.screen()).load(file);m.gui.screen().onClose();});c.waitTicks(12);
|
||||
open(c);c.runOnClient(m->{((ClayWorkshopScreen)m.gui.screen()).load(file);m.gui.screen().onClose();});c.waitTicks(12);
|
||||
server.runOnServer(s->check(player(s).containerMenu==player(s).inventoryMenu,"Closed workshop releases crafting session"));
|
||||
check(Arrays.equals(Files.readAllBytes(file),fixture),"Original GLB never rewritten");
|
||||
}
|
||||
@@ -162,11 +187,14 @@ public final class Statuary106ClientChecks implements FabricClientGameTest {
|
||||
}
|
||||
System.out.println("STATUARY106_PASS GLB textured scene, bounded import, shared folder filters, native paid crafting, 16 clay colors, model ID, shift-click, place/drop/re-place, Metabli plan, cancellation, server save/reconnect without source");
|
||||
System.out.println("STATUARY111_PASS nine-slice GUI FR/EN scales 2/3, four native gaze placements, renderer directions, structure rotation/mirror, legacy state default, drop/re-place and persisted orientation");
|
||||
System.out.println("STATUARY112_PASS fixed compact hotbar-only menu, native input transfer, hidden slots/offhand rejected, full hotbar no consumption, partial merge exact cost, closing restitution");
|
||||
}catch(IOException e){throw new AssertionError(e);}finally{try{Files.deleteIfExists(file);}catch(IOException ignored){}}
|
||||
}
|
||||
private static ServerPlayer player(net.minecraft.server.MinecraftServer s){return s.getPlayerList().getPlayers().getFirst();}
|
||||
private static void checkWorkshopLayout(ClientGameTestContext c){c.runOnClient(m->{var screen=(ClayWorkshopScreen)m.gui.screen();var refresh=button(screen,"sanctuary.statuary.refresh");int left=refresh.getX()-10,top=refresh.getY()-127;
|
||||
check(top>=0&&top+screen.panelHeight()<=screen.height,"Workshop frame fits GUI height");
|
||||
check(screen.panelHeight()==202&&screen.getMenu().slots.size()==11,"Compact height and nine player slots regardless of reserve capacity");
|
||||
for(int i=0;i<9;i++){var slot=screen.getMenu().getSlot(i+2);check(slot.container==m.player.getInventory()&&slot.getContainerSlot()==i,"Displayed player slot belongs to active native hotbar");}
|
||||
for(var slot:screen.getMenu().slots)if(slot.x>=0&&slot.y>=0)check(slot.x>3&&slot.x+17<310&&slot.y>3&&slot.y+17<screen.panelHeight(),"Every visible slot fits inside nine-slice frame");
|
||||
for(var widget:screen.children())if(widget instanceof Button b)check(b.getX()>=left&&b.getRight()<=left+310&&b.getY()>=top&&b.getBottom()<=top+145,"Every action stays clear of explanations and inventory");
|
||||
});}
|
||||
|
||||
@@ -19,11 +19,12 @@ import net.minecraft.world.item.ItemStack;
|
||||
public final class ClayWorkshopScreen extends AbstractContainerScreen<ClayWorkshopMenu> {
|
||||
private static final Identifier FRAME=fr.koka.sanctuary.SanctuaryMod.id("container/clay_workshop/frame");
|
||||
private static final Identifier SLOT=Identifier.withDefaultNamespace("container/slot");
|
||||
private static final Identifier SELECTION=fr.koka.sanctuary.SanctuaryMod.id("hud/hotbar_selection");
|
||||
private static final ExecutorService WORKER=Executors.newSingleThreadExecutor(r->{var t=new Thread(r,"Sanctuary clay models");t.setDaemon(true);return t;});
|
||||
private List<Path> files=List.of();private int page,generation,ticks;private boolean busy,pending;
|
||||
private VoxelSculpture source,previewSource;private ItemStack preview=ItemStack.EMPTY,lastClay=ItemStack.EMPTY;
|
||||
private Component status=text("choose_model");private Button rotate;
|
||||
public ClayWorkshopScreen(ClayWorkshopMenu menu,Inventory inv,Component title){super(menu,inv,title,310,286);inventoryLabelX=75;inventoryLabelY=186;}
|
||||
public ClayWorkshopScreen(ClayWorkshopMenu menu,Inventory inv,Component title){super(menu,inv,title,310,202);}
|
||||
public static Component text(String key,Object...args){return Component.translatable("sanctuary.statuary."+key,args);}
|
||||
public static void register(){
|
||||
net.minecraft.client.gui.screens.MenuScreens.register(Statuary.MENU,ClayWorkshopScreen::new);StatuaryRenderer.register();
|
||||
@@ -65,15 +66,14 @@ public final class ClayWorkshopScreen extends AbstractContainerScreen<ClayWorksh
|
||||
}
|
||||
@Override public void onFilesDrop(List<Path> files){if(!files.isEmpty())load(files.getFirst());}
|
||||
@Override public void removed(){generation++;super.removed();}
|
||||
int panelHeight(){
|
||||
// The inventory mixin moves or hides rows according to the player's capacity.
|
||||
return Math.max(177,menu.slots.stream().filter(s->s.x>=0&&s.y>=0).mapToInt(s->s.y+30).max().orElse(177));
|
||||
}
|
||||
int panelHeight(){return imageHeight;}
|
||||
@Override public void extractBackground(GuiGraphicsExtractor g,int mx,int my,float partial){
|
||||
g.blitSprite(RenderPipelines.GUI_TEXTURED,FRAME,leftPos,topPos,imageWidth,panelHeight());
|
||||
g.fill(leftPos+148,topPos+23,leftPos+300,topPos+92,0xFF8B8B8B);
|
||||
g.fill(leftPos+149,topPos+24,leftPos+299,topPos+91,0xFF373737);
|
||||
for(var slot:menu.slots)if(slot.x>=0&&slot.y>=0)g.blitSprite(RenderPipelines.GUI_TEXTURED,SLOT,leftPos+slot.x-1,topPos+slot.y-1,18,18);
|
||||
if(minecraft.player!=null)g.blitSprite(RenderPipelines.GUI_TEXTURED,SELECTION,
|
||||
leftPos+ClayWorkshopMenu.HOTBAR_X-3+minecraft.player.getInventory().getSelectedSlot()*18,topPos+ClayWorkshopMenu.HOTBAR_Y-3,22,21);
|
||||
if(!preview.isEmpty()){
|
||||
g.pose().pushMatrix();g.pose().translate(leftPos+194,topPos+27);g.pose().scale(3.75f,3.75f);g.item(preview,0,0);g.pose().popMatrix();
|
||||
}
|
||||
@@ -85,7 +85,6 @@ public final class ClayWorkshopScreen extends AbstractContainerScreen<ClayWorksh
|
||||
@Override protected void extractLabels(GuiGraphicsExtractor g,int mx,int my){
|
||||
g.text(font,title,10,8,0xFF303030,false);g.text(font,text("models"),10,22,0xFF404040,false);
|
||||
g.text(font,text("input"),230-font.width(text("input"))/2,95,0xFF303030,false);g.text(font,text("output"),imageWidth-10-font.width(text("output")),95,0xFF303030,false);
|
||||
if(inventoryLabelY>=0)g.text(font,playerInventoryTitle,inventoryLabelX,inventoryLabelY,0xFF303030,false);
|
||||
}
|
||||
public boolean busy(){return busy;}
|
||||
public VoxelSculpture selected(){return source;}
|
||||
|
||||
@@ -90,6 +90,8 @@ public final class HotbarClient {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// This workshop exposes only the current hotbar; Tab remains native focus navigation.
|
||||
if (screen instanceof ClayWorkshopScreen) return;
|
||||
ScreenKeyboardEvents.afterKeyRelease(screen).register((s,event) -> {if (key.matches(event)) held=false;});
|
||||
ScreenKeyboardEvents.allowKeyPress(screen).register((s,event) -> {
|
||||
if (!ProgressionClient.active() || !key.matches(event) || event.isEscape() || editing(s.getFocused())) return true;
|
||||
|
||||
+1
-3
@@ -190,8 +190,7 @@ abstract class AbstractContainerScreenMixin implements SanctuaryContainerScreenG
|
||||
if (!sanctuary$integratedInventory) return;
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
int panelHeight = PANEL_BASE_HEIGHT + sanctuary$visibleRows * BANK_ROW_STEP;
|
||||
// The workshop draws one nine-slice frame around its controls and all visible rows.
|
||||
if (!((Object)this instanceof fr.koka.sanctuary.client.ClayWorkshopScreen)) graphics.blit(RenderPipelines.GUI_TEXTURED,
|
||||
graphics.blit(RenderPipelines.GUI_TEXTURED,
|
||||
SanctuaryMod.id("textures/gui/container/" + sanctuary$textureProfile
|
||||
+ "/inventory_" + sanctuary$visibleRows + ".png"),
|
||||
sanctuary$panelLeft, sanctuary$panelTop,
|
||||
@@ -332,7 +331,6 @@ abstract class AbstractContainerScreenMixin implements SanctuaryContainerScreenG
|
||||
// These screens paint previews or controls below their lowest machine
|
||||
// slot. Their explicit anchors keep the common panel from covering them.
|
||||
if (screen instanceof StonecutterScreen) return 70;
|
||||
if (screen instanceof fr.koka.sanctuary.client.ClayWorkshopScreen) return 176;
|
||||
if (screen instanceof CartographyTableScreen) return 81;
|
||||
if (screen instanceof EnchantmentScreen) return 73;
|
||||
if (screen instanceof SmithingScreen) return 82;
|
||||
|
||||
@@ -18,6 +18,7 @@ import net.minecraft.world.phys.*;
|
||||
|
||||
/** Native input/result slots. Server recomputes the color from the actual clay and charges each take. */
|
||||
public final class ClayWorkshopMenu extends AbstractContainerMenu {
|
||||
public static final int HOTBAR_START=2,HOTBAR_END=HOTBAR_START+Inventory.SELECTION_SIZE,HOTBAR_X=75,HOTBAR_Y=176;
|
||||
public record View(BlockPos pos,UUID token){
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf,View> CODEC=StreamCodec.of((b,p)->{b.writeBlockPos(p.pos);b.writeUUID(p.token);},b->new View(b.readBlockPos(),b.readUUID()));
|
||||
}
|
||||
@@ -51,7 +52,8 @@ public final class ClayWorkshopMenu extends AbstractContainerMenu {
|
||||
@Override public boolean mayPickup(Player p){return mayCraft(p)&&clay(input.getItem(0))&&!getItem().isEmpty();}
|
||||
@Override public void onTake(Player p,ItemStack stack){input.removeItem(0,1);input.setChanged();super.onTake(p,stack);}
|
||||
});
|
||||
addStandardInventorySlots(inv,75,198);
|
||||
// Use the nine carried slots without registering a full inventory/overflow range.
|
||||
addInventoryHotbarSlots(inv,HOTBAR_X,HOTBAR_Y);
|
||||
}
|
||||
// Colors match the published clay palette (tools/colored-bricks-palette.json), in native dye order.
|
||||
private static final int[] CLAY_COLORS={0xC6C4C8,0xD3AA8D,0xD39FD0,0x91B0CA,0xC8BF98,0xA2C69A,0xDAC4DD,0x74767D,0xA9A8AC,0xA8C5C1,0xBE91E3,0xA0A7BC,0xA49A91,0xA0B499,0xB49A99,0x444541};
|
||||
@@ -81,13 +83,17 @@ public final class ClayWorkshopMenu extends AbstractContainerMenu {
|
||||
var hit=new BlockHitResult(Vec3.atCenterOf(view.pos),Direction.UP,view.pos,false);
|
||||
return UseBlockCallback.EVENT.invoker().interact(p,level,InteractionHand.MAIN_HAND,hit)==InteractionResult.PASS&&stillValid(p);
|
||||
}
|
||||
@Override public void clicked(int slot,int button,ContainerInput input,Player p){if(stillValid(p))super.clicked(slot,button,input,p);}
|
||||
@Override public void clicked(int slot,int button,ContainerInput input,Player p){
|
||||
if(!stillValid(p)||slot>=slots.size()||(slot< -1&&slot!=-999))return;
|
||||
if(input==ContainerInput.SWAP&&(button<0||button>=Inventory.SELECTION_SIZE))return;
|
||||
super.clicked(slot,button,input,p);
|
||||
}
|
||||
@Override public boolean canTakeItemForPickAll(ItemStack stack,Slot slot){return slot.container!=result&&super.canTakeItemForPickAll(stack,slot);}
|
||||
@Override public void removed(Player p){super.removed(p);if(level!=null)clearContainer(p,input);result.clearContent();selected=null;}
|
||||
@Override public ItemStack quickMoveStack(Player p,int index){
|
||||
if(index<0||index>=slots.size()||!stillValid(p))return ItemStack.EMPTY;var slot=slots.get(index);if(!slot.mayPickup(p)||!slot.hasItem())return ItemStack.EMPTY;
|
||||
var stack=slot.getItem();var copy=stack.copy();
|
||||
if(index<2){if(!moveItemStackTo(stack,2,38,true))return ItemStack.EMPTY;}
|
||||
if(index<HOTBAR_START){if(!moveItemStackTo(stack,HOTBAR_START,HOTBAR_END,true))return ItemStack.EMPTY;}
|
||||
else if(clay(stack)){if(!moveItemStackTo(stack,0,1,false))return ItemStack.EMPTY;}
|
||||
else return ItemStack.EMPTY;
|
||||
if(stack.isEmpty())slot.set(ItemStack.EMPTY);else slot.setChanged();
|
||||
|
||||
@@ -2157,7 +2157,7 @@
|
||||
"sanctuary.plans.imported_model": "3D model → block statue",
|
||||
"sanctuary.plans.model_files_help": "GLB models in schematics/sanctuary/, alongside your schematics. Create a full-block statue here. Craft 16³ miniatures at the clay workshop.",
|
||||
"sanctuary.plans.empty_models": "Place a .glb file in the shared folder, then refresh.",
|
||||
"sanctuary.statuary.choose_model": "Choose a GLB model. 1 clay block = 1 statue. Plain clay keeps model colors; colored clay gives a solid color.",
|
||||
"sanctuary.statuary.choose_model": "Choose a GLB model, then add clay from your hotbar.",
|
||||
"sanctuary.statuary.folder": "Shared folder",
|
||||
"sanctuary.statuary.refresh": "Refresh",
|
||||
"sanctuary.statuary.models": "GLB models",
|
||||
|
||||
@@ -2157,7 +2157,7 @@
|
||||
"sanctuary.plans.imported_model": "Modèle 3D → statue en blocs",
|
||||
"sanctuary.plans.model_files_help": "Modèles GLB dans schematics/sanctuary/, avec vos schémas. Ici, créez une grande statue en blocs. Les miniatures 16³ se fabriquent à l’atelier d’argile.",
|
||||
"sanctuary.plans.empty_models": "Déposez un fichier .glb dans le dossier commun, puis actualisez.",
|
||||
"sanctuary.statuary.choose_model": "Choisissez un modèle GLB. 1 bloc d’argile = 1 statue. Argile normale : couleurs du modèle ; argile colorée : teinte unie.",
|
||||
"sanctuary.statuary.choose_model": "Choisissez un modèle GLB, puis déposez de l’argile depuis votre barre rapide.",
|
||||
"sanctuary.statuary.folder": "Dossier commun",
|
||||
"sanctuary.statuary.refresh": "Actualiser",
|
||||
"sanctuary.statuary.models": "Modèles GLB",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
name = "Sanctuary"
|
||||
author = "KOKA99CAB"
|
||||
version = "beta.111"
|
||||
version = "beta.112"
|
||||
pack-format = "packwiz:1.1.0"
|
||||
|
||||
[index]
|
||||
|
||||
Reference in New Issue
Block a user