release: publier Sanctuary 26.2.0-alpha.225 #125
@@ -6,6 +6,16 @@ Ce journal suit les **versions du modpack**. Lorsqu'un seul module change, sa ve
|
||||
- Depuis `26.2.0-alpha.6`, le pack et les modules ont des versions indépendantes. I Like To Move It et Only Fun deviennent les cinquième et sixième modules actifs dans l'alpha 46.
|
||||
- Les versions ci-dessous ont été reconstituées à partir des JAR archivés, des packs locaux et du code source actuel. Les dates n'étant pas enregistrées de manière fiable, elles ne sont pas inventées.
|
||||
|
||||
## `26.2.0-alpha.225`
|
||||
|
||||
Modules modifiés : **Another World `0.0.0-alpha.56`**, **It's Alive `0.0.0-alpha.38`**, **I Like To Move It `0.0.0-alpha.18`**, **Only Fun `0.0.0-alpha.27`** et **Sanctuary `0.0.0-alpha.129`**. Les cinq autres modules conservent leur version issue de l'alpha.224.
|
||||
|
||||
- exige que l’attaquant possède son propre coffre-fort actif avant de commencer un braquage, revalide cette condition pendant le forage et refuse défensivement tout transfert si le coffre disparaît entre-temps ;
|
||||
- empêche les familiers temporaires Sanctuary de produire du butin vanilla, de l’expérience ou une lootbox Only Fun, et les exclut des contrats de panneau comme des demandes communautaires ;
|
||||
- conserve la variante Canaplia des seize graines historiques pendant le placement, la croissance naturelle, la poudre d’os, la création de la partie haute, le rechargement et la récolte, ce qui réactive notamment l’hybridation Tendre + Olive vers Dorée ;
|
||||
- remplace la laine violette du Magic Carpet par la texture historique 16 × 16 du Totem de Notch, utilisée par l’objet canonique et son alias hérité sans modifier la plateforme 3 × 3 ;
|
||||
- conserve tous les IDs, sauvegardes et formats de données ; les protocoles réseau restent Another World 2, It's Alive 4, I Like To Move It 5, Only Fun 3 et Sanctuary 23, sans nouveau monde ni réinitialisation.
|
||||
|
||||
## `26.2.0-alpha.224`
|
||||
|
||||
Modules modifiés : **It's Alive `0.0.0-alpha.37`**, **Ouch `0.0.0-alpha.11`**, **I Like To Move It `0.0.0-alpha.17`**, **Only Fun `0.0.0-alpha.26`**, **Sanctuary `0.0.0-alpha.128`** et **MasterKey `0.0.0-alpha.9`**.
|
||||
|
||||
@@ -22,7 +22,9 @@ 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.224`**. Cette release distribue It's Alive **`0.0.0-alpha.37`**, Ouch **`0.0.0-alpha.11`**, I Like To Move It **`0.0.0-alpha.17`**, Only Fun **`0.0.0-alpha.26`**, Sanctuary **`0.0.0-alpha.128`** et MasterKey **`0.0.0-alpha.9`** ; les quatre autres modules conservent leur version issue de l'alpha.223.
|
||||
Version courante du pack distribué : **`26.2.0-alpha.225`**. Cette release distribue Another World **`0.0.0-alpha.56`**, It's Alive **`0.0.0-alpha.38`**, I Like To Move It **`0.0.0-alpha.18`**, Only Fun **`0.0.0-alpha.27`** et Sanctuary **`0.0.0-alpha.129`** ; les cinq autres modules conservent leur version issue de l'alpha.224.
|
||||
|
||||
L’alpha.225 corrige quatre tickets : posséder un coffre-fort devient obligatoire pour en braquer un autre, les familiers temporaires ne donnent plus de butin, d’expérience, de lootbox ni de progression de quête, les seize variantes Canaplia survivent à tout leur cycle de culture, et le Magic Carpet retrouve la texture historique du Totem de Notch à la place de la laine violette. Les IDs, sauvegardes, formats de données et protocoles restent compatibles.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -1647,6 +1647,7 @@ tasks.register("generateAnotherWorldResources") {
|
||||
"message.anotherworld.safe_vault.empty_before_break": "Videz le coffre-fort et attendez la fin du forage avant de le démonter.",
|
||||
"message.anotherworld.safe_vault.drill_own": "Vous ne pouvez pas forer votre propre coffre-fort.",
|
||||
"message.anotherworld.safe_vault.inactive": "Ce coffre-fort n’est pas actif.",
|
||||
"message.anotherworld.safe_vault.raid_requires_vault": "Vous devez posséder un coffre-fort actif avant d’en braquer un autre.",
|
||||
"message.anotherworld.safe_vault.empty": "Ce coffre-fort est vide.",
|
||||
"message.anotherworld.safe_vault.owner_offline": "Le propriétaire doit être connecté pour que le coffre-fort soit attaquable.",
|
||||
"message.anotherworld.safe_vault.protected": "Ce coffre-fort reste protégé pendant environ %s minute(s).",
|
||||
@@ -1671,6 +1672,7 @@ tasks.register("generateAnotherWorldResources") {
|
||||
"message.anotherworld.safe_vault.empty_before_break": "Опустошите сейф и дождитесь окончания сверления перед разборкой.",
|
||||
"message.anotherworld.safe_vault.drill_own": "Нельзя сверлить собственный сейф.",
|
||||
"message.anotherworld.safe_vault.inactive": "Этот сейф не активен.",
|
||||
"message.anotherworld.safe_vault.raid_requires_vault": "Для ограбления чужого сейфа нужен собственный активный сейф.",
|
||||
"message.anotherworld.safe_vault.empty": "Этот сейф пуст.",
|
||||
"message.anotherworld.safe_vault.owner_offline": "Владелец должен быть в сети, чтобы атаковать сейф.",
|
||||
"message.anotherworld.safe_vault.protected": "Сейф защищён ещё примерно %s минут(ы).",
|
||||
@@ -1695,6 +1697,7 @@ tasks.register("generateAnotherWorldResources") {
|
||||
"message.anotherworld.safe_vault.empty_before_break": "Empty the safe and wait for drilling to end before dismantling it.",
|
||||
"message.anotherworld.safe_vault.drill_own": "You cannot drill your own safe.",
|
||||
"message.anotherworld.safe_vault.inactive": "This safe is not active.",
|
||||
"message.anotherworld.safe_vault.raid_requires_vault": "You need an active safe of your own before raiding another one.",
|
||||
"message.anotherworld.safe_vault.empty": "This safe is empty.",
|
||||
"message.anotherworld.safe_vault.owner_offline": "The owner must be online before the safe can be raided.",
|
||||
"message.anotherworld.safe_vault.protected": "This safe remains protected for about %s minute(s).",
|
||||
@@ -1953,7 +1956,7 @@ tasks.register("verifyAnotherWorldCatalog") {
|
||||
"src/main/resources/assets/redstoner/textures/item/weather_gauge.png",
|
||||
"src/main/resources/data/redstoner/recipe/weather_gauge.json"
|
||||
]
|
||||
if (project.version.toString() != "0.0.0-alpha.55"
|
||||
if (project.version.toString() != "0.0.0-alpha.56"
|
||||
|| sulfurMigration.target?.modules?.anotherworld != "0.0.0-alpha.44"
|
||||
|| sulfurMigration.sulfur_caves?.sanctuary?.dripstone_cell_period != 2
|
||||
|| !javaText.contains('Identifier.parse("sanctuary:sulfur_caves")')
|
||||
@@ -3270,7 +3273,7 @@ tasks.register("verifyClimateBiomeMixer") {
|
||||
"pack/migrations/26.2.0-alpha.188-beta-climate-provinces-to-alpha.189.json"))
|
||||
def mixer = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/anotherworld/mixin/MultiNoiseBiomeSourceParameterListPresetMixin.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.55"
|
||||
if (project.version.toString() != "0.0.0-alpha.56"
|
||||
|| migration.source?.pack_version != "26.2.0-alpha.188"
|
||||
|| migration.target?.pack_version != "26.2.0-alpha.189"
|
||||
|| migration.target?.modules?.anotherworld != "0.0.0-alpha.48"
|
||||
|
||||
+11
-1
@@ -113,6 +113,12 @@ public final class VaultData extends SavedData {
|
||||
&& address.pos() == center.asLong() && address.owner().equals(ownerId));
|
||||
}
|
||||
|
||||
public boolean hasRegisteredVault(UUID owner) {
|
||||
if (owner == null) return false;
|
||||
String ownerId = owner.toString();
|
||||
return vaults.stream().anyMatch(address -> address.owner().equals(ownerId));
|
||||
}
|
||||
|
||||
public void unregisterNear(ServerLevel level, BlockPos member) {
|
||||
String dimension = dimension(level);
|
||||
boolean changed = vaults.removeIf(address -> {
|
||||
@@ -163,7 +169,7 @@ public final class VaultData extends SavedData {
|
||||
|
||||
/** Transfers the exact 20% raid share into the attacker's unlimited balances without item spam. */
|
||||
public long transferRaid(UUID owner, UUID attacker) {
|
||||
if (owner == null || attacker == null || owner.equals(attacker)) return 0L;
|
||||
if (!canReceiveRaidLoot(owner, attacker, hasRegisteredVault(attacker))) return 0L;
|
||||
Account source = accounts.get(owner);
|
||||
if (source == null) return 0L;
|
||||
Account target = accounts.computeIfAbsent(attacker, ignored -> new Account());
|
||||
@@ -180,6 +186,10 @@ public final class VaultData extends SavedData {
|
||||
return total;
|
||||
}
|
||||
|
||||
static boolean canReceiveRaidLoot(UUID owner, UUID attacker, boolean attackerHasRegisteredVault) {
|
||||
return owner != null && attacker != null && !owner.equals(attacker) && attackerHasRegisteredVault;
|
||||
}
|
||||
|
||||
public long protectionRemaining(UUID owner, long gameTime) {
|
||||
Account account = accounts.get(owner);
|
||||
return account == null ? 0L : Math.max(0L, account.protectedUntil - gameTime);
|
||||
|
||||
+11
@@ -48,6 +48,10 @@ public final class VaultDrillingService {
|
||||
attacker.sendSystemMessage(Component.translatable("message.anotherworld.safe_vault.inactive"));
|
||||
return false;
|
||||
}
|
||||
if (!data.hasRegisteredVault(attacker.getUUID())) {
|
||||
attacker.sendSystemMessage(Component.translatable("message.anotherworld.safe_vault.raid_requires_vault"));
|
||||
return false;
|
||||
}
|
||||
if (!data.hasContents(formed.owner())) {
|
||||
attacker.sendSystemMessage(Component.translatable("message.anotherworld.safe_vault.empty"));
|
||||
return false;
|
||||
@@ -104,6 +108,13 @@ public final class VaultDrillingService {
|
||||
attacker.stopUsingItem();
|
||||
return;
|
||||
}
|
||||
if (!VaultData.get(level.getServer()).hasRegisteredVault(attacker.getUUID())) {
|
||||
cancel(attacker, false);
|
||||
attacker.sendOverlayMessage(Component.translatable(
|
||||
"message.anotherworld.safe_vault.raid_requires_vault"));
|
||||
attacker.stopUsingItem();
|
||||
return;
|
||||
}
|
||||
|
||||
int elapsed = session.elapsedTicks() + 1;
|
||||
Session progressed = session.withElapsed(elapsed);
|
||||
|
||||
+10
@@ -1,5 +1,7 @@
|
||||
package fr.koka99cab.sanctuary26.anotherworld.vault;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class VaultRulesSmoke {
|
||||
private VaultRulesSmoke() {
|
||||
}
|
||||
@@ -55,6 +57,14 @@ public final class VaultRulesSmoke {
|
||||
require(VaultRules.raidLoot(Integer.MAX_VALUE) == 429_496_730L, "32-bit balance raid");
|
||||
require(VaultRules.raidLoot(Long.MAX_VALUE) == 1_844_674_407_370_955_162L,
|
||||
"64-bit balance raid without overflow");
|
||||
UUID owner = UUID.fromString("00000000-0000-0000-0000-000000000001");
|
||||
UUID attacker = UUID.fromString("00000000-0000-0000-0000-000000000002");
|
||||
require(!VaultData.canReceiveRaidLoot(owner, attacker, false),
|
||||
"an attacker without a registered vault cannot receive raid loot");
|
||||
require(VaultData.canReceiveRaidLoot(owner, attacker, true),
|
||||
"an attacker with a registered vault can receive raid loot");
|
||||
require(!VaultData.canReceiveRaidLoot(owner, owner, true),
|
||||
"a vault owner cannot receive loot from their own vault");
|
||||
require(VaultRules.saturatingAdd(Long.MAX_VALUE - 2L, 10L) == Long.MAX_VALUE,
|
||||
"balance saturation without overflow");
|
||||
for (long balance : new long[] {0L, Integer.MAX_VALUE, (long) Integer.MAX_VALUE + 1L, Long.MAX_VALUE}) {
|
||||
|
||||
+390
-222
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -16,19 +16,19 @@ ambiance_version=0.0.0-alpha.22
|
||||
ambiance_lifecycle=active
|
||||
redstoner_version=0.0.0-alpha.13
|
||||
redstoner_lifecycle=active
|
||||
anotherworld_version=0.0.0-alpha.55
|
||||
anotherworld_version=0.0.0-alpha.56
|
||||
anotherworld_lifecycle=active
|
||||
itsalive_version=0.0.0-alpha.37
|
||||
itsalive_version=0.0.0-alpha.38
|
||||
itsalive_lifecycle=active
|
||||
ouch_version=0.0.0-alpha.11
|
||||
ouch_lifecycle=active
|
||||
masterkey_version=0.0.0-alpha.9
|
||||
masterkey_lifecycle=active
|
||||
iliketomoveit_version=0.0.0-alpha.17
|
||||
iliketomoveit_version=0.0.0-alpha.18
|
||||
iliketomoveit_lifecycle=active
|
||||
onlyfun_version=0.0.0-alpha.26
|
||||
onlyfun_version=0.0.0-alpha.27
|
||||
onlyfun_lifecycle=active
|
||||
sanctuary_version=0.0.0-alpha.128
|
||||
sanctuary_version=0.0.0-alpha.129
|
||||
sanctuary_lifecycle=active
|
||||
pack_version=26.2.0-alpha.224
|
||||
pack_version=26.2.0-alpha.225
|
||||
maven_group=fr.koka99cab.sanctuary26
|
||||
|
||||
@@ -321,7 +321,7 @@ tasks.register("verifyPortals") {
|
||||
}
|
||||
def atlasCavernsMigration = new JsonSlurper().parse(rootProject.file(
|
||||
"pack/migrations/26.2.0-alpha.149-atlas-caverns-to-alpha.150.json"))
|
||||
if (project.version.toString() != "0.0.0-alpha.17"
|
||||
if (project.version.toString() != "0.0.0-alpha.18"
|
||||
|| atlasCavernsMigration.target?.pack_version != "26.2.0-alpha.150"
|
||||
|| atlasCavernsMigration.target?.modules?.iliketomoveit != "0.0.0-alpha.9"
|
||||
|| atlasCavernsMigration.portals?.active_routes != [
|
||||
@@ -357,7 +357,7 @@ tasks.register("verifySilverAircraft") {
|
||||
def manifest = new JsonSlurper().parse(file("src/main/resources/fabric.mod.json"))
|
||||
def migration = new JsonSlurper().parse(rootProject.file(
|
||||
"pack/migrations/26.2.0-alpha.184-silver-engineering-to-alpha.185.json"))
|
||||
if (project.version.toString() != "0.0.0-alpha.17"
|
||||
if (project.version.toString() != "0.0.0-alpha.18"
|
||||
|| recipe.pattern != ["PGP", "GCG", "PBP"]
|
||||
|| recipe.key != [B: "anotherworld:silver_block", C: "anotherworld:aircraft_cockpit",
|
||||
G: "anotherworld:silver_gear", P: "anotherworld:silver_plate"]
|
||||
@@ -386,7 +386,7 @@ tasks.register("verifyGrapplePrediction") {
|
||||
def payload = file("src/main/java/fr/koka99cab/sanctuary26/iliketomoveit/network/GrappleStatePayload.java").text
|
||||
def controller = file("src/main/java/fr/koka99cab/sanctuary26/iliketomoveit/grapple/GrappleController.java").text
|
||||
def prediction = file("src/main/java/fr/koka99cab/sanctuary26/iliketomoveit/client/grapple/ClientGrapplePrediction.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.17"
|
||||
if (project.version.toString() != "0.0.0-alpha.18"
|
||||
|| migration.target?.modules?.iliketomoveit != "0.0.0-alpha.13"
|
||||
|| migration.grappling?.owner_prediction_payload != "iliketomoveit:s2c/grapple_state"
|
||||
|| migration.compatibility?.iliketomoveit_source_network_protocol != 4
|
||||
@@ -418,7 +418,7 @@ tasks.register("verifyWaystoneDescendingRelease") {
|
||||
"src/main/java/fr/koka99cab/sanctuary26/iliketomoveit/waystone/WaystoneElevatorRules.java").text
|
||||
def teleportation = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/iliketomoveit/network/WaystoneTeleportation.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.17"
|
||||
if (project.version.toString() != "0.0.0-alpha.18"
|
||||
|| migration.target?.modules?.iliketomoveit != "0.0.0-alpha.14"
|
||||
|| migration.waystone_elevator?.undiscovered_downward_destination_allowed != true
|
||||
|| migration.waystone_elevator?.destination_discovered_after_arrival != true
|
||||
@@ -452,7 +452,7 @@ tasks.register("verifyAlphaPortal") {
|
||||
def locales = ["fr_fr", "en_us", "ru_ru"].collect {
|
||||
new JsonSlurper().parse(file("src/main/resources/assets/iliketomoveit/lang/${it}.json"))
|
||||
}
|
||||
if (project.version.toString() != "0.0.0-alpha.17"
|
||||
if (project.version.toString() != "0.0.0-alpha.18"
|
||||
|| route.schema_version != 2 || route.source_dimension != "minecraft:overworld"
|
||||
|| route.destination_dimension != "sanctuary:alpha"
|
||||
|| route.destination_resolver != "fixed"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "minecraft:block/purple_wool"
|
||||
"layer0": "iliketomoveit:item/magic_carpet"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 366 B |
@@ -565,7 +565,7 @@ tasks.register("verifyItsAlive") {
|
||||
def climateSource = file("src/main/java/fr/koka99cab/sanctuary26/itsalive/agriculture/ClimateCropForaging.java").text
|
||||
def pineappleSource = file("src/main/java/fr/koka99cab/sanctuary26/itsalive/agriculture/PineappleItem.java").text
|
||||
def worldgenSource = file("src/main/java/fr/koka99cab/sanctuary26/itsalive/agriculture/ItsAliveAgricultureWorldGeneration.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.37"
|
||||
if (project.version.toString() != "0.0.0-alpha.38"
|
||||
|| climateMigration.target?.pack_version != "26.2.0-alpha.157"
|
||||
|| climateMigration.target?.modules?.itsalive != "0.0.0-alpha.24"
|
||||
|| !climateSource.contains("PlayerBlockBreakEvents.AFTER.register")
|
||||
@@ -1322,7 +1322,7 @@ tasks.register("verifyCulinaryReliability") {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (project.version.toString() != "0.0.0-alpha.37"
|
||||
if (project.version.toString() != "0.0.0-alpha.38"
|
||||
|| migration.target?.modules?.itsalive != "0.0.0-alpha.28"
|
||||
|| migration.culinary_fixes?.food_press_contents_destroyed_on_break != true
|
||||
|| migration.culinary_fixes?.comparator_contract?.blocks?.size() != 6
|
||||
@@ -1360,7 +1360,7 @@ tasks.register("verifyCauldronBatching") {
|
||||
def recipe = file("src/main/java/fr/koka99cab/sanctuary26/itsalive/culinary/recipe/CulinaryProcessRecipe.java").text
|
||||
def cauldron = file("src/main/java/fr/koka99cab/sanctuary26/itsalive/culinary/block/entity/CookingCauldronBlockEntity.java").text
|
||||
def timed = file("src/main/java/fr/koka99cab/sanctuary26/itsalive/culinary/block/entity/AbstractTimedCulinaryBlockEntity.java").text
|
||||
if (project.version.toString() != "0.0.0-alpha.37"
|
||||
if (project.version.toString() != "0.0.0-alpha.38"
|
||||
|| migration.target?.modules?.itsalive != "0.0.0-alpha.29"
|
||||
|| migration.cauldron_batches?.logical_stack_limit != 64
|
||||
|| migration.cauldron_batches?.identity != "same_item_same_components"
|
||||
@@ -1398,7 +1398,7 @@ tasks.register("verifyFermentationBarrelAutomation") {
|
||||
def partialMatching = file("src/main/java/fr/koka99cab/sanctuary26/itsalive/culinary/recipe/CulinaryPartialMatching.java").text
|
||||
def blockstate = new JsonSlurper().parse(
|
||||
file("src/main/resources/assets/itsalive/blockstates/fermentation_barrel.json"))
|
||||
if (project.version.toString() != "0.0.0-alpha.37"
|
||||
if (project.version.toString() != "0.0.0-alpha.38"
|
||||
|| migration.source?.modules?.itsalive != "0.0.0-alpha.29"
|
||||
|| migration.target?.modules?.itsalive != "0.0.0-alpha.30"
|
||||
|| migration.fermentation_barrel?.maximum_units_per_input_slot != 1
|
||||
@@ -1446,7 +1446,7 @@ tasks.register("verifyCulinaryLecternRelease") {
|
||||
[(locale): new JsonSlurper().parse(file(
|
||||
"src/main/resources/assets/itsalive/lang/${locale}.json"))]
|
||||
}
|
||||
if (project.version.toString() != "0.0.0-alpha.37"
|
||||
if (project.version.toString() != "0.0.0-alpha.38"
|
||||
|| migration.target?.modules?.itsalive != "0.0.0-alpha.31"
|
||||
|| migration.culinary_lectern?.book_tag != "minecraft:lectern_books"
|
||||
|| migration.culinary_lectern?.owner_catalog_shared_while_placed != true
|
||||
@@ -1509,7 +1509,7 @@ tasks.register("verifyCanapliaForbiddenBook") {
|
||||
fr_fr: "Livre interdit",
|
||||
en_us: "Forbidden Book",
|
||||
ru_ru: "Запретная книга"]
|
||||
if (project.version.toString() != "0.0.0-alpha.37"
|
||||
if (project.version.toString() != "0.0.0-alpha.38"
|
||||
|| !pageItem.contains("CanapliaKnowledge.learnRandomUnknown(serverPlayer)")
|
||||
|| !knowledge.contains("unknownVariants(data.personal(player.getUUID()))")
|
||||
|| !knowledge.contains("data.personal(inscription.ownerId())")
|
||||
|
||||
+4
-2
@@ -34,7 +34,8 @@ public final class CanapliaHarvestService {
|
||||
|
||||
public static void onMatureHarvest(ServerLevel level, ServerPlayer player, BlockPos pos, BlockState state) {
|
||||
if (!state.is(CanapliaContent.HEMP_CROP) || !CanapliaContent.HEMP_CROP.isMaxAge(state)) return;
|
||||
CanapliaVariantKey parent = state.getValue(CanapliaHempCropBlock.VARIANT);
|
||||
CanapliaVariantKey parent = CanapliaVariantLifecycle.harvestedFromPlant(
|
||||
state.getValue(CanapliaHempCropBlock.VARIANT));
|
||||
CanapliaKnowledge.discover(player, parent, "harvest");
|
||||
ClimateCropForaging.CropClimate climate = ClimateCropForaging.climate(level.getBiome(pos));
|
||||
AmbianceLunarHarvestApi.Conditions lunar = AmbianceLunarHarvestApi.evaluate(level, pos);
|
||||
@@ -65,7 +66,8 @@ public final class CanapliaHarvestService {
|
||||
if (pos.equals(center)) continue;
|
||||
BlockState state = level.getBlockState(pos);
|
||||
if (state.is(CanapliaContent.HEMP_CROP) && CanapliaContent.HEMP_CROP.isMaxAge(state)) {
|
||||
variants.add(state.getValue(CanapliaHempCropBlock.VARIANT));
|
||||
variants.add(CanapliaVariantLifecycle.harvestedFromPlant(
|
||||
state.getValue(CanapliaHempCropBlock.VARIANT)));
|
||||
}
|
||||
}
|
||||
return Set.copyOf(variants);
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package fr.koka99cab.sanctuary26.itsalive.canaplia;
|
||||
|
||||
/** Stable variant hand-off across placement, growth, block-state persistence and harvest. */
|
||||
public final class CanapliaVariantLifecycle {
|
||||
private CanapliaVariantLifecycle() {
|
||||
}
|
||||
|
||||
public static CanapliaVariantKey plantedFromSeed(CanapliaVariantKey seedVariant) {
|
||||
return require(seedVariant);
|
||||
}
|
||||
|
||||
public static CanapliaVariantKey afterGrowth(CanapliaVariantKey currentVariant) {
|
||||
return require(currentVariant);
|
||||
}
|
||||
|
||||
public static CanapliaVariantKey upperFromLower(CanapliaVariantKey lowerVariant) {
|
||||
return require(lowerVariant);
|
||||
}
|
||||
|
||||
public static CanapliaVariantKey harvestedFromPlant(CanapliaVariantKey plantVariant) {
|
||||
return require(plantVariant);
|
||||
}
|
||||
|
||||
public static String persistedKey(CanapliaVariantKey variant) {
|
||||
return require(variant).key();
|
||||
}
|
||||
|
||||
public static CanapliaVariantKey fromPersistedKey(String key) {
|
||||
return CanapliaVariantKey.byKey(key).orElse(CanapliaVariantKey.COMMON);
|
||||
}
|
||||
|
||||
private static CanapliaVariantKey require(CanapliaVariantKey variant) {
|
||||
if (variant == null) throw new IllegalArgumentException("Canaplia variant cannot be null");
|
||||
return variant;
|
||||
}
|
||||
}
|
||||
+17
-6
@@ -2,6 +2,7 @@ package fr.koka99cab.sanctuary26.itsalive.canaplia.block;
|
||||
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaContent;
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaHarvestService;
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaVariantLifecycle;
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaVariantKey;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -43,19 +44,28 @@ public final class CanapliaHempCropBlock extends CropBlock {
|
||||
|
||||
@Override
|
||||
protected ItemStack getCloneItemStack(LevelReader level, BlockPos pos, BlockState state, boolean includeData) {
|
||||
return new ItemStack(CanapliaContent.family(state.getValue(VARIANT)).seeds());
|
||||
CanapliaVariantKey variant = CanapliaVariantLifecycle.harvestedFromPlant(state.getValue(VARIANT));
|
||||
return new ItemStack(CanapliaContent.family(variant).seeds());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
super.randomTick(state, level, pos, random);
|
||||
synchronizeUpper(level, pos, level.getBlockState(pos));
|
||||
if (level.getRawBrightness(pos, 0) < 9) return;
|
||||
int age = getAge(state);
|
||||
if (age >= getMaxAge()) return;
|
||||
float speed = getGrowthSpeed(this, level, pos);
|
||||
if (random.nextInt((int) (25.0F / speed) + 1) != 0) return;
|
||||
BlockState grown = state.setValue(AGE, age + 1).setValue(VARIANT,
|
||||
CanapliaVariantLifecycle.afterGrowth(state.getValue(VARIANT)));
|
||||
level.setBlock(pos, grown, Block.UPDATE_CLIENTS);
|
||||
synchronizeUpper(level, pos, grown);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void growCrops(Level level, BlockPos pos, BlockState state) {
|
||||
int age = Math.min(getMaxAge(), getAge(state) + getBonemealAgeIncrease(level));
|
||||
BlockState grown = state.setValue(AGE, age);
|
||||
BlockState grown = state.setValue(AGE, age).setValue(VARIANT,
|
||||
CanapliaVariantLifecycle.afterGrowth(state.getValue(VARIANT)));
|
||||
level.setBlock(pos, grown, Block.UPDATE_CLIENTS);
|
||||
synchronizeUpper(level, pos, grown);
|
||||
}
|
||||
@@ -78,7 +88,7 @@ public final class CanapliaHempCropBlock extends CropBlock {
|
||||
if (!(level instanceof ServerLevel serverLevel) || !(player instanceof ServerPlayer serverPlayer)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
CanapliaVariantKey variant = state.getValue(VARIANT);
|
||||
CanapliaVariantKey variant = CanapliaVariantLifecycle.harvestedFromPlant(state.getValue(VARIANT));
|
||||
Block.popResource(serverLevel, pos,
|
||||
new ItemStack(CanapliaContent.family(variant).bud(), 1 + serverLevel.getRandom().nextInt(3)));
|
||||
CanapliaHarvestService.onMatureHarvest(serverLevel, serverPlayer, pos, state);
|
||||
@@ -97,7 +107,8 @@ public final class CanapliaHempCropBlock extends CropBlock {
|
||||
if (getAge(state) >= UPPER_START_AGE) {
|
||||
if (aboveState.isAir() || aboveState.is(CanapliaContent.HEMP_TOP)) {
|
||||
level.setBlock(above, CanapliaContent.HEMP_TOP.defaultBlockState()
|
||||
.setValue(AGE, getAge(state)).setValue(VARIANT, state.getValue(VARIANT)), Block.UPDATE_CLIENTS);
|
||||
.setValue(AGE, getAge(state)).setValue(VARIANT,
|
||||
CanapliaVariantLifecycle.upperFromLower(state.getValue(VARIANT))), Block.UPDATE_CLIENTS);
|
||||
}
|
||||
} else if (aboveState.is(CanapliaContent.HEMP_TOP)) {
|
||||
level.removeBlock(above, false);
|
||||
|
||||
+4
-1
@@ -1,6 +1,7 @@
|
||||
package fr.koka99cab.sanctuary26.itsalive.canaplia.block;
|
||||
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaContent;
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaVariantLifecycle;
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaVariantKey;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -26,7 +27,9 @@ public final class CanapliaHempTopBlock extends CropBlock {
|
||||
|
||||
@Override
|
||||
protected ItemStack getCloneItemStack(LevelReader level, BlockPos pos, BlockState state, boolean includeData) {
|
||||
return new ItemStack(CanapliaContent.family(state.getValue(CanapliaHempCropBlock.VARIANT)).seeds());
|
||||
CanapliaVariantKey variant = CanapliaVariantLifecycle.harvestedFromPlant(
|
||||
state.getValue(CanapliaHempCropBlock.VARIANT));
|
||||
return new ItemStack(CanapliaContent.family(variant).seeds());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-3
@@ -1,5 +1,6 @@
|
||||
package fr.koka99cab.sanctuary26.itsalive.canaplia.item;
|
||||
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaVariantLifecycle;
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.CanapliaVariantKey;
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.block.CanapliaHempCropBlock;
|
||||
import fr.koka99cab.sanctuary26.itsalive.canaplia.block.CanapliaResinBlock;
|
||||
@@ -24,12 +25,17 @@ public final class CanapliaVariantBlockItem extends BlockItem {
|
||||
@Override
|
||||
protected BlockState getPlacementState(BlockPlaceContext context) {
|
||||
BlockState state = super.getPlacementState(context);
|
||||
if (state == null) return null;
|
||||
return applyVariant(state, variant);
|
||||
}
|
||||
|
||||
public static BlockState applyVariant(BlockState state, CanapliaVariantKey variant) {
|
||||
if (state == null || variant == null) return state;
|
||||
CanapliaVariantKey placed = CanapliaVariantLifecycle.plantedFromSeed(variant);
|
||||
if (state.hasProperty(CanapliaHempCropBlock.VARIANT)) {
|
||||
return state.setValue(CanapliaHempCropBlock.VARIANT, variant);
|
||||
return state.setValue(CanapliaHempCropBlock.VARIANT, placed);
|
||||
}
|
||||
if (state.hasProperty(CanapliaResinBlock.VARIANT)) {
|
||||
return state.setValue(CanapliaResinBlock.VARIANT, variant);
|
||||
return state.setValue(CanapliaResinBlock.VARIANT, placed);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
+16
@@ -61,6 +61,22 @@ public final class CanapliaGameplaySmoke {
|
||||
Set.of(), Temperature.COLD, Humidity.BALANCED, 12, false, false, true, true, false)).isEmpty(),
|
||||
"lunar harvest requires a sky dimension");
|
||||
|
||||
for (CanapliaVariantKey variant : CanapliaVariantKey.values()) {
|
||||
CanapliaVariantKey placed = CanapliaVariantLifecycle.plantedFromSeed(variant);
|
||||
assertEquals(variant, placed,
|
||||
"historical seed placement variant " + variant.key());
|
||||
CanapliaVariantKey grown = CanapliaVariantLifecycle.afterGrowth(placed);
|
||||
assertEquals(variant, grown,
|
||||
"natural growth variant " + variant.key());
|
||||
CanapliaVariantKey persisted = CanapliaVariantLifecycle.fromPersistedKey(
|
||||
CanapliaVariantLifecycle.persistedKey(grown));
|
||||
assertEquals(variant, persisted, "persisted variant " + variant.key());
|
||||
assertEquals(variant, CanapliaVariantLifecycle.upperFromLower(persisted),
|
||||
"upper crop variant " + variant.key());
|
||||
assertEquals(variant, CanapliaVariantLifecycle.harvestedFromPlant(persisted),
|
||||
"harvest variant " + variant.key());
|
||||
}
|
||||
|
||||
CanapliaEffectRules.Plan minimum = CanapliaEffectRules.plan(0, 1, 0, 0);
|
||||
assertEquals(CanapliaEffectRules.MIN_PRIMARY_TICKS, minimum.primaryTicks(), "minimum primary duration");
|
||||
assertTrue(!minimum.badTrip(), "nonzero bad-trip roll");
|
||||
|
||||
+3
-1
@@ -2,6 +2,7 @@ package fr.koka99cab.sanctuary26.onlyfun.loot;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.OnlyFunMod;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.registry.OnlyFunRegistries;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.api.entity.SanctuaryEntityPolicy;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -38,7 +39,8 @@ public final class OnlyFunLootboxDrops {
|
||||
}
|
||||
|
||||
private static void afterDeath(LivingEntity entity, DamageSource source) {
|
||||
if (!(entity instanceof Mob mob) || !(entity.level() instanceof ServerLevel level)) return;
|
||||
if (SanctuaryEntityPolicy.isTemporaryCompanion(entity)
|
||||
|| !(entity instanceof Mob mob) || !(entity.level() instanceof ServerLevel level)) return;
|
||||
ServerPlayer killer = playerKiller(entity, source);
|
||||
if (killer == null || killer.isSpectator()) return;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"java": ">=25",
|
||||
"fabric-api": ">=0.154.2+26.2",
|
||||
"iliketomoveit": ">=0.0.0-alpha.17",
|
||||
"sanctuary": ">=0.0.0-alpha.127"
|
||||
"sanctuary": ">=0.0.0-alpha.129"
|
||||
},
|
||||
"custom": {
|
||||
"sanctuary26": {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"status": "released",
|
||||
"source": {
|
||||
"pack_version": "26.2.0-alpha.224",
|
||||
"modules": {
|
||||
"anotherworld": "0.0.0-alpha.55",
|
||||
"itsalive": "0.0.0-alpha.37",
|
||||
"iliketomoveit": "0.0.0-alpha.17",
|
||||
"onlyfun": "0.0.0-alpha.26",
|
||||
"sanctuary": "0.0.0-alpha.128"
|
||||
}
|
||||
},
|
||||
"target": {
|
||||
"pack_version": "26.2.0-alpha.225",
|
||||
"modules": {
|
||||
"anotherworld": "0.0.0-alpha.56",
|
||||
"itsalive": "0.0.0-alpha.38",
|
||||
"iliketomoveit": "0.0.0-alpha.18",
|
||||
"onlyfun": "0.0.0-alpha.27",
|
||||
"sanctuary": "0.0.0-alpha.129"
|
||||
}
|
||||
},
|
||||
"fixed_issues": [105, 116, 123, 124],
|
||||
"safe_vault_raid": {
|
||||
"owner": "anotherworld",
|
||||
"attacker_requires_registered_vault": true,
|
||||
"validated_when_drilling_starts": true,
|
||||
"revalidated_during_drilling": true,
|
||||
"revalidated_before_balance_transfer": true
|
||||
},
|
||||
"temporary_companions": {
|
||||
"owner": "sanctuary",
|
||||
"public_policy_api": "SanctuaryEntityPolicy",
|
||||
"vanilla_loot_suppressed": true,
|
||||
"experience_suppressed": true,
|
||||
"quest_board_progress_suppressed": true,
|
||||
"community_request_progress_suppressed": true,
|
||||
"onlyfun_lootboxes_suppressed": true
|
||||
},
|
||||
"canaplia_variants": {
|
||||
"owner": "itsalive",
|
||||
"historical_seed_ids_preserved": 16,
|
||||
"placement_preserves_variant": true,
|
||||
"natural_growth_preserves_variant": true,
|
||||
"bonemeal_preserves_variant": true,
|
||||
"upper_crop_preserves_variant": true,
|
||||
"block_state_persistence_preserves_variant": true,
|
||||
"harvest_and_break_drops_preserve_variant": true,
|
||||
"mature_neighbors_preserve_variant": true,
|
||||
"tender_plus_olive_can_produce_gold": true
|
||||
},
|
||||
"magic_carpet_texture": {
|
||||
"owner": "iliketomoveit",
|
||||
"canonical_item": "iliketomoveit:magic_carpet",
|
||||
"legacy_item": "portals:notch_totem",
|
||||
"texture": "iliketomoveit:item/magic_carpet",
|
||||
"historical_totem_art_restored": true,
|
||||
"purple_wool_reference_removed": true,
|
||||
"platform_behavior_changed": false
|
||||
},
|
||||
"compatibility": {
|
||||
"existing_saves_supported": true,
|
||||
"new_world_required": false,
|
||||
"save_reset_required": false,
|
||||
"registry_ids_changed": false,
|
||||
"persistent_data_formats_changed": false,
|
||||
"network_protocols_changed": false,
|
||||
"client_server_update_required": true,
|
||||
"anotherworld_data_version": 5,
|
||||
"anotherworld_network_protocol": 2,
|
||||
"itsalive_data_version": 4,
|
||||
"itsalive_network_protocol": 4,
|
||||
"iliketomoveit_data_version": 4,
|
||||
"iliketomoveit_network_protocol": 5,
|
||||
"onlyfun_data_version": 2,
|
||||
"onlyfun_network_protocol": 3,
|
||||
"sanctuary_data_version": 13,
|
||||
"sanctuary_network_protocol": 23
|
||||
},
|
||||
"distribution": {
|
||||
"pack_release_updated": true,
|
||||
"packwiz_index_refreshed": true,
|
||||
"prism_modrinth_index_updated": true,
|
||||
"shared_client_server_mrpack": true
|
||||
}
|
||||
}
|
||||
+15
-15
@@ -14,15 +14,15 @@ hash = "41f4f4c88c71d0a23446d5014f7407d55ad477441847099cad24492b032335a0"
|
||||
|
||||
[[files]]
|
||||
file = "mods/ambiance-0.0.0-alpha.22.jar"
|
||||
hash = "20f1b31b773c7cea9c5cbe01161c2c7e541eaca714213752a8d6227bae85c9b9"
|
||||
hash = "d6c44f09ddc455ae0cec02a1568e07036f11a3b2d105659e9aa14289357ddb47"
|
||||
|
||||
[[files]]
|
||||
file = "mods/anotherworld-0.0.0-alpha.55.jar"
|
||||
hash = "2c01fb018cce49b8ef5ff6de1f2c4d0218243c23581333675c60dfe4ab48dfde"
|
||||
file = "mods/anotherworld-0.0.0-alpha.56.jar"
|
||||
hash = "ede131b654d26110af8c7e64238c1b5f99634c3e1307346953ad6ab622ccfedc"
|
||||
|
||||
[[files]]
|
||||
file = "mods/events-0.0.0-alpha.0.jar"
|
||||
hash = "5bc52d22c93203c6c3881cd9086db5a1166feb6b31a67af8e761c9c0d0b87f37"
|
||||
hash = "9ccc30733117ab9984df3d69b98ab82d731578a6c35116595c96f2362ea22b76"
|
||||
|
||||
[[files]]
|
||||
file = "mods/fabric-api.pw.toml"
|
||||
@@ -30,8 +30,8 @@ hash = "df032a6b051d0262cf24945c43e0e6b915e8cc1d62af62fe933932e7f35960c4"
|
||||
metafile = true
|
||||
|
||||
[[files]]
|
||||
file = "mods/iliketomoveit-0.0.0-alpha.17.jar"
|
||||
hash = "4aa2f5394f4e12301d13026c7b542660334f7c4e4f8057ea3d36dd04dfe461b6"
|
||||
file = "mods/iliketomoveit-0.0.0-alpha.18.jar"
|
||||
hash = "f0ccb5a3f411cb01bcf328a139a1f90e8357587c549778f98354d910d69b2616"
|
||||
|
||||
[[files]]
|
||||
file = "mods/iris.pw.toml"
|
||||
@@ -39,8 +39,8 @@ hash = "84a3cef87679c0d21dceb4641167dc0368b9a3593698f6a3632485e9b3a8d5da"
|
||||
metafile = true
|
||||
|
||||
[[files]]
|
||||
file = "mods/itsalive-0.0.0-alpha.37.jar"
|
||||
hash = "926803c09b2247f42363d1042346bff9798b982aaeafa96800711e259c48039d"
|
||||
file = "mods/itsalive-0.0.0-alpha.38.jar"
|
||||
hash = "6dab7b2dca11cc1cd9789ed66cc8403b849cb73227d965d979eeebcf5c201309"
|
||||
|
||||
[[files]]
|
||||
file = "mods/jei.pw.toml"
|
||||
@@ -49,23 +49,23 @@ metafile = true
|
||||
|
||||
[[files]]
|
||||
file = "mods/masterkey-0.0.0-alpha.9.jar"
|
||||
hash = "3069c9e784f425f59189c255638648ca51598c8626e340df9bf72a2c06f31753"
|
||||
hash = "ab0241386a0958505622487318c2e2e269e57e793ef873ed71dac8e0f420338b"
|
||||
|
||||
[[files]]
|
||||
file = "mods/onlyfun-0.0.0-alpha.26.jar"
|
||||
hash = "5d2915c761e87cd94f4d4075c85ebb71f021b59b92ced5eba6b5da8bb1413519"
|
||||
file = "mods/onlyfun-0.0.0-alpha.27.jar"
|
||||
hash = "15dbc6a9c3d4fef699c1f62f989c1d47e366cc781747c76640f5de668ea00510"
|
||||
|
||||
[[files]]
|
||||
file = "mods/ouch-0.0.0-alpha.11.jar"
|
||||
hash = "0e26e6e386781e2e258ef01eecad0129c00eec11783f182a34226213ea5f252e"
|
||||
hash = "5bc1e244309a8835ef1d7869225201179cbee12853fb2f2390bfd47f2fcc3c4a"
|
||||
|
||||
[[files]]
|
||||
file = "mods/redstoner-0.0.0-alpha.13.jar"
|
||||
hash = "2e9519b8f8092883616fdce4e5b4ab427b7875565423ea18a90adb40df751878"
|
||||
hash = "d913aac62b6e7152c5751ea0ddc820a98a0b74c3864f4c0bdbfc3b44eeaa3417"
|
||||
|
||||
[[files]]
|
||||
file = "mods/sanctuary-0.0.0-alpha.128.jar"
|
||||
hash = "9ac5e370509e0d0be77222aca227b698d449cfbcdde70ebc58fee2cec05e6a7e"
|
||||
file = "mods/sanctuary-0.0.0-alpha.129.jar"
|
||||
hash = "0b67c37680e5004b7f08ac9de32b501fc8f2025fe5a7649858948f14a5d8f5f5"
|
||||
|
||||
[[files]]
|
||||
file = "mods/simple-voice-chat.pw.toml"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
name = "Sanctuary 26.2"
|
||||
author = "KOKA99CAB"
|
||||
version = "26.2.0-alpha.224"
|
||||
version = "26.2.0-alpha.225"
|
||||
pack-format = "packwiz:1.1.0"
|
||||
|
||||
[index]
|
||||
file = "index.toml"
|
||||
hash-format = "sha256"
|
||||
hash = "0073565d9342eae7a811696a273094f157b9a6ec113c8282c3e92714c8fe921a"
|
||||
hash = "19d66dcd7dbbc8e1a9471dcb259a9c9aad727acbc8428f114a48c05c2fd1d8de"
|
||||
|
||||
[versions]
|
||||
fabric = "0.19.3"
|
||||
|
||||
@@ -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.224",
|
||||
"versionId": "26.2.0-alpha.225",
|
||||
"files": [
|
||||
{
|
||||
"path": "mods/fabric-api-0.158.0+26.2.jar",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"pack_version": "26.2.0-alpha.224",
|
||||
"pack_version": "26.2.0-alpha.225",
|
||||
"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.224.mrpack",
|
||||
"primary_file": "sanctuary-26.2.0-alpha.225.mrpack",
|
||||
"shared_client_server_pack": true,
|
||||
"update_owner": "packwiz-installer",
|
||||
"check_on_launch": true,
|
||||
|
||||
+20
-11
@@ -124,6 +124,14 @@ tasks.register("inventorySortSmoke", JavaExec) {
|
||||
mainClass = "fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryInventorySortSmoke"
|
||||
}
|
||||
|
||||
tasks.register("entityPolicySmoke", JavaExec) {
|
||||
group = "verification"
|
||||
description = "Checks that only tagged temporary companions are excluded from loot and quests."
|
||||
dependsOn tasks.named("testClasses")
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
mainClass = "fr.koka99cab.sanctuary26.sanctuary.api.entity.SanctuaryEntityPolicySmoke"
|
||||
}
|
||||
|
||||
tasks.register("alphaIndevGeneratorSmoke", JavaExec) {
|
||||
group = "verification"
|
||||
description = "Checks deterministic finite Indev islands, residences and safe portal arrivals."
|
||||
@@ -175,7 +183,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| !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")
|
||||
@@ -2891,7 +2899,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| 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
|
||||
@@ -3057,7 +3065,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| 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
|
||||
@@ -3095,7 +3103,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| 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]
|
||||
@@ -3126,7 +3134,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| migration.target?.pack_version != "26.2.0-alpha.195"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.111"
|
||||
|| migration.compatibility?.sanctuary_data_version != 11
|
||||
@@ -3157,7 +3165,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.112"
|
||||
|| migration.mailbox?.id != "sanctuary:delivery_box"
|
||||
|| migration.mailbox?.horizontal_facing != true
|
||||
@@ -3185,7 +3193,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| !transfer.contains("shouldAppendOverflow")
|
||||
|| !transfer.contains("isVanillaStorageRange")
|
||||
|| !transfer.contains("hotbarStart")
|
||||
@@ -3231,7 +3239,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.113"
|
||||
|| migration.quest_requests?.save_key != "quest_requests"
|
||||
|| migration.quest_requests?.data_version != 1
|
||||
@@ -3289,7 +3297,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| 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"
|
||||
@@ -3358,7 +3366,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| 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"
|
||||
@@ -3430,7 +3438,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.128"
|
||||
if (project.version.toString() != "0.0.0-alpha.129"
|
||||
|| migration.target?.modules?.sanctuary != "0.0.0-alpha.122"
|
||||
|| migration.alpha_world?.dimension != "sanctuary:alpha"
|
||||
|| migration.alpha_world?.legacy_dimension != "portals:alpha"
|
||||
@@ -3468,5 +3476,6 @@ tasks.named("check") {
|
||||
dependsOn tasks.named("alphaIndevGeneratorSmoke")
|
||||
dependsOn tasks.named("capeJoinSyncSmoke")
|
||||
dependsOn tasks.named("inventorySortSmoke")
|
||||
dependsOn tasks.named("entityPolicySmoke")
|
||||
dependsOn tasks.named("verifyAlphaIndev")
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.api.entity;
|
||||
|
||||
import java.util.Set;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
/** Public cross-module policy for Sanctuary-owned temporary gameplay entities. */
|
||||
public final class SanctuaryEntityPolicy {
|
||||
public static final String TEMPORARY_COMPANION_TAG = "sanctuary_companion";
|
||||
|
||||
private SanctuaryEntityPolicy() {
|
||||
}
|
||||
|
||||
/** Temporary companions must never yield loot, experience or quest progress. */
|
||||
public static boolean isTemporaryCompanion(Entity entity) {
|
||||
return entity != null && containsTemporaryCompanionTag(entity.entityTags());
|
||||
}
|
||||
|
||||
static boolean containsTemporaryCompanionTag(Set<String> tags) {
|
||||
return tags != null && tags.contains(TEMPORARY_COMPANION_TAG);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.gameplay;
|
||||
|
||||
import fr.koka99cab.sanctuary26.sanctuary.SanctuaryMod;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.api.entity.SanctuaryEntityPolicy;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -24,7 +25,7 @@ import net.minecraft.world.item.SpawnEggItem;
|
||||
|
||||
/** Server-owned lifecycle for the one companion equipped by each player. */
|
||||
public final class SanctuaryCompanions {
|
||||
public static final String ENTITY_TAG = "sanctuary_companion";
|
||||
public static final String ENTITY_TAG = SanctuaryEntityPolicy.TEMPORARY_COMPANION_TAG;
|
||||
private static final String OWNER_TAG_PREFIX = "sanctuary_companion_owner_";
|
||||
private static final Identifier MINI_SCALE = SanctuaryMod.id("companion/mini_scale");
|
||||
private static final double TELEPORT_DISTANCE_SQUARED = 144.0D;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.mixin;
|
||||
|
||||
import fr.koka99cab.sanctuary26.sanctuary.gameplay.SanctuaryCompanions;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.api.entity.SanctuaryEntityPolicy;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -12,7 +12,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
abstract class EntityCompanionMixin {
|
||||
@Inject(method = "shouldBeSaved", at = @At("HEAD"), cancellable = true)
|
||||
private void sanctuary$doNotSaveTemporaryCompanion(CallbackInfoReturnable<Boolean> cir) {
|
||||
if (((Entity) (Object) this).entityTags().contains(SanctuaryCompanions.ENTITY_TAG)) {
|
||||
if (SanctuaryEntityPolicy.isTemporaryCompanion((Entity) (Object) this)) {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.mixin;
|
||||
|
||||
import fr.koka99cab.sanctuary26.sanctuary.api.entity.SanctuaryEntityPolicy;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
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;
|
||||
|
||||
/** Temporary companions can be defeated, but never become a renewable loot or XP source. */
|
||||
@Mixin(LivingEntity.class)
|
||||
abstract class LivingEntityCompanionMixin {
|
||||
@Inject(method = "dropAllDeathLoot", at = @At("HEAD"), cancellable = true)
|
||||
private void sanctuary$suppressTemporaryCompanionLoot(ServerLevel level, DamageSource source,
|
||||
CallbackInfo ci) {
|
||||
if (SanctuaryEntityPolicy.isTemporaryCompanion((LivingEntity) (Object) this)) ci.cancel();
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.shop;
|
||||
|
||||
import fr.koka99cab.sanctuary26.sanctuary.api.entity.SanctuaryEntityPolicy;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.api.inventory.SanctuaryPlayerInventoryView;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.registry.SanctuaryRegistries;
|
||||
import java.time.Duration;
|
||||
@@ -199,7 +200,8 @@ public final class QuestBoardService {
|
||||
}
|
||||
|
||||
private static void afterDeath(LivingEntity entity, DamageSource source) {
|
||||
if (!(entity.level() instanceof ServerLevel level)) return;
|
||||
if (SanctuaryEntityPolicy.isTemporaryCompanion(entity)
|
||||
|| !(entity.level() instanceof ServerLevel level)) return;
|
||||
ServerPlayer player = killer(entity, source);
|
||||
if (player == null || player.isSpectator()) return;
|
||||
Identifier killed = BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType());
|
||||
|
||||
+3
-1
@@ -1,5 +1,6 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.shop;
|
||||
|
||||
import fr.koka99cab.sanctuary26.sanctuary.api.entity.SanctuaryEntityPolicy;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.api.inventory.SanctuaryPlayerInventoryView;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.network.QuestRequestAcceptPayload;
|
||||
import fr.koka99cab.sanctuary26.sanctuary.network.QuestRequestConfirmPayload;
|
||||
@@ -323,7 +324,8 @@ public final class QuestRequestService {
|
||||
}
|
||||
|
||||
private static void afterDeath(LivingEntity entity, DamageSource source) {
|
||||
if (!(entity.level() instanceof ServerLevel level)) return;
|
||||
if (SanctuaryEntityPolicy.isTemporaryCompanion(entity)
|
||||
|| !(entity.level() instanceof ServerLevel level)) return;
|
||||
ServerPlayer killer = killer(entity, source);
|
||||
if (killer == null || killer.isSpectator()) return;
|
||||
Identifier killedType = BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType());
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"InventoryMixin",
|
||||
"InventoryMenuMixin",
|
||||
"ItemTooltipMixin",
|
||||
"LivingEntityCompanionMixin",
|
||||
"PlayerOverflowMixin",
|
||||
"SlotMixin",
|
||||
"ServerGamePacketListenerMixin"
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package fr.koka99cab.sanctuary26.sanctuary.api.entity;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public final class SanctuaryEntityPolicySmoke {
|
||||
private SanctuaryEntityPolicySmoke() {
|
||||
}
|
||||
|
||||
public static void main(String[] arguments) {
|
||||
require(SanctuaryEntityPolicy.containsTemporaryCompanionTag(
|
||||
Set.of(SanctuaryEntityPolicy.TEMPORARY_COMPANION_TAG)),
|
||||
"temporary companion tag was not recognized");
|
||||
require(!SanctuaryEntityPolicy.containsTemporaryCompanionTag(Set.of("minecraft:mob")),
|
||||
"ordinary mob was classified as a temporary companion");
|
||||
require(!SanctuaryEntityPolicy.containsTemporaryCompanionTag(Set.of()),
|
||||
"untagged mob was classified as a temporary companion");
|
||||
}
|
||||
|
||||
private static void require(boolean value, String message) {
|
||||
if (!value) throw new AssertionError(message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user