Compare commits

..
Author SHA1 Message Date
koka 53fa32bbc5 feat(sanctuary): ajouter les modes du suivi de quetes
Verification 26.2 / check (pull_request) Canceled after 0s
2026-08-31 03:27:36 +02:00
koka 7a2f697024 Merge pull request 'release: publier les dernières PR dans Packwiz alpha.222' (#100) from release/26.2.0-alpha.222 into main
Verification 26.2 / check (push) Canceled after 0s
Reviewed-on: #100
2026-08-30 13:33:43 +00:00
koka 4d0750e993 release: update Packwiz to alpha.222
Verification 26.2 / check (pull_request) Canceled after 0s
2026-08-30 15:29:24 +02:00
koka c49673801f Merge pull request 'fix(sanctuary): simplifier le suivi de quetes' (#99) from fix/quest-tracker-transparent into main
Verification 26.2 / check (push) Canceled after 0s
Reviewed-on: #99
2026-08-30 13:15:18 +00:00
24 changed files with 695 additions and 256 deletions
+15 -1
View File
@@ -8,12 +8,26 @@ Ce journal suit les **versions du modpack**. Lorsqu'un seul module change, sa ve
## Non distribué
Module modifié : **Sanctuary `0.0.0-alpha.124`**. Le pack public reste temporairement en `26.2.0-alpha.221` jusqu'à la publication.
Module modifié : **Sanctuary `0.0.0-alpha.125`**. Le pack public reste temporairement en `26.2.0-alpha.222` jusqu'à la publication.
- ajoute un style configurable au suivi des quêtes (`N` par défaut), entre le texte transparent et le panneau original de Chris ;
- place automatiquement le style texte sous la hauteur réelle de la colonne gauche du F3 permanent afin que les deux affichages ne se recouvrent pas ;
- nomme et ordonne les contrats suivis par niveau de récompense, rubis puis saphir puis émeraude/autres, au lieu du libellé générique « Contrat du jour » ;
- conserve les données Sanctuary v11, le protocole réseau 22, les IDs et les sauvegardes existantes.
## `26.2.0-alpha.222`
Modules modifiés : **Red-Stoner `0.0.0-alpha.13`** et **Sanctuary `0.0.0-alpha.124`**. Les huit autres modules conservent leur version issue de l'alpha.221.
- ajoute au terminal de stockage une recherche clavier focalisée à l'ouverture, compatible avec les noms affichés, les IDs, les traductions, les accents et Unicode ;
- affiche les grandes quantités du terminal sous forme compacte (`1K`, `1.2K`, `12K`, `1.2M`) tout en conservant la valeur exacte dans l'infobulle ;
- stabilise les emplacements du terminal lorsqu'une pile est vidée afin que les ressources suivantes ne glissent plus sous le curseur, puis recompacte volontairement lors d'une nouvelle recherche ou à la réouverture ;
- ajoute un suivi d'avancement des quêtes, affichable ou masquable par une touche configurable (`J` par défaut) ;
- affiche chaque quête active comme un simple texte ombré en haut à gauche, sans panneau, bordure, titre, message vide ni barre de progression ;
- synchronise un snapshot borné des quêtes acceptées et des contrats réservés, sans les demandes que le joueur a lui-même créées, ni changer les données Sanctuary v11 ni le protocole réseau 22 ;
- propose une saisie assistée des IDs d'item et de mob dans l'éditeur de demande, avec icône et noms proches.
- actualise JEI vers la bêta Fabric 26.2 `30.29.0.196` et synchronise Packwiz avec l'index Prism/Modrinth ;
- conserve Red-Stoner data v3/protocole 7, Sanctuary data v11/protocole 22, les IDs et les sauvegardes existantes ; aucun nouveau monde ni réinitialisation n'est nécessaire.
## `26.2.0-alpha.221`
+3 -1
View File
@@ -22,10 +22,12 @@ Licence : **GNU GPL 3.0 or Later** (`GPL-3.0-or-later`)
Les descriptions FR/EN/RU sont déclarées dans chaque `fabric.mod.json` et dans les fichiers de langue. Les images fournies servent d'icônes Fabric de leur module.
Version courante du pack distribué : **`26.2.0-alpha.221`**. Cette release distribue Ambiance **`0.0.0-alpha.22`** ; les neuf autres modules conservent leur version issue de l'alpha.220.
Version courante du pack distribué : **`26.2.0-alpha.222`**. Cette release distribue Red-Stoner **`0.0.0-alpha.13`** et Sanctuary **`0.0.0-alpha.124`** ; les huit autres modules conservent leur version issue de l'alpha.221.
La migration [CANA-01 à CANA-09](CANAPLIA.md) porte Canaplia vers Minecraft 26.2. It's Alive **`0.0.0-alpha.35`** enregistre les seize familles, les découvertes, hybridations, résines, effets serveur et le Livre interdit personnel, partageable temporairement sur un pupitre. Ambiance **`0.0.0-alpha.14`** compose les profils visuels bornés avec ses réglages d'accessibilité, et Only Fun **`0.0.0-alpha.21`** exclut explicitement Canaplia et les raccourcis de progression du Shop.
Lalpha.222 améliore le terminal de stockage avec une recherche Unicode, des quantités compactes et des emplacements qui ne glissent plus lorsqu'une pile est vidée. Elle ajoute aussi le suivi textuel des quêtes actives en haut à gauche, masquable avec une touche configurable, ainsi que des suggestions dIDs ditems et de mobs dans l’éditeur de demandes. JEI passe à la bêta Fabric 26.2 `30.29.0.196`.
Lalpha.221 corrige la cause réelle du disque blanc ralenti et trop grave : Ambiance décode désormais la source AAC en stéréo native à 48 kHz, puis moyenne explicitement chaque paire gauche/droite vers le flux mono positionnel de Minecraft. OpenAL reste à sa hauteur normale `1.0`, sans compensation artificielle ni changement de durée.
Lalpha.220 transforme le terminal de stockage en véritable menu de conteneur, sans bouton de fermeture propriétaire. Les clics gauche/droit et shift-clic permettent maintenant de retirer ou déposer des piles depuis toutes les rangées dinventaire débloquées par Sanctuary, jusqu’à six ; la grille agrégée reste non réorganisable et remplit chaque coffre ou baril dans lordre de découverte avant de passer au suivant.
+288 -190
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -28,7 +28,7 @@ iliketomoveit_version=0.0.0-alpha.16
iliketomoveit_lifecycle=active
onlyfun_version=0.0.0-alpha.25
onlyfun_lifecycle=active
sanctuary_version=0.0.0-alpha.124
sanctuary_version=0.0.0-alpha.125
sanctuary_lifecycle=active
pack_version=26.2.0-alpha.221
pack_version=26.2.0-alpha.222
maven_group=fr.koka99cab.sanctuary26
@@ -0,0 +1,60 @@
{
"schema_version": 1,
"source": {
"pack_version": "26.2.0-alpha.221",
"modules": {
"redstoner": "0.0.0-alpha.12",
"sanctuary": "0.0.0-alpha.122"
}
},
"target": {
"pack_version": "26.2.0-alpha.222",
"modules": {
"redstoner": "0.0.0-alpha.13",
"sanctuary": "0.0.0-alpha.124"
}
},
"storage_terminal_search": {
"owner": "redstoner",
"maximum_search_length": 32,
"searches_display_name_item_id_and_translation": true,
"accent_and_unicode_folding": true,
"compact_quantity_labels": true,
"exact_quantity_tooltip": true,
"empty_slots_remain_stable_until_new_search_or_reopen": true,
"server_authoritative_transfers": true
},
"quest_tracker": {
"owner": "sanctuary",
"default_key": "J",
"maximum_entries": 8,
"accepted_quests_only": true,
"reserved_contracts_included": true,
"own_published_requests_excluded": true,
"transparent_text_only_hud": true,
"item_and_mob_id_suggestions": true,
"server_authoritative_snapshot": true
},
"compatibility": {
"new_world_required": false,
"save_reset_required": false,
"existing_saves_supported": true,
"registry_ids_changed": false,
"redstoner_data_version": 3,
"redstoner_network_protocol": 7,
"sanctuary_data_version": 11,
"sanctuary_network_protocol": 22
},
"distribution": {
"pack_release_updated": true,
"packwiz_index_refreshed": true,
"prism_modrinth_index_updated": true,
"shared_client_server_mrpack": true,
"jei": {
"source": "30.28.0.193",
"target": "30.29.0.196",
"modrinth_version_id": "vvwsu2BR",
"packwiz_and_prism_synchronized": true
}
}
}
+13 -13
View File
@@ -14,15 +14,15 @@ hash = "41f4f4c88c71d0a23446d5014f7407d55ad477441847099cad24492b032335a0"
[[files]]
file = "mods/ambiance-0.0.0-alpha.22.jar"
hash = "4d5c31315c436c716a48e7d563fd179be2f39951c24bf4641f83a05a24323a3e"
hash = "a6b7d98971f411b202f5679aae05c1bc93754ee9a42d10dc903413b17ba97a63"
[[files]]
file = "mods/anotherworld-0.0.0-alpha.55.jar"
hash = "7fbe2ac0d2ece62285aa8277ff92284760f2ccfd6d08159015661978bd0cde9a"
hash = "cac322006ad9a724bb656b400cf47d84a18e76cd1a35f4bd13c112f443d89275"
[[files]]
file = "mods/events-0.0.0-alpha.0.jar"
hash = "cbf8d47fb82ca06c438b75fd72f805acc9813dffc6168d6786d54644ef8bb387"
hash = "8d2566ceb838a486fe9d8539acd127446e51d7085dcbefa86ee3c1aea4630a6d"
[[files]]
file = "mods/fabric-api.pw.toml"
@@ -31,7 +31,7 @@ metafile = true
[[files]]
file = "mods/iliketomoveit-0.0.0-alpha.16.jar"
hash = "26156014813bfa4384194ac0f20395b6df590a5bc29cd91a32114ced988bbaf3"
hash = "94e5afefab9485c40a47342dc84f401fa126be40064832929601e781b3a03738"
[[files]]
file = "mods/iris.pw.toml"
@@ -40,32 +40,32 @@ metafile = true
[[files]]
file = "mods/itsalive-0.0.0-alpha.36.jar"
hash = "5673ab7d605f2f1d18d807f0939f90ecedc13c892d03991fd60f7d02d74f9404"
hash = "94899dba383aff41c39f7f25ee1a6b7998aa1e9889e7b9e9039a984e0bc0882f"
[[files]]
file = "mods/jei.pw.toml"
hash = "a213ae237f9b3934bdcd0d282119d4166d0ccbb7c7223daa8d7ed956c69a0e3f"
hash = "115d2239bc86ecdad88705dbc215d27ddb6a08d94f74fa538abb6a67a00bf71e"
metafile = true
[[files]]
file = "mods/masterkey-0.0.0-alpha.8.jar"
hash = "202b159fd04b7447cda7fa9f7360693106927904819e834956ab46f6e07f658e"
hash = "102c3da5d201452be23ab84338e023c284f4770270ca945b0c63b6a996fb2922"
[[files]]
file = "mods/onlyfun-0.0.0-alpha.25.jar"
hash = "6551d75cb3cb7be220b2aa6033d4eb533fa28a39ee88d66bbb0c40244c08c259"
hash = "2676072de7dbc9b27c4fd0e50e797832356fbc903e80c5bd9a5bd55c1a5aff1b"
[[files]]
file = "mods/ouch-0.0.0-alpha.10.jar"
hash = "0c2e0e2ef7f6c944d0b6d5609c42431a0a31f1d92087c3b5c28b67f76795d8cf"
hash = "59815a0415c48c92acfc7c18bd2399780bab9b0b5eee69c2b35a66cb1474109b"
[[files]]
file = "mods/redstoner-0.0.0-alpha.12.jar"
hash = "fee65e8d865f918ec8174b7a808762658ed91b566af590d99a3448f9d237175a"
file = "mods/redstoner-0.0.0-alpha.13.jar"
hash = "0bd3888b13c894aed806696a5e73e076c7ed985eff2c242f0df9ec64ff3fcb37"
[[files]]
file = "mods/sanctuary-0.0.0-alpha.122.jar"
hash = "9183652d13fd3c15663ca645f96f2a70026cb4d3a82f62e92fc3c72aea72d589"
file = "mods/sanctuary-0.0.0-alpha.124.jar"
hash = "39a3db3d1d9876ac5c7a7135a3b524c82508d0ee2e0c5c410c6515ff8931c6fb"
[[files]]
file = "mods/simple-voice-chat.pw.toml"
+4 -4
View File
@@ -1,14 +1,14 @@
name = "Just Enough Items (JEI)"
filename = "jei-26.2-fabric-30.28.0.193.jar"
filename = "jei-26.2-fabric-30.29.0.196.jar"
side = "both"
pin = true
[download]
url = "https://cdn.modrinth.com/data/u6dRKJwZ/versions/qaCQ7LGO/jei-26.2-fabric-30.28.0.193.jar"
url = "https://cdn.modrinth.com/data/u6dRKJwZ/versions/vvwsu2BR/jei-26.2-fabric-30.29.0.196.jar"
hash-format = "sha512"
hash = "0869f4d5840a732a04bedc7684b40140f087f3d759066c206d9d1ec710007f7c9585e4f5ebb04f10e56bef1cf2eddc6978bb2b2c93541cff5fcf7e1609859468"
hash = "c62a3e264d4b0ec398b9aa717a4832eecb8663bbcec4483dc58a9999d5e4e55ca4f5bca285a0d4a003220c698c3e87c8a3294366e085e72545e4afe84aec92c7"
[update]
[update.modrinth]
mod-id = "u6dRKJwZ"
version = "qaCQ7LGO"
version = "vvwsu2BR"
+2 -2
View File
@@ -1,12 +1,12 @@
name = "Sanctuary 26.2"
author = "KOKA99CAB"
version = "26.2.0-alpha.221"
version = "26.2.0-alpha.222"
pack-format = "packwiz:1.1.0"
[index]
file = "index.toml"
hash-format = "sha256"
hash = "60e8342eef216dd225488fe4225a28c9b9940858642cbfc8025541affd150ab0"
hash = "53b5bec1b113ab2b353e6a142e3dbc4617ea7d27003bcbb09b668938dd262ddd"
[versions]
fabric = "0.19.3"
+6 -6
View File
@@ -3,7 +3,7 @@
"game": "minecraft",
"name": "Sanctuary 26.2",
"summary": "Sanctuary 26.2 — progression, exploration, worldgen and multiplayer systems by KOKA99CAB.",
"versionId": "26.2.0-alpha.221",
"versionId": "26.2.0-alpha.222",
"files": [
{
"path": "mods/fabric-api-0.158.0+26.2.jar",
@@ -21,19 +21,19 @@
"fileSize": 2535765
},
{
"path": "mods/jei-26.2-fabric-30.28.0.193.jar",
"path": "mods/jei-26.2-fabric-30.29.0.196.jar",
"hashes": {
"sha1": "494f9dc787d6c9cd9d32793da2784ab16834faf3",
"sha512": "0869f4d5840a732a04bedc7684b40140f087f3d759066c206d9d1ec710007f7c9585e4f5ebb04f10e56bef1cf2eddc6978bb2b2c93541cff5fcf7e1609859468"
"sha1": "c2f2c6b5822a560fe9872921b908ffc2f3297d00",
"sha512": "c62a3e264d4b0ec398b9aa717a4832eecb8663bbcec4483dc58a9999d5e4e55ca4f5bca285a0d4a003220c698c3e87c8a3294366e085e72545e4afe84aec92c7"
},
"env": {
"client": "required",
"server": "required"
},
"downloads": [
"https://cdn.modrinth.com/data/u6dRKJwZ/versions/qaCQ7LGO/jei-26.2-fabric-30.28.0.193.jar"
"https://cdn.modrinth.com/data/u6dRKJwZ/versions/vvwsu2BR/jei-26.2-fabric-30.29.0.196.jar"
],
"fileSize": 1822836
"fileSize": 1838353
},
{
"path": "mods/voicechat-fabric-2.6.22+26.2.jar",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"schema_version": 1,
"pack_version": "26.2.0-alpha.221",
"pack_version": "26.2.0-alpha.222",
"release_channel": "alpha",
"minecraft_version": "26.2",
"fabric_loader_version": "0.19.3",
@@ -9,7 +9,7 @@
"project_id": "FgOqyAXd",
"project_slug": "sanctuary-koka99",
"project_url": "https://modrinth.com/modpack/sanctuary-koka99",
"primary_file": "sanctuary-26.2.0-alpha.221.mrpack",
"primary_file": "sanctuary-26.2.0-alpha.222.mrpack",
"shared_client_server_pack": true,
"update_owner": "packwiz-installer",
"check_on_launch": true,
+20 -11
View File
@@ -76,6 +76,14 @@ tasks.register("questTrackerModelSmoke", JavaExec) {
mainClass = "fr.koka99cab.sanctuary26.sanctuary.shop.QuestTrackerModelSmoke"
}
tasks.register("questTrackerDisplaySmoke", JavaExec) {
group = "verification"
description = "Checks quest-tracker display modes and permanent-F3 layout."
dependsOn tasks.named("testClasses")
classpath = sourceSets.test.runtimeClasspath
mainClass = "fr.koka99cab.sanctuary26.sanctuary.client.QuestTrackerDisplaySmoke"
}
tasks.register("questIdSuggestionsSmoke", JavaExec) {
group = "verification"
description = "Checks accent-folded quest-panel item name ranking and inventory pins."
@@ -151,7 +159,7 @@ tasks.register("verifyBlackMarket") {
def questData = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardData.java").text
def quests = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/QuestBoardService.java").text
def inventoryView = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/api/inventory/SanctuaryPlayerInventoryView.java").text
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| !data.contains('SAVE_KEY = "black_market"') || !data.contains("DATA_VERSION = 2")
|| !data.contains("ItemStack.CODEC.fieldOf(\"item\")") || !data.contains("replaceListing")
|| !service.contains("LISTING_FEE_RUBIES = 1") || !service.contains("COMMISSION_PERCENT = 5")
@@ -2850,7 +2858,7 @@ tasks.register("verifyShopCurrencyRowsRelease") {
"src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/BlackMarketService.java").text
def shop = file(
"src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopService.java").text
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.117"
|| migration.shop_currency_inventory?.all_unlocked_rows_counted != true
|| migration.shop_currency_inventory?.overflow_rows_counted != true
@@ -3016,7 +3024,7 @@ tasks.register("verifyProgressivePauseMenu") {
"pack/migrations/26.2.0-alpha.190-progressive-pause-menu-to-alpha.191.json"))
def pauseMenu = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/client/PauseScreenMixin.java").text
def costs = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/progression/SanctuaryProgressionCosts.java").text
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.target?.pack_version != "26.2.0-alpha.191"
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.109"
|| migration.pause_menu?.progression_full_width != true
@@ -3054,7 +3062,7 @@ tasks.register("verifyShopPricingLayout") {
"pack/migrations/26.2.0-alpha.192-shop-pricing-layout-to-alpha.193.json"))
def screen = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/client/SanctuaryShopScreen.java").text
def progression = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopProgression.java").text
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.110"
|| migration.shop_layout?.locked_offer_buttons != 6
|| migration.shop_layout?.button_slots != [4, 5, 6, 7, 8, 9]
@@ -3085,7 +3093,7 @@ tasks.register("verifyShopCsvBossGates") {
"pack/migrations/26.2.0-alpha.194-shop-csv-boss-gates-to-alpha.195.json"))
def provider = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopCatalogProvider.java").text
def catalog = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopOfferCatalog.java").text
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.target?.pack_version != "26.2.0-alpha.195"
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.111"
|| migration.compatibility?.sanctuary_data_version != 11
@@ -3116,7 +3124,7 @@ tasks.register("verifyDeliveryBoxOrientation") {
def blockstate = new JsonSlurper().parse(
file("src/main/resources/assets/sanctuary/blockstates/delivery_box.json"))
def expectedVariants = ["facing=north", "facing=east", "facing=south", "facing=west"].toSet()
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.112"
|| migration.mailbox?.id != "sanctuary:delivery_box"
|| migration.mailbox?.horizontal_facing != true
@@ -3144,7 +3152,7 @@ tasks.register("verifyInventoryShiftClick") {
def mixin = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/AbstractContainerMenuMixin.java").text
def screen = file("src/main/java/fr/koka99cab/sanctuary26/sanctuary/mixin/client/AbstractContainerScreenMixin.java").text
def gui = file("INVENTORY_GUI.md").text
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| !transfer.contains("shouldAppendOverflow")
|| !transfer.contains("isVanillaStorageRange")
|| !transfer.contains("hotbarStart")
@@ -3190,7 +3198,7 @@ tasks.register("verifyQuestRequests") {
new JsonSlurper().parse(file("src/main/resources/data/sanctuary/loot_table/blocks/${path}.json"))
.pools[0].entries[0].name
}
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.113"
|| migration.quest_requests?.save_key != "quest_requests"
|| migration.quest_requests?.data_version != 1
@@ -3248,7 +3256,7 @@ tasks.register("verifyQuestRequestContracts") {
def locales = ["fr_fr", "en_us", "ru_ru"].collectEntries { locale ->
[(locale): new JsonSlurper().parse(file("src/main/resources/assets/sanctuary/lang/${locale}.json"))]
}
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.source?.pack_version != "26.2.0-alpha.198"
|| migration.source?.modules?.sanctuary != "0.0.0-alpha.114"
|| migration.target?.pack_version != "26.2.0-alpha.199"
@@ -3317,7 +3325,7 @@ tasks.register("verifyQuestRequestEscrow") {
def locales = ["fr_fr", "en_us", "ru_ru"].collectEntries { locale ->
[(locale): new JsonSlurper().parse(file("src/main/resources/assets/sanctuary/lang/${locale}.json"))]
}
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.source?.pack_version != "26.2.0-alpha.200"
|| migration.source?.modules?.sanctuary != "0.0.0-alpha.115"
|| migration.target?.pack_version != "26.2.0-alpha.201"
@@ -3389,7 +3397,7 @@ tasks.register("verifyAlphaIndev") {
def locales = ["fr_fr", "en_us", "ru_ru"].collect {
new JsonSlurper().parse(file("src/main/resources/assets/sanctuary/lang/${it}.json"))
}
if (project.version.toString() != "0.0.0-alpha.124"
if (project.version.toString() != "0.0.0-alpha.125"
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.122"
|| migration.alpha_world?.dimension != "sanctuary:alpha"
|| migration.alpha_world?.legacy_dimension != "portals:alpha"
@@ -3421,6 +3429,7 @@ tasks.named("check") {
dependsOn tasks.named("celestialGraphSmoke")
dependsOn tasks.named("questRequestModelSmoke")
dependsOn tasks.named("questTrackerModelSmoke")
dependsOn tasks.named("questTrackerDisplaySmoke")
dependsOn tasks.named("questIdSuggestionsSmoke")
dependsOn tasks.named("alphaIndevGeneratorSmoke")
dependsOn tasks.named("verifyAlphaIndev")
@@ -0,0 +1,19 @@
package fr.koka99cab.sanctuary26.sanctuary.client;
/** Tracks the exact height occupied by the left-hand F3 column. */
public final class QuestTrackerDebugLayout {
private static final int TOP_MARGIN = 2;
private static final int DEBUG_LINE_HEIGHT = 9;
private static final int GAP = 2;
private static int leftLineCount;
private QuestTrackerDebugLayout() {}
public static void captureLeftLines(int count) {
leftLineCount = Math.max(0, count);
}
static int textTop(boolean debugVisible) {
return debugVisible ? TOP_MARGIN + leftLineCount * DEBUG_LINE_HEIGHT + GAP : TOP_MARGIN;
}
}
@@ -0,0 +1,30 @@
package fr.koka99cab.sanctuary26.sanctuary.client;
/** Persisted visual styles offered by the quest tracker. */
enum QuestTrackerDisplayMode {
TEXT("text"),
PANEL("panel");
private final String id;
QuestTrackerDisplayMode(String id) {
this.id = id;
}
String id() {
return id;
}
QuestTrackerDisplayMode next() {
return this == TEXT ? PANEL : TEXT;
}
static QuestTrackerDisplayMode byId(String value) {
if (value != null) {
for (QuestTrackerDisplayMode mode : values()) {
if (mode.id.equalsIgnoreCase(value)) return mode;
}
}
return TEXT;
}
}
@@ -14,11 +14,21 @@ import net.minecraft.network.chat.Component;
import net.minecraft.resources.Identifier;
import net.minecraft.world.item.ItemStack;
/** Transparent, toggleable text overlay for accepted quests. */
/** Toggleable quest overlay with transparent F3-aligned and original panel modes. */
final class QuestTrackerHud {
private static final int MAX_WIDTH = 240;
private static final int MARGIN = 2;
private static final int ENTRY_SPACING = 2;
private static final int TEXT_MAX_WIDTH = 240;
private static final int TEXT_MARGIN = 2;
private static final int TEXT_ENTRY_SPACING = 2;
private static final int PANEL_WIDTH = 168;
private static final int PANEL_MARGIN = 6;
private static final int PANEL_HEADER_HEIGHT = 14;
private static final int PANEL_ENTRY_HEIGHT = 22;
private static final int PANEL_EMPTY_HEIGHT = 22;
private static final int PANEL_BACKGROUND = 0xD0181417;
private static final int PANEL_OUTLINE = 0xFFE8A8C3;
private static final int PANEL_TITLE = 0xFFF2C4D7;
private static final int PANEL_MUTED = 0xFF9F8B95;
private static final int PANEL_BAR_BACK = 0xFF3A2A30;
private static final int BODY = 0xFFE9DDE2;
private QuestTrackerHud() {}
@@ -32,29 +42,82 @@ final class QuestTrackerHud {
if (client.player == null || client.level == null || client.gui.screen() != null) return;
if (!SanctuaryClientQuests.visible()) return;
List<QuestTrackerSnapshotPayload.Entry> entries = SanctuaryClientQuests.entries();
if (SanctuaryClientQuests.displayMode() == QuestTrackerDisplayMode.PANEL) {
renderPanel(graphics, client, entries);
return;
}
renderText(graphics, client, entries);
}
private static void renderText(GuiGraphicsExtractor graphics, Minecraft client,
List<QuestTrackerSnapshotPayload.Entry> entries) {
if (entries.isEmpty()) return;
int width = Math.max(1, Math.min(MAX_WIDTH, graphics.guiWidth() - MARGIN * 2));
int y = MARGIN;
int width = Math.max(1, Math.min(TEXT_MAX_WIDTH, graphics.guiWidth() - TEXT_MARGIN * 2));
int y = QuestTrackerDebugLayout.textTop(client.getDebugOverlay().showDebugScreen());
for (QuestTrackerSnapshotPayload.Entry entry : entries) {
drawEntry(graphics, client.font, entry, MARGIN, y, width);
y += client.font.lineHeight * 2 + ENTRY_SPACING;
drawTextEntry(graphics, client.font, entry, TEXT_MARGIN, y, width);
y += client.font.lineHeight * 2 + TEXT_ENTRY_SPACING;
}
}
private static void drawEntry(GuiGraphicsExtractor graphics, Font font,
private static void drawTextEntry(GuiGraphicsExtractor graphics, Font font,
QuestTrackerSnapshotPayload.Entry entry, int x, int y, int width) {
int accent = 0xFF000000 | (entry.accent() & 0xFFFFFF);
graphics.text(font, ellipsize(font, title(entry), width), x, y, accent, true);
Component progress = Component.translatable("hud.sanctuary.quest_tracker.progress",
action(entry), describeTarget(entry), entry.progress(), entry.required());
Component progress = progress(entry);
graphics.text(font, ellipsize(font, progress, width), x, y + font.lineHeight, BODY, true);
}
private static void renderPanel(GuiGraphicsExtractor graphics, Minecraft client,
List<QuestTrackerSnapshotPayload.Entry> entries) {
int height = PANEL_HEADER_HEIGHT + 6
+ (entries.isEmpty() ? PANEL_EMPTY_HEIGHT : entries.size() * PANEL_ENTRY_HEIGHT);
int right = PANEL_MARGIN + PANEL_WIDTH;
int bottom = PANEL_MARGIN + height;
graphics.fill(PANEL_MARGIN, PANEL_MARGIN, right, bottom, PANEL_BACKGROUND);
graphics.outline(PANEL_MARGIN, PANEL_MARGIN, PANEL_WIDTH, height, PANEL_OUTLINE);
graphics.text(client.font, Component.translatable("hud.sanctuary.quest_tracker.title"),
PANEL_MARGIN + 6, PANEL_MARGIN + 4, PANEL_TITLE);
if (entries.isEmpty()) {
graphics.text(client.font, Component.translatable("hud.sanctuary.quest_tracker.empty"),
PANEL_MARGIN + 6, PANEL_MARGIN + PANEL_HEADER_HEIGHT + 4, PANEL_MUTED);
return;
}
int y = PANEL_MARGIN + PANEL_HEADER_HEIGHT + 2;
for (QuestTrackerSnapshotPayload.Entry entry : entries) {
drawPanelEntry(graphics, client.font, entry, PANEL_MARGIN + 6, y, PANEL_WIDTH - 12);
y += PANEL_ENTRY_HEIGHT;
}
}
private static void drawPanelEntry(GuiGraphicsExtractor graphics, Font font,
QuestTrackerSnapshotPayload.Entry entry, int x, int y, int width) {
int accent = 0xFF000000 | (entry.accent() & 0xFFFFFF);
graphics.text(font, ellipsize(font, title(entry), width), x, y, accent);
graphics.text(font, ellipsize(font, progress(entry), width), x, y + 9, BODY);
int barTop = y + 18;
graphics.fill(x, barTop, x + width, barTop + 2, PANEL_BAR_BACK);
int filled = Math.round(width * Math.min(1.0F, entry.progress() / (float) entry.required()));
if (filled > 0) graphics.fill(x, barTop, x + filled, barTop + 2, accent);
}
private static Component title(QuestTrackerSnapshotPayload.Entry entry) {
if (QuestTrackerSync.SOURCE_BOARD.equals(entry.source())) {
return switch (entry.title()) {
case "ruby", "sapphire", "emerald" -> Component.translatable(
"hud.sanctuary.quest_tracker.reward." + entry.title());
default -> Component.translatable("hud.sanctuary.quest_tracker.source.board");
};
}
if (!entry.title().isBlank()) return Component.literal(entry.title());
return Component.translatable("hud.sanctuary.quest_tracker.source." + entry.source());
}
private static Component progress(QuestTrackerSnapshotPayload.Entry entry) {
return Component.translatable("hud.sanctuary.quest_tracker.progress",
action(entry), describeTarget(entry), entry.progress(), entry.required());
}
private static Component action(QuestTrackerSnapshotPayload.Entry entry) {
if (QuestTrackerSync.SOURCE_BOARD.equals(entry.source())) {
return Component.translatable("sign.sanctuary.quest_board.action." + entry.action());
@@ -50,6 +50,7 @@ public final class SanctuaryClient implements ClientModInitializer {
private static KeyMapping celestialKey;
private static KeyMapping switchHotbarRowKey;
private static KeyMapping questTrackerKey;
private static KeyMapping questTrackerModeKey;
private static boolean zoomWasHeld;
private static boolean smokeScreenOpened;
private static boolean inventorySmokeScreenOpened;
@@ -99,6 +100,12 @@ public final class SanctuaryClient implements ClientModInitializer {
GLFW.GLFW_KEY_J,
SANCTUARY_CATEGORY
));
questTrackerModeKey = KeyMappingHelper.registerKeyMapping(new KeyMapping(
"key.sanctuary.quest_tracker_mode",
InputConstants.Type.KEYSYM,
GLFW.GLFW_KEY_N,
SANCTUARY_CATEGORY
));
SanctuaryAtlasOptions.load();
SanctuaryClientQuests.load();
SanctuaryBulkActionsClient.initialize();
@@ -198,6 +205,10 @@ public final class SanctuaryClient implements ClientModInitializer {
if (client.player != null && client.level != null && client.gui.screen() == null)
toggleQuestTracker(client);
}
while (questTrackerModeKey.consumeClick()) {
if (client.player != null && client.level != null && client.gui.screen() == null)
cycleQuestTrackerMode(client);
}
while (celestialKey.consumeClick()) {
if (client.player != null && client.level != null && client.gui.screen() == null)
client.gui.setScreen(new CelestialSkyScreen(null));
@@ -300,6 +311,13 @@ public final class SanctuaryClient implements ClientModInitializer {
: "hud.sanctuary.quest_tracker.toggle_off"));
}
private static void cycleQuestTrackerMode(Minecraft client) {
QuestTrackerDisplayMode mode = SanctuaryClientQuests.cycleDisplayMode();
if (client.player == null) return;
client.player.sendOverlayMessage(Component.translatable(
"hud.sanctuary.quest_tracker.mode." + mode.id()));
}
private static void toggleAtlas(Minecraft client) {
if (client.gui.screen() instanceof SanctuaryAtlasScreen) {
client.gui.setScreen(null);
@@ -13,7 +13,7 @@ import java.nio.file.Path;
import java.util.List;
import net.fabricmc.loader.api.FabricLoader;
/** Client-side quest tracker snapshot and visibility preference. */
/** Client-side quest tracker snapshot and persisted display preferences. */
final class SanctuaryClientQuests {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private static final Path FILE = FabricLoader.getInstance().getConfigDir()
@@ -61,6 +61,19 @@ final class SanctuaryClientQuests {
return settings.visible;
}
static QuestTrackerDisplayMode displayMode() {
load();
return QuestTrackerDisplayMode.byId(settings.mode);
}
static QuestTrackerDisplayMode cycleDisplayMode() {
load();
QuestTrackerDisplayMode mode = displayMode().next();
settings.mode = mode.id();
save();
return mode;
}
private static void save() {
try {
Files.createDirectories(FILE.getParent());
@@ -75,5 +88,6 @@ final class SanctuaryClientQuests {
private static final class Settings {
boolean visible = true;
String mode = QuestTrackerDisplayMode.TEXT.id();
}
}
@@ -0,0 +1,20 @@
package fr.koka99cab.sanctuary26.sanctuary.mixin.client;
import fr.koka99cab.sanctuary26.sanctuary.client.QuestTrackerDebugLayout;
import java.util.List;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.components.DebugScreenOverlay;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
/** Lets the transparent quest tracker start immediately below the current F3 lines. */
@Mixin(DebugScreenOverlay.class)
abstract class DebugScreenOverlayMixin {
@Inject(method = "extractLines", at = @At("HEAD"))
private void sanctuary$captureLeftColumn(GuiGraphicsExtractor graphics, List<String> lines,
boolean left, CallbackInfo ci) {
if (left) QuestTrackerDebugLayout.captureLeftLines(lines.size());
}
}
@@ -3,8 +3,10 @@ package fr.koka99cab.sanctuary26.sanctuary.shop;
import fr.koka99cab.sanctuary26.sanctuary.network.QuestTrackerSnapshotPayload;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.resources.Identifier;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
@@ -43,29 +45,58 @@ public final class QuestTrackerSync {
}
private static List<QuestTrackerSnapshotPayload.Entry> entries(ServerPlayer player) {
List<QuestTrackerSnapshotPayload.Entry> entries = new ArrayList<>();
List<RankedEntry> entries = new ArrayList<>();
UUID playerId = player.getUUID();
MinecraftServer server = player.level().getServer();
for (QuestRequestData.Request request : QuestRequestData.get(server).requests()) {
if (entries.size() >= QuestTrackerSnapshotPayload.MAX_ENTRIES) break;
if (!tracksAcceptedRequest(request, playerId)) continue;
entries.add(new QuestTrackerSnapshotPayload.Entry(
addRanked(entries, new RankedEntry(new QuestTrackerSnapshotPayload.Entry(
SOURCE_REQUEST, clip(request.id(), 36), clip(request.objective(), 96),
clip(request.action(), 16), clip(request.target(), 64),
request.progress(), request.quantity(), request.ownerColor()));
request.progress(), request.quantity(), request.ownerColor()),
rewardOrder(request.rewardTarget())));
}
for (QuestBoardData.Board board : QuestBoardData.get(server).boards()) {
if (entries.size() >= QuestTrackerSnapshotPayload.MAX_ENTRIES) break;
QuestBoardData.Contract contract = board.contract().orElse(null);
if (contract == null || contract.claim().isEmpty()
|| !contract.claim().get().playerId().equals(playerId)) continue;
QuestBoardData.Objective objective = contract.objective();
entries.add(new QuestTrackerSnapshotPayload.Entry(
SOURCE_BOARD, clip(board.id(), 36), "",
addRanked(entries, new RankedEntry(new QuestTrackerSnapshotPayload.Entry(
SOURCE_BOARD, clip(board.id(), 36), board.questTier().id(),
clip(objective.kind(), 16), clip(objective.target(), 64),
objective.progress(), objective.required(), tierColor(board.questTier())));
objective.progress(), objective.required(), tierColor(board.questTier())),
rewardOrder(board.questTier())));
}
return entries;
return entries.stream().map(RankedEntry::entry).toList();
}
private static void addRanked(List<RankedEntry> entries, RankedEntry candidate) {
int index = 0;
while (index < entries.size() && entries.get(index).rewardOrder() <= candidate.rewardOrder()) {
index++;
}
entries.add(index, candidate);
if (entries.size() > QuestTrackerSnapshotPayload.MAX_ENTRIES) entries.removeLast();
}
static int rewardOrder(QuestBoardTier tier) {
return switch (tier) {
case RUBY -> 0;
case SAPPHIRE -> 1;
case EMERALD -> 2;
};
}
static int rewardOrder(String rewardTarget) {
Identifier id = Identifier.tryParse(rewardTarget);
String path = (id == null ? rewardTarget : id.getPath());
if (path == null) return 3;
path = path.toLowerCase(Locale.ROOT);
if (path.equals("ruby") || path.startsWith("ruby_")) return 0;
if (path.equals("sapphire") || path.equals("saphire")
|| path.startsWith("sapphire_") || path.startsWith("saphire_")) return 1;
if (path.equals("emerald") || path.startsWith("emerald_")) return 2;
return 3;
}
static boolean tracksAcceptedRequest(QuestRequestData.Request request, UUID playerId) {
@@ -87,4 +118,6 @@ public final class QuestTrackerSync {
if (value == null) return "";
return value.length() <= maximumLength ? value : value.substring(0, maximumLength);
}
private record RankedEntry(QuestTrackerSnapshotPayload.Entry entry, int rewardOrder) {}
}
@@ -244,11 +244,19 @@
"key.sanctuary.zoom": "Telephoto zoom",
"key.sanctuary.switch_hotbar_row": "Cycle hotbar row",
"key.sanctuary.quest_tracker": "Quest tracker",
"key.sanctuary.quest_tracker_mode": "Quest tracker style",
"hud.sanctuary.quest_tracker.title": "Quests",
"hud.sanctuary.quest_tracker.empty": "No quest in progress",
"hud.sanctuary.quest_tracker.toggle_on": "Quest tracker shown",
"hud.sanctuary.quest_tracker.toggle_off": "Quest tracker hidden",
"hud.sanctuary.quest_tracker.mode.text": "Quest tracker: F3 text",
"hud.sanctuary.quest_tracker.mode.panel": "Quest tracker: Chris's panel",
"hud.sanctuary.quest_tracker.source.request": "Accepted request",
"hud.sanctuary.quest_tracker.source.owned": "Your request",
"hud.sanctuary.quest_tracker.source.board": "Daily contract",
"hud.sanctuary.quest_tracker.source.board": "Quest contract",
"hud.sanctuary.quest_tracker.reward.emerald": "Emerald quest",
"hud.sanctuary.quest_tracker.reward.ruby": "Ruby quest",
"hud.sanctuary.quest_tracker.reward.sapphire": "Sapphire quest",
"hud.sanctuary.quest_tracker.progress": "%s · %s · %s/%s",
"message.sanctuary.zoom": "Zoom · FOV %s",
"message.sanctuary.companion.passive_only": "This essence only has a passive power.",
@@ -244,11 +244,19 @@
"key.sanctuary.zoom": "Zoom téléobjectif",
"key.sanctuary.switch_hotbar_row": "Changer de rangée de hotbar",
"key.sanctuary.quest_tracker": "Suivi des quêtes",
"key.sanctuary.quest_tracker_mode": "Style du suivi des quêtes",
"hud.sanctuary.quest_tracker.title": "Quêtes",
"hud.sanctuary.quest_tracker.empty": "Aucune quête en cours",
"hud.sanctuary.quest_tracker.toggle_on": "Suivi des quêtes affiché",
"hud.sanctuary.quest_tracker.toggle_off": "Suivi des quêtes masqué",
"hud.sanctuary.quest_tracker.mode.text": "Suivi des quêtes : texte F3",
"hud.sanctuary.quest_tracker.mode.panel": "Suivi des quêtes : panneau de Chris",
"hud.sanctuary.quest_tracker.source.request": "Demande acceptée",
"hud.sanctuary.quest_tracker.source.owned": "Votre demande",
"hud.sanctuary.quest_tracker.source.board": "Contrat du jour",
"hud.sanctuary.quest_tracker.source.board": "Contrat de quête",
"hud.sanctuary.quest_tracker.reward.emerald": "Quête émeraude",
"hud.sanctuary.quest_tracker.reward.ruby": "Quête rubis",
"hud.sanctuary.quest_tracker.reward.sapphire": "Quête saphir",
"hud.sanctuary.quest_tracker.progress": "%s · %s · %s/%s",
"message.sanctuary.zoom": "Zoom · FOV %s",
"message.sanctuary.companion.passive_only": "Cette essence ne possède qu'un pouvoir passif.",
@@ -156,11 +156,19 @@
"key.sanctuary.zoom": "Телефото-зум",
"key.sanctuary.switch_hotbar_row": "Сменить ряд панели быстрого доступа",
"key.sanctuary.quest_tracker": "Трекер заданий",
"key.sanctuary.quest_tracker_mode": "Стиль трекера заданий",
"hud.sanctuary.quest_tracker.title": "Задания",
"hud.sanctuary.quest_tracker.empty": "Нет активного задания",
"hud.sanctuary.quest_tracker.toggle_on": "Трекер заданий показан",
"hud.sanctuary.quest_tracker.toggle_off": "Трекер заданий скрыт",
"hud.sanctuary.quest_tracker.mode.text": "Трекер заданий: текст F3",
"hud.sanctuary.quest_tracker.mode.panel": "Трекер заданий: панель Криса",
"hud.sanctuary.quest_tracker.source.request": "Принятый запрос",
"hud.sanctuary.quest_tracker.source.owned": "Ваш запрос",
"hud.sanctuary.quest_tracker.source.board": "Дневной контракт",
"hud.sanctuary.quest_tracker.source.board": "Контракт задания",
"hud.sanctuary.quest_tracker.reward.emerald": "Изумрудное задание",
"hud.sanctuary.quest_tracker.reward.ruby": "Рубиновое задание",
"hud.sanctuary.quest_tracker.reward.sapphire": "Сапфировое задание",
"hud.sanctuary.quest_tracker.progress": "%s · %s · %s/%s",
"message.sanctuary.zoom": "Зум · FOV %s",
"message.sanctuary.companion.passive_only": "У этой сущности есть только пассивная способность.",
@@ -25,6 +25,7 @@
"client.CreativeModeInventoryScreenMixin",
"client.CreativeSlotWrapperAccessor",
"client.CreateWorldScreenWorldTabMixin",
"client.DebugScreenOverlayMixin",
"client.GuiGraphicsExtractorMixin",
"client.InventoryScreenMixin",
"client.JeiBookmarkButtonControllerMixin",
@@ -0,0 +1,25 @@
package fr.koka99cab.sanctuary26.sanctuary.client;
/** Checks the persisted display-mode fallback and the F3 column offset. */
public final class QuestTrackerDisplaySmoke {
private QuestTrackerDisplaySmoke() {}
public static void main(String[] args) {
require(QuestTrackerDisplayMode.byId(null) == QuestTrackerDisplayMode.TEXT,
"Missing legacy settings must keep the transparent text mode");
require(QuestTrackerDisplayMode.byId("panel") == QuestTrackerDisplayMode.PANEL,
"Chris's original panel mode must remain selectable");
require(QuestTrackerDisplayMode.PANEL.next() == QuestTrackerDisplayMode.TEXT,
"Display mode cycling must wrap back to text");
QuestTrackerDebugLayout.captureLeftLines(7);
require(QuestTrackerDebugLayout.textTop(false) == 2,
"The tracker must keep its top-left margin when F3 is hidden");
require(QuestTrackerDebugLayout.textTop(true) == 67,
"The tracker must start below all seven permanent F3 lines");
}
private static void require(boolean value, String message) {
if (!value) throw new AssertionError(message);
}
}
@@ -28,6 +28,15 @@ public final class QuestTrackerModelSmoke {
QuestTrackerSnapshotPayload mixed = new QuestTrackerSnapshotPayload(List.of(owned, board));
require(mixed.entries().get(0).required() == 4 && mixed.entries().get(1).progress() == 2,
"Owned and board progress must be preserved");
require(QuestTrackerSync.rewardOrder("anotherworld:ruby")
< QuestTrackerSync.rewardOrder("anotherworld:sapphire"),
"Ruby rewards must be ordered before sapphire rewards");
require(QuestTrackerSync.rewardOrder("anotherworld:saphire")
== QuestTrackerSync.rewardOrder(QuestBoardTier.SAPPHIRE),
"The legacy saphire spelling must remain in the sapphire reward group");
require(QuestTrackerSync.rewardOrder(QuestBoardTier.SAPPHIRE)
< QuestTrackerSync.rewardOrder(QuestBoardTier.EMERALD),
"Sapphire rewards must be ordered before emerald and uncategorized rewards");
require(new QuestTrackerSnapshotPayload(null).entries().isEmpty(),
"Null snapshot must become an empty tracker");
UUID owner = UUID.fromString("6c43c35a-3498-41bb-a77e-10c679e635d1");