Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0833d4e44f | ||
|
|
07b2eba5c5 | ||
|
|
122ff939aa |
+4
-1
@@ -8,7 +8,7 @@ Ce journal suit les **versions du modpack**. Lorsqu'un seul module change, sa ve
|
||||
|
||||
## Non distribué
|
||||
|
||||
Modules modifiés : **It's Alive `0.0.0-alpha.36`**, **Sanctuary `0.0.0-alpha.119`**, **Ambiance `0.0.0-alpha.15`** et **Only Fun `0.0.0-alpha.22`**. Le pack distribué reste en `26.2.0-alpha.210` jusqu’à la prochaine release.
|
||||
Modules modifiés : **It's Alive `0.0.0-alpha.36`**, **Sanctuary `0.0.0-alpha.119`**, **Ambiance `0.0.0-alpha.15`**, **Only Fun `0.0.0-alpha.23`** et **Ouch `0.0.0-alpha.10`**. Le pack distribué reste en `26.2.0-alpha.210` jusqu’à la prochaine release.
|
||||
|
||||
- remplace les pages noires du Livre interdit par l’affichage clair du livre vanilla ;
|
||||
- conserve les seize entrées, les découvertes personnelles, le partage sur pupitre, les IDs, les données v3 et le protocole réseau 4.
|
||||
@@ -20,6 +20,9 @@ Modules modifiés : **It's Alive `0.0.0-alpha.36`**, **Sanctuary `0.0.0-alpha.11
|
||||
- importe le cœur GPL historique d’Abjection dans Only Fun, sans onzième JAR : `/piss`, `/vomit`, `/shit` et `/smoke` retrouvent leurs durées, jets, impacts et messages ;
|
||||
- restaure `abjection:shit_ball` ainsi que le bloc boueux `abjection:shit_cube`, sa recette, son butin et ses ressources originales en FR/EN/RU ;
|
||||
- expose une API serveur unique pour les commandes et le futur raccourci, nettoie les actions temporaires à la mort, en spectateur ou à la déconnexion, et conserve les données v2 ainsi que le protocole réseau 2 d’Only Fun.
|
||||
- rend `abjection:shit_ball` lançable à la main et par dispenser grâce à une fabrique publique installée par Ouch, sans réenregistrer l’objet possédé par Only Fun ;
|
||||
- restaure le projectile historique de 0,25 bloc, sa consommation unique hors créatif, son rendu d’objet, son impact boueux et ses sept secondes de nausée ajoutées à la durée existante ;
|
||||
- déclare la dépendance sans cycle `ouch -> onlyfun` ; les données et protocoles réseau d’Ouch et d’Only Fun restent inchangés.
|
||||
|
||||
## `26.2.0-alpha.210`
|
||||
|
||||
|
||||
+12
-8
@@ -3439,7 +3439,7 @@ tasks.register("verifyProgressivePauseMenuRelease") {
|
||||
}
|
||||
if (rootProject.pack_version != "26.2.0-alpha.210"
|
||||
|| rootProject.sanctuary_version != "0.0.0-alpha.119"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.22"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.23"
|
||||
|| release.pack_version != rootProject.pack_version || index.versionId != rootProject.pack_version
|
||||
|| migration.source?.modules != [sanctuary: "0.0.0-alpha.108", onlyfun: "0.0.0-alpha.17"]
|
||||
|| migration.target?.modules != [sanctuary: "0.0.0-alpha.109", onlyfun: "0.0.0-alpha.17"]
|
||||
@@ -3606,6 +3606,7 @@ project(":ouch") {
|
||||
dependencies {
|
||||
implementation project(":anotherworld")
|
||||
implementation project(":ambiance")
|
||||
implementation project(":onlyfun")
|
||||
implementation project(":sanctuary")
|
||||
}
|
||||
}
|
||||
@@ -3646,7 +3647,7 @@ project(":onlyfun") {
|
||||
|
||||
tasks.register("verifyOnlyFun") {
|
||||
group = "verification"
|
||||
description = "Checks Only Fun alpha.21, including the historical Abjection server actions."
|
||||
description = "Checks Only Fun alpha.22, including its public Abjection projectile contract."
|
||||
inputs.files(fileTree("src/main/java"))
|
||||
inputs.files(fileTree("src/main/resources"))
|
||||
inputs.file(file("art/shop/shop_catalog_editable.csv"))
|
||||
@@ -3740,7 +3741,7 @@ project(":onlyfun") {
|
||||
def shitCubeRecipe = new JsonSlurper().parse(file(
|
||||
"src/main/resources/data/abjection/recipe/shit_cube.json"))
|
||||
if (manifest.custom.sanctuary26.data_version != 2
|
||||
|| project.version.toString() != "0.0.0-alpha.22"
|
||||
|| project.version.toString() != "0.0.0-alpha.23"
|
||||
|| manifest.depends?.sanctuary != "*"
|
||||
|| manifest.provides != ["abjection"]
|
||||
|| manifest.custom.sanctuary26.network_protocol != 2
|
||||
@@ -3754,6 +3755,9 @@ project(":onlyfun") {
|
||||
|| !javaText.contains('action == Action.SHIT && !restarted')
|
||||
|| !javaText.contains('new ItemStack(AbjectionRegistries.SHIT_BALL)')
|
||||
|| !javaText.contains('BlockBehaviour.Properties.ofFullCopy(Blocks.MUD)')
|
||||
|| !javaText.contains('class ShitBallItem extends Item implements ProjectileItem')
|
||||
|| !javaText.contains('public static void registerFactory(Factory implementation)')
|
||||
|| !javaText.contains('return AbjectionProjectileApi.create(level, position, stack, direction)')
|
||||
|| shitCubeTexture == null || shitCubeTexture.width != 16 || shitCubeTexture.height != 16
|
||||
|| shitBallTexture == null || shitBallTexture.width != 16 || shitBallTexture.height != 16
|
||||
|| shitCubeRecipe.key?.get("#") != "abjection:shit_ball"
|
||||
@@ -4181,7 +4185,7 @@ tasks.register("verifyShopPricingLayoutRelease") {
|
||||
def release = new JsonSlurper().parse(file("pack/release.json"))
|
||||
def index = new JsonSlurper().parse(file("pack/prism/modrinth.index.json"))
|
||||
if (rootProject.pack_version != "26.2.0-alpha.210"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.22"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.23"
|
||||
|| rootProject.sanctuary_version != "0.0.0-alpha.119"
|
||||
|| release.pack_version != rootProject.pack_version || index.versionId != rootProject.pack_version
|
||||
|| migration.source?.pack_version != "26.2.0-alpha.192"
|
||||
@@ -4275,7 +4279,7 @@ tasks.register("verifyShopCsvBossGatesRelease") {
|
||||
def catalog = file("onlyfun/src/main/java/fr/koka99cab/sanctuary26/onlyfun/shop/OnlyFunShopCatalog.java").text
|
||||
def offerCatalog = file("sanctuary/src/main/java/fr/koka99cab/sanctuary26/sanctuary/shop/ShopOfferCatalog.java").text
|
||||
if (rootProject.pack_version != "26.2.0-alpha.210"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.22"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.23"
|
||||
|| rootProject.sanctuary_version != "0.0.0-alpha.119"
|
||||
|| release.pack_version != rootProject.pack_version || index.versionId != rootProject.pack_version
|
||||
|| migration.source?.pack_version != "26.2.0-alpha.194"
|
||||
@@ -4523,7 +4527,7 @@ tasks.register("verifyWeatherTntAmbianceRelease") {
|
||||
def rootBuild = file("build.gradle").text
|
||||
if (rootProject.pack_version != "26.2.0-alpha.210"
|
||||
|| rootProject.ambiance_version != "0.0.0-alpha.15"
|
||||
|| rootProject.ouch_version != "0.0.0-alpha.9"
|
||||
|| rootProject.ouch_version != "0.0.0-alpha.10"
|
||||
|| migration.source?.pack_version != "26.2.0-alpha.199"
|
||||
|| migration.source?.modules != [ambiance: "0.0.0-alpha.11", ouch: "0.0.0-alpha.8"]
|
||||
|| migration.target?.pack_version != "26.2.0-alpha.200"
|
||||
@@ -5544,7 +5548,7 @@ tasks.register("verifyCanapliaAlpha209Release") {
|
||||
if (rootProject.pack_version != "26.2.0-alpha.210"
|
||||
|| rootProject.ambiance_version != "0.0.0-alpha.15"
|
||||
|| rootProject.itsalive_version != "0.0.0-alpha.36"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.22"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.23"
|
||||
|| migration.source?.pack_version != "26.2.0-alpha.207"
|
||||
|| migration.source?.modules != expectedSource
|
||||
|| migration.target?.pack_version != "26.2.0-alpha.208"
|
||||
@@ -5652,7 +5656,7 @@ tasks.register("verifyAlpha210Release") {
|
||||
}
|
||||
if (rootProject.pack_version != "26.2.0-alpha.210"
|
||||
|| rootProject.sanctuary_version != "0.0.0-alpha.119"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.22"
|
||||
|| rootProject.onlyfun_version != "0.0.0-alpha.23"
|
||||
|| rootProject.iliketomoveit_version != expectedTarget.iliketomoveit
|
||||
|| rootProject.itsalive_version != "0.0.0-alpha.36"
|
||||
|| migration.source?.pack_version != "26.2.0-alpha.209"
|
||||
|
||||
+2
-2
@@ -20,13 +20,13 @@ anotherworld_version=0.0.0-alpha.55
|
||||
anotherworld_lifecycle=active
|
||||
itsalive_version=0.0.0-alpha.36
|
||||
itsalive_lifecycle=active
|
||||
ouch_version=0.0.0-alpha.9
|
||||
ouch_version=0.0.0-alpha.10
|
||||
ouch_lifecycle=active
|
||||
masterkey_version=0.0.0-alpha.8
|
||||
masterkey_lifecycle=active
|
||||
iliketomoveit_version=0.0.0-alpha.15
|
||||
iliketomoveit_lifecycle=active
|
||||
onlyfun_version=0.0.0-alpha.22
|
||||
onlyfun_version=0.0.0-alpha.23
|
||||
onlyfun_lifecycle=active
|
||||
sanctuary_version=0.0.0-alpha.119
|
||||
sanctuary_lifecycle=active
|
||||
|
||||
@@ -20,3 +20,11 @@ ou à la déconnexion.
|
||||
Le bloc `abjection:shit_cube` reprend les propriétés de la boue, l’apparence 16×16, la recette
|
||||
neuf boules, le butin et l’outil pelle historiques. Les textes sont disponibles en français,
|
||||
anglais et russe. Only Fun reste en données v2 et protocole réseau 2 pour ABJ-01.
|
||||
|
||||
## ABJ-02 — projectile Ouch
|
||||
|
||||
Only Fun expose l’objet et le point d’installation `AbjectionProjectileApi`. Ouch, qui dépend
|
||||
explicitement d’Only Fun sans cycle inverse, enregistre le type d’entité sous le même ID historique
|
||||
`abjection:shit_ball` et installe la fabrique commune au lancer manuel et au dispenser. Le
|
||||
projectile mesure 0,25 bloc, consomme une unité hors créatif, joue le son de boue à l’impact et
|
||||
ajoute sept secondes à la nausée déjà présente sans dupliquer d’objet.
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ public final class AbjectionRegistries {
|
||||
public static final Block SHIT_CUBE = registerBlock("shit_cube", Block::new,
|
||||
BlockBehaviour.Properties.ofFullCopy(Blocks.MUD));
|
||||
public static final Item SHIT_BALL = registerItem("shit_ball",
|
||||
properties -> new Item(properties.stacksTo(16).useItemDescriptionPrefix()));
|
||||
properties -> new ShitBallItem(properties.stacksTo(16).useItemDescriptionPrefix()));
|
||||
public static final Item SHIT_CUBE_ITEM = registerBlockItem("shit_cube", SHIT_CUBE);
|
||||
|
||||
private AbjectionRegistries() {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.abjection;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.api.abjection.AbjectionProjectileApi;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Position;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.ProjectileItem;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
/** Historical item identity with a projectile contract implemented by the owning Ouch module. */
|
||||
public final class ShitBallItem extends Item implements ProjectileItem {
|
||||
public ShitBallItem(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Projectile asProjectile(Level level, Position position, ItemStack stack, Direction direction) {
|
||||
return AbjectionProjectileApi.create(level, position, stack, direction);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.api.abjection;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.abjection.AbjectionRegistries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/** Minimal public object contract consumed by Ouch's Abjection projectile implementation. */
|
||||
public final class AbjectionItems {
|
||||
private AbjectionItems() {
|
||||
}
|
||||
|
||||
public static Item shitBall() {
|
||||
return AbjectionRegistries.SHIT_BALL;
|
||||
}
|
||||
|
||||
public static Identifier shitBallId() {
|
||||
return Identifier.fromNamespaceAndPath("abjection", "shit_ball");
|
||||
}
|
||||
|
||||
public static boolean isShitBall(ItemStack stack) {
|
||||
return stack.is(AbjectionRegistries.SHIT_BALL);
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.api.abjection;
|
||||
|
||||
import java.util.Objects;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Position;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
/**
|
||||
* Owner-neutral projectile factory contract: Only Fun owns the item, while Ouch installs its
|
||||
* combat implementation without creating a reverse dependency.
|
||||
*/
|
||||
public final class AbjectionProjectileApi {
|
||||
@FunctionalInterface
|
||||
public interface Factory {
|
||||
Projectile create(Level level, Position position, ItemStack stack, Direction direction);
|
||||
}
|
||||
|
||||
private static Factory factory;
|
||||
|
||||
private AbjectionProjectileApi() {
|
||||
}
|
||||
|
||||
public static void registerFactory(Factory implementation) {
|
||||
Objects.requireNonNull(implementation, "implementation");
|
||||
if (factory != null) throw new IllegalStateException("Abjection projectile factory already registered");
|
||||
factory = implementation;
|
||||
}
|
||||
|
||||
public static Projectile create(Level level, Position position, ItemStack stack, Direction direction) {
|
||||
if (factory == null) throw new IllegalStateException("Ouch has not registered the Abjection projectile factory");
|
||||
return factory.create(level, position, stack, direction);
|
||||
}
|
||||
|
||||
public static boolean isReady() {
|
||||
return factory != null;
|
||||
}
|
||||
}
|
||||
+45
-3
@@ -12,9 +12,10 @@ def miningRifleTextureHash = "4e4295c5c8a8f71d03307809448ff64363eec2d2aefdc538c4
|
||||
|
||||
tasks.register("verifyOuch") {
|
||||
group = "verification"
|
||||
description = "Checks Ouch alpha.9, including the Ambiance-backed Weather TNT and migrated Gunz arsenal."
|
||||
description = "Checks Ouch alpha.10, including the shared Abjection projectile implementation."
|
||||
inputs.files(fileTree("src/main/java"))
|
||||
inputs.files(fileTree("src/main/resources"))
|
||||
inputs.files(rootProject.fileTree("onlyfun/src/main/java"))
|
||||
inputs.file(rootProject.file("pack/migrations/26.1.2-gunz-to-26.2.0-alpha.41.json"))
|
||||
inputs.file(rootProject.file("pack/migrations/26.2.0-alpha.99-canon-weather-ownership-to-alpha.100.json"))
|
||||
inputs.file(rootProject.file("pack/migrations/26.2.0-alpha.184-silver-engineering-to-alpha.185.json"))
|
||||
@@ -25,6 +26,34 @@ tasks.register("verifyOuch") {
|
||||
def weatherRegistry = file("src/main/java/fr/koka99cab/sanctuary26/ouch/registry/OuchWeatherTnt.java").text
|
||||
def weatherEntity = file("src/main/java/fr/koka99cab/sanctuary26/ouch/entity/WeatherTntEntity.java").text
|
||||
def manifest = new JsonSlurper().parse(file("src/main/resources/fabric.mod.json"))
|
||||
def abjectionRegistry = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/ouch/registry/OuchAbjectionProjectiles.java").text
|
||||
def abjectionEntity = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/ouch/entity/ShitBallEntity.java").text
|
||||
def abjectionClient = file(
|
||||
"src/main/java/fr/koka99cab/sanctuary26/ouch/client/OuchClient.java").text
|
||||
def onlyFunProjectileApi = rootProject.file(
|
||||
"onlyfun/src/main/java/fr/koka99cab/sanctuary26/onlyfun/api/abjection/AbjectionProjectileApi.java").text
|
||||
if (manifest.depends?.onlyfun != ">=0.0.0-alpha.23"
|
||||
|| manifest.custom?.sanctuary26?.data_version != 1
|
||||
|| manifest.custom?.sanctuary26?.network_protocol != 1
|
||||
|| !rootProject.file("build.gradle").text.contains('implementation project(":onlyfun")')
|
||||
|| !onlyFunProjectileApi.contains("public interface Factory")
|
||||
|| !abjectionRegistry.contains('AbjectionProjectileApi.registerFactory(')
|
||||
|| !abjectionRegistry.contains('AbjectionItems.shitBallId()')
|
||||
|| abjectionRegistry.contains('AbjectionRegistries')
|
||||
|| !abjectionRegistry.contains('DispenserBlock.registerProjectileBehavior(AbjectionItems.shitBall())')
|
||||
|| !abjectionRegistry.contains('UseItemCallback.EVENT.register')
|
||||
|| !abjectionRegistry.contains('stack.consume(1, player)')
|
||||
|| !abjectionRegistry.contains('Projectile.spawnProjectileFromRotation(')
|
||||
|| !abjectionRegistry.contains('.sized(0.25F, 0.25F)')
|
||||
|| !abjectionEntity.contains('NAUSEA_DURATION_TICKS = 7 * 20')
|
||||
|| !abjectionEntity.contains('current == null ? 0 : current.getDuration()')
|
||||
|| !abjectionEntity.contains('SoundEvents.MUD_PLACE')
|
||||
|| !abjectionEntity.contains('discard()')
|
||||
|| !abjectionClient.contains('new ThrownItemRenderer<>(context)')) {
|
||||
throw new GradleException("Ouch alpha.10 Abjection projectile contract is incomplete")
|
||||
}
|
||||
if (!itemRegistry.contains("MIGRATED_WEAPONS = List.of(CREEPERLOCK, MITRAILLETTE, EXPULSEUR, ASPIRATEUR)")
|
||||
|| !itemRegistry.contains("WEAPONS = List.of(CREEPERLOCK, MITRAILLETTE, EXPULSEUR, ASPIRATEUR, MINING_RIFLE, BETA_BOW)")
|
||||
|| !creativeTab.contains('OuchMod.id("arsenal")')
|
||||
@@ -89,7 +118,7 @@ tasks.register("verifyOuch") {
|
||||
def creeperlockRecipe = new JsonSlurper().parse(file("src/main/resources/data/ouch/recipe/creeperlock.json"))
|
||||
def silverEngineeringMigration = 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.9"
|
||||
if (project.version.toString() != "0.0.0-alpha.10"
|
||||
|| mitrailletteRecipe.pattern != ["RRP", "IGI", " B "]
|
||||
|| mitrailletteRecipe.key != [B: "minecraft:stone_button", G: "minecraft:redstone",
|
||||
I: "anotherworld:silver_ingot", P: "anotherworld:silver_plate", R: "anotherworld:silver_rod"]
|
||||
@@ -317,10 +346,23 @@ tasks.register("verifyOuch") {
|
||||
throw new GradleException("Runtime Ouch resources must not re-register the legacy gunz namespace: ${forbiddenNamespaceReferences}")
|
||||
}
|
||||
|
||||
logger.lifecycle("Ouch verified: 6 enchantable items, 4 custom enchantments, debuffed bases, shared headshots, 6 recipes and FR/EN/RU translations.")
|
||||
logger.lifecycle("Ouch verified: arsenal, Weather TNT and shared Abjection projectile contract.")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("test") {
|
||||
failOnNoDiscoveredTests = false
|
||||
}
|
||||
|
||||
tasks.register("shitBallContractSmoke", JavaExec) {
|
||||
group = "verification"
|
||||
description = "Checks additive and overflow-safe Shit Ball nausea."
|
||||
dependsOn tasks.named("testClasses")
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
mainClass = "fr.koka99cab.sanctuary26.ouch.entity.ShitBallContractSmoke"
|
||||
}
|
||||
|
||||
tasks.named("check") {
|
||||
dependsOn tasks.named("shitBallContractSmoke")
|
||||
dependsOn tasks.named("verifyOuch")
|
||||
}
|
||||
|
||||
@@ -2,16 +2,20 @@ package fr.koka99cab.sanctuary26.ouch.client;
|
||||
|
||||
import fr.koka99cab.sanctuary26.ouch.OuchMod;
|
||||
import fr.koka99cab.sanctuary26.ouch.entity.WeatherTntEntity;
|
||||
import fr.koka99cab.sanctuary26.ouch.registry.OuchAbjectionProjectiles;
|
||||
import fr.koka99cab.sanctuary26.ouch.registry.OuchWeatherTnt;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.TntRenderer;
|
||||
import net.minecraft.client.renderer.entity.ThrownItemRenderer;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
|
||||
public final class OuchClient implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
EntityRendererRegistry.register(OuchAbjectionProjectiles.ENTITY,
|
||||
context -> new ThrownItemRenderer<>(context));
|
||||
registerWeatherRenderer(OuchWeatherTnt.ENTITY);
|
||||
registerWeatherRenderer(OuchWeatherTnt.REDSTONER_COMPAT_ENTITY);
|
||||
registerWeatherRenderer(OuchWeatherTnt.LEGACY_ENTITY);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package fr.koka99cab.sanctuary26.ouch.entity;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.api.abjection.AbjectionItems;
|
||||
import fr.koka99cab.sanctuary26.ouch.registry.OuchAbjectionProjectiles;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.projectile.throwableitemprojectile.ThrowableItemProjectile;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
||||
/** Server-authoritative historical Abjection projectile, owned by Ouch. */
|
||||
public final class ShitBallEntity extends ThrowableItemProjectile {
|
||||
public static final int NAUSEA_DURATION_TICKS = 7 * 20;
|
||||
|
||||
public ShitBallEntity(EntityType<? extends ShitBallEntity> type, Level level) {
|
||||
super(type, level);
|
||||
}
|
||||
|
||||
public ShitBallEntity(Level level, LivingEntity owner, ItemStack stack) {
|
||||
super(OuchAbjectionProjectiles.ENTITY, owner, level, stack.copyWithCount(1));
|
||||
}
|
||||
|
||||
public ShitBallEntity(Level level, double x, double y, double z, ItemStack stack) {
|
||||
super(OuchAbjectionProjectiles.ENTITY, x, y, z, level, stack.copyWithCount(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Item getDefaultItem() {
|
||||
return AbjectionItems.shitBall();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHitEntity(EntityHitResult hit) {
|
||||
super.onHitEntity(hit);
|
||||
if (!(level() instanceof ServerLevel) || !(hit.getEntity() instanceof LivingEntity target)) return;
|
||||
MobEffectInstance current = target.getEffect(MobEffects.NAUSEA);
|
||||
int duration = extendedNauseaDuration(current == null ? 0 : current.getDuration());
|
||||
target.addEffect(new MobEffectInstance(MobEffects.NAUSEA, duration), getOwner());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHit(HitResult hit) {
|
||||
super.onHit(hit);
|
||||
if (level() instanceof ServerLevel level) {
|
||||
level.playSound(null, getX(), getY(), getZ(), SoundEvents.MUD_PLACE,
|
||||
SoundSource.PLAYERS, 1.0F, 0.75F);
|
||||
discard();
|
||||
}
|
||||
}
|
||||
|
||||
static int extendedNauseaDuration(int currentDuration) {
|
||||
return (int) Math.min(Integer.MAX_VALUE, Math.max(0L, currentDuration) + NAUSEA_DURATION_TICKS);
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package fr.koka99cab.sanctuary26.ouch.registry;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.api.abjection.AbjectionItems;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.api.abjection.AbjectionProjectileApi;
|
||||
import fr.koka99cab.sanctuary26.ouch.entity.ShitBallEntity;
|
||||
import net.fabricmc.fabric.api.event.player.UseItemCallback;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Position;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
|
||||
/** Shared manual/dispenser creation path for {@code abjection:shit_ball}. */
|
||||
public final class OuchAbjectionProjectiles {
|
||||
private static final float SHOOT_POWER = 1.5F;
|
||||
private static final ResourceKey<EntityType<?>> KEY = ResourceKey.create(
|
||||
Registries.ENTITY_TYPE, AbjectionItems.shitBallId());
|
||||
public static final EntityType<ShitBallEntity> ENTITY = Registry.register(
|
||||
BuiltInRegistries.ENTITY_TYPE, KEY,
|
||||
EntityType.Builder.<ShitBallEntity>of(ShitBallEntity::new, MobCategory.MISC)
|
||||
.sized(0.25F, 0.25F).clientTrackingRange(4).updateInterval(10).build(KEY));
|
||||
private static boolean initialized;
|
||||
|
||||
private OuchAbjectionProjectiles() {
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
AbjectionProjectileApi.registerFactory(OuchAbjectionProjectiles::createFromDispenser);
|
||||
DispenserBlock.registerProjectileBehavior(AbjectionItems.shitBall());
|
||||
UseItemCallback.EVENT.register((player, level, hand) -> {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
if (!AbjectionItems.isShitBall(stack) || player.isSpectator()) return InteractionResult.PASS;
|
||||
if (level.isClientSide()) return InteractionResult.SUCCESS;
|
||||
if (!(level instanceof ServerLevel serverLevel)) return InteractionResult.FAIL;
|
||||
serverLevel.playSound(null, player.getX(), player.getY(), player.getZ(),
|
||||
SoundEvents.SNOWBALL_THROW, SoundSource.NEUTRAL, 0.5F,
|
||||
0.4F / (serverLevel.getRandom().nextFloat() * 0.4F + 0.8F));
|
||||
Projectile.spawnProjectileFromRotation(OuchAbjectionProjectiles::createFromPlayer,
|
||||
serverLevel, stack, player, 0.0F, SHOOT_POWER, 1.0F);
|
||||
player.awardStat(Stats.ITEM_USED.get(AbjectionItems.shitBall()));
|
||||
stack.consume(1, player);
|
||||
return InteractionResult.SUCCESS_SERVER;
|
||||
});
|
||||
}
|
||||
|
||||
public static ShitBallEntity createFromPlayer(ServerLevel level, LivingEntity owner, ItemStack stack) {
|
||||
return new ShitBallEntity(level, owner, stack);
|
||||
}
|
||||
|
||||
public static Projectile createFromDispenser(Level level, Position position, ItemStack stack,
|
||||
Direction direction) {
|
||||
return new ShitBallEntity(level, position.x(), position.y(), position.z(), stack);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public final class OuchRegistries {
|
||||
initialized = true;
|
||||
OuchItems.initialize();
|
||||
OuchWeatherTnt.initialize();
|
||||
OuchAbjectionProjectiles.initialize();
|
||||
OuchCreativeTab.initialize();
|
||||
OuchMod.LOGGER.debug("[{}] Weapon registry hub initialized.", OuchMod.DISPLAY_NAME);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"fabric-api": ">=0.154.2+26.2",
|
||||
"anotherworld": ">=0.0.0-alpha.46",
|
||||
"ambiance": ">=0.0.0-alpha.12",
|
||||
"onlyfun": ">=0.0.0-alpha.23",
|
||||
"sanctuary": ">=0.0.0-alpha.110"
|
||||
},
|
||||
"custom": {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package fr.koka99cab.sanctuary26.ouch.entity;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
|
||||
/** Executable regression test for additive and overflow-safe nausea duration. */
|
||||
public final class ShitBallContractSmoke {
|
||||
private ShitBallContractSmoke() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
require(ShitBallEntity.NAUSEA_DURATION_TICKS == 140, "historical duration changed");
|
||||
require(ShitBallEntity.extendedNauseaDuration(0) == 140, "first hit duration is wrong");
|
||||
require(ShitBallEntity.extendedNauseaDuration(60) == 200,
|
||||
"a new impact did not add seven seconds");
|
||||
require(ShitBallEntity.extendedNauseaDuration(-10) == 140,
|
||||
"negative input was not normalized");
|
||||
require(ShitBallEntity.extendedNauseaDuration(Integer.MAX_VALUE) == Integer.MAX_VALUE,
|
||||
"duration overflow was not saturated");
|
||||
}
|
||||
|
||||
private static void require(boolean condition, String message) {
|
||||
if (!condition) throw new AssertionError(message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user