feat(onlyfun): feu d'artifice Nourra au visage du skin #174
+32
-2
@@ -3916,11 +3916,11 @@ project(":onlyfun") {
|
||||
"src/main/java/fr/koka99cab/sanctuary26/onlyfun/world/CobbleDungeonStructure.java").text
|
||||
def dungeonStructureSet = new JsonSlurper().parse(file(
|
||||
"src/main/resources/data/onlyfun/worldgen/structure_set/cobble_dungeons.json"))
|
||||
def expectedItems = ["normal_lootbox", "cape_lootbox", "spawn_egg_lootbox", "anomaly_lootbox", "lucky_block"]
|
||||
def expectedItems = ["normal_lootbox", "cape_lootbox", "spawn_egg_lootbox", "anomaly_lootbox", "lucky_block", "nourra_firework"]
|
||||
def missingModels = expectedItems.findAll { id ->
|
||||
!file("src/main/resources/assets/onlyfun/items/${id}.json").exists()
|
||||
}
|
||||
def textureIds = ["normal_lootbox", "cape_lootbox", "spawn_egg_lootbox", "anomaly_lootbox"]
|
||||
def textureIds = ["normal_lootbox", "cape_lootbox", "spawn_egg_lootbox", "anomaly_lootbox", "nourra_firework"]
|
||||
def invalidTextures = textureIds.findAll { id ->
|
||||
def texture = file("src/main/resources/assets/onlyfun/textures/item/${id}.png")
|
||||
if (!texture.isFile()) return true
|
||||
@@ -4138,6 +4138,36 @@ project(":onlyfun") {
|
||||
|| !shopCsvText.contains("TRUE,ITEM,iliketomoveit:magic_cloud,1,3072,192,")
|
||||
|| !javaText.contains("MAGIC_CLOUD, MAGIC_CARPET")
|
||||
|| !file("src/main/resources/assets/onlyfun/models/block/lucky_block.json").text.contains("onlyfun:block/lucky_block")
|
||||
|| !javaText.contains("class NourraFireworkItem extends FireworkRocketItem")
|
||||
|| !javaText.contains("class SkinFaceFireworkExplosions")
|
||||
|| !javaText.contains("FACE = {")
|
||||
|| !javaText.contains("HOOD = {")
|
||||
|| !javaText.contains("HOOD_TOP = {")
|
||||
|| !javaText.contains("0x4A9DFC")
|
||||
|| !javaText.contains("TRANSPARENT")
|
||||
|| !javaText.contains("class SkinFaceFireworkClient")
|
||||
|| !javaText.contains("DustParticleOptions")
|
||||
|| !javaText.contains("PIXEL = 1.85D")
|
||||
|| !javaText.contains("LARGE_PIXEL = 2.7D")
|
||||
|| !javaText.contains("LARGE_SPARK_SIZE = 5.15F")
|
||||
|| !javaText.contains("SPARK_LIFETIME_TICKS = 38")
|
||||
|| !javaText.contains("new Fireworks(3, List.of())")
|
||||
|| !javaText.contains("LIFETIME_TRIM_TICKS = 5")
|
||||
|| !javaText.contains("onlyfun\$midFlightLifetime")
|
||||
|| !javaText.contains("onlyfun\$drawSkinFace")
|
||||
|| !javaText.contains("output.accept(OnlyFunRegistries.NOURRA_FIREWORK)")
|
||||
|| !file("src/main/resources/onlyfun.mixins.json").text.contains("FireworkRocketLifetimeMixin")
|
||||
|| !file("src/main/resources/onlyfun.client.mixins.json").text.contains("FireworkRocketEntityMixin")
|
||||
|| !file("src/main/resources/onlyfun.client.mixins.json").text.contains("ParticleLifetimeAccessor")
|
||||
|| !file("src/main/resources/fabric.mod.json").text.contains("onlyfun.mixins.json")
|
||||
|| !file("src/main/resources/fabric.mod.json").text.contains("onlyfun.client.mixins.json")
|
||||
|| !file("src/main/resources/data/onlyfun/recipe/nourra_firework.json").text.contains("onlyfun:nourra_firework")
|
||||
|| ["fr_fr", "en_us", "ru_ru"].any { locale ->
|
||||
def translations = new JsonSlurper().parse(file(
|
||||
"src/main/resources/assets/onlyfun/lang/${locale}.json"))
|
||||
!translations.containsKey("item.onlyfun.nourra_firework")
|
||||
|| !translations.containsKey("tooltip.onlyfun.nourra_firework")
|
||||
}
|
||||
|| !missingModels.isEmpty()
|
||||
|| !invalidTextureHashes.isEmpty()
|
||||
|| !invalidTextures.isEmpty()) {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 641 B |
+100
@@ -0,0 +1,100 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.client;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkExplosions;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.mixin.client.ParticleLifetimeAccessor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.core.particles.ColorParticleOption;
|
||||
import net.minecraft.core.particles.DustParticleOptions;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
/** Paints the baked skin as a firework burst of coloured sparks, facing the player. */
|
||||
public final class SkinFaceFireworkClient {
|
||||
public static final double PIXEL = 1.85D;
|
||||
public static final double LARGE_PIXEL = 2.7D;
|
||||
public static final float SPARK_SIZE = 3.6F;
|
||||
public static final float LARGE_SPARK_SIZE = 5.15F;
|
||||
public static final int SPARK_LIFETIME_TICKS = 38;
|
||||
private static final int SUBPIXELS = 2;
|
||||
|
||||
private SkinFaceFireworkClient() {
|
||||
}
|
||||
|
||||
public static void explode(FireworkRocketEntity rocket) {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
if (minecraft.level == null || minecraft.particleEngine == null) return;
|
||||
Vec3 origin = rocket.position();
|
||||
RandomSource random = rocket.level().getRandom();
|
||||
minecraft.level.playLocalSound(origin.x, origin.y, origin.z,
|
||||
SoundEvents.FIREWORK_ROCKET_BLAST, SoundSource.AMBIENT,
|
||||
20.0F, 0.95F + random.nextFloat() * 0.1F, true);
|
||||
minecraft.level.addParticle(ColorParticleOption.create(ParticleTypes.FLASH, 0xFFFFFFFF),
|
||||
origin.x, origin.y, origin.z, 0.0D, 0.0D, 0.0D);
|
||||
Vec3 look = facing(minecraft, origin);
|
||||
Vec3 right = look.cross(new Vec3(0.0D, 1.0D, 0.0D));
|
||||
if (right.lengthSqr() < 1.0E-6D) right = new Vec3(1.0D, 0.0D, 0.0D);
|
||||
right = right.normalize();
|
||||
Vec3 up = new Vec3(0.0D, 1.0D, 0.0D);
|
||||
int[] pixels = SkinFaceFireworkExplosions.portrait();
|
||||
int width = SkinFaceFireworkExplosions.WIDTH;
|
||||
int height = SkinFaceFireworkExplosions.portraitHeight();
|
||||
boolean large = largeBurst(rocket);
|
||||
double pixel = burstPixel(large);
|
||||
float sparkSize = burstSparkSize(large);
|
||||
double midX = (width - 1) / 2.0D;
|
||||
double midY = (height - 1) / 2.0D;
|
||||
double step = 1.0D / SUBPIXELS;
|
||||
for (int row = 0; row < height; row++) {
|
||||
for (int col = 0; col < width; col++) {
|
||||
int color = visibleSpark(pixels[row * width + col]);
|
||||
DustParticleOptions dust = new DustParticleOptions(color, sparkSize);
|
||||
for (int subY = 0; subY < SUBPIXELS; subY++) {
|
||||
for (int subX = 0; subX < SUBPIXELS; subX++) {
|
||||
double px = (col - midX) + (subX + 0.5D) * step - 0.5D;
|
||||
double py = (midY - row) - (subY + 0.5D) * step + 0.5D;
|
||||
Vec3 offset = right.scale(px * pixel).add(up.scale(py * pixel));
|
||||
Vec3 pos = origin.add(offset);
|
||||
Vec3 velocity = offset.scale(0.05D);
|
||||
Particle spark = minecraft.particleEngine.createParticle(
|
||||
dust, pos.x, pos.y, pos.z, velocity.x, velocity.y, velocity.z);
|
||||
if (spark != null) {
|
||||
((ParticleLifetimeAccessor) spark).onlyfun$setLifetime(SPARK_LIFETIME_TICKS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean largeBurst(FireworkRocketEntity rocket) {
|
||||
return (rocket.getUUID().getLeastSignificantBits() & 1L) != 0L;
|
||||
}
|
||||
|
||||
public static double burstPixel(boolean large) {
|
||||
return large ? LARGE_PIXEL : PIXEL;
|
||||
}
|
||||
|
||||
public static float burstSparkSize(boolean large) {
|
||||
return large ? LARGE_SPARK_SIZE : SPARK_SIZE;
|
||||
}
|
||||
|
||||
private static Vec3 facing(Minecraft minecraft, Vec3 origin) {
|
||||
Vec3 camera = minecraft.gameRenderer.mainCamera().position();
|
||||
Vec3 horizontal = new Vec3(origin.x - camera.x, 0.0D, origin.z - camera.z);
|
||||
if (horizontal.lengthSqr() < 1.0E-4D) return new Vec3(0.0D, 0.0D, 1.0D);
|
||||
return horizontal.normalize();
|
||||
}
|
||||
|
||||
private static int visibleSpark(int rgb) {
|
||||
int red = rgb >> 16 & 0xFF;
|
||||
int green = rgb >> 8 & 0xFF;
|
||||
int blue = rgb & 0xFF;
|
||||
if (red + green + blue < 24) return 0x2A2A2A;
|
||||
return rgb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.item;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.FireworkRocketItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.component.TooltipDisplay;
|
||||
|
||||
/** Firework rocket that bursts into the baked Nourra skin face. */
|
||||
public final class NourraFireworkItem extends FireworkRocketItem {
|
||||
public NourraFireworkItem(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, TooltipContext context, TooltipDisplay display,
|
||||
Consumer<Component> tooltip, TooltipFlag flag) {
|
||||
tooltip.accept(Component.translatable("tooltip.onlyfun.nourra_firework").withStyle(ChatFormatting.AQUA));
|
||||
super.appendHoverText(stack, context, display, tooltip, flag);
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.item;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.registry.OnlyFunRegistries;
|
||||
import java.util.List;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.Fireworks;
|
||||
|
||||
/**
|
||||
* Baked Nourra head (NameMC 6166a439e9354904): inner face, hood overlay, hood top.
|
||||
* The portrait is drawn client-side with firework sparks.
|
||||
*/
|
||||
public final class SkinFaceFireworkExplosions {
|
||||
public static final int WIDTH = 8;
|
||||
public static final int HEIGHT = 8;
|
||||
public static final int TRANSPARENT = -1;
|
||||
/** Inner head front, top-left origin, row-major RGB. */
|
||||
public static final int[] FACE = {
|
||||
0x000000, 0x020202, 0x0A0A0A, 0x111111, 0x111111, 0x0A0A0A, 0x020202, 0x000000,
|
||||
0x020202, 0x0A0A0A, 0x111111, 0x181818, 0x181818, 0x111111, 0x0A0A0A, 0x020202,
|
||||
0x0A0A0A, 0x111111, 0x181818, 0x212121, 0x212121, 0x181818, 0x111111, 0x0A0A0A,
|
||||
0x111111, 0x181818, 0x212121, 0x2B2B2B, 0x2B2B2B, 0x212121, 0x181818, 0x111111,
|
||||
0x111111, 0x000000, 0x000000, 0xFFF0E1, 0xFFF0E1, 0x000000, 0x000000, 0x111111,
|
||||
0x0A0A0A, 0xFFFFFF, 0x4A9DFC, 0xFDE8D7, 0xFFF0E1, 0x4A9DFC, 0xFFFFFF, 0x0A0A0A,
|
||||
0x020202, 0xFFFFFF, 0x4A9DFC, 0xFDE8D7, 0xFDE8D7, 0x4A9DFC, 0xFFFFFF, 0x020202,
|
||||
0x000000, 0xF0C6A5, 0xF7D5B9, 0xFCE1CA, 0xFCE1CA, 0xF7D5B9, 0xF0C6A5, 0x000000
|
||||
};
|
||||
/** Hat/hood overlay on the front; {@link #TRANSPARENT} keeps the inner face visible. */
|
||||
public static final int[] HOOD = {
|
||||
0x000000, 0x0D0D0D, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0x0D0D0D, 0x000000,
|
||||
0x0D0D0D, 0x707070, 0xD8D8D8, 0xE5E5E5, 0xE5E5E5, 0xD8D8D8, 0x707070, 0x0D0D0D,
|
||||
0xCBCBCB, 0xD8D8D8, 0xFFFFFF, 0x212121, 0x212121, 0x181818, 0xD8D8D8, 0xCBCBCB,
|
||||
0xD8D8D8, 0x181818, 0x212121, 0x2B2B2B, 0x2B2B2B, 0x212121, 0x181818, 0xD8D8D8,
|
||||
0x111111, 0x181818, 0x212121, 0x2B2B2B, TRANSPARENT, TRANSPARENT, TRANSPARENT, 0x111111,
|
||||
0x0A0A0A, 0x111111, 0x181818, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
|
||||
0x020202, 0x0A0A0A, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
|
||||
TRANSPARENT, 0x020202, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT
|
||||
};
|
||||
/** Front edge of the hood top (skin overlay top rows closest to the face). */
|
||||
public static final int[] HOOD_TOP = {
|
||||
0xBEBEBE, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0xBEBEBE,
|
||||
0xB8B8B8, 0xB8B8B8, 0xCBCBCB, 0xD8D8D8, 0xD8D8D8, 0xCBCBCB, 0xB8B8B8, 0xB8B8B8
|
||||
};
|
||||
|
||||
private SkinFaceFireworkExplosions() {
|
||||
}
|
||||
|
||||
/** Vanilla flight 3 minus half a gunpowder step, so the burst sits between 2 and 3. */
|
||||
public static final int LIFETIME_TRIM_TICKS = 5;
|
||||
|
||||
public static Fireworks defaultFireworks() {
|
||||
return new Fireworks(3, List.of());
|
||||
}
|
||||
|
||||
public static int midFlightLifetime(int vanillaLifetime) {
|
||||
return Math.max(1, vanillaLifetime - LIFETIME_TRIM_TICKS);
|
||||
}
|
||||
|
||||
public static boolean isSkinFaceRocket(ItemStack stack) {
|
||||
return !stack.isEmpty() && stack.is(OnlyFunRegistries.NOURRA_FIREWORK);
|
||||
}
|
||||
|
||||
public static int opaqueHoodPixels() {
|
||||
int count = 0;
|
||||
for (int color : HOOD) {
|
||||
if (color != TRANSPARENT) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Hood top (2 rows) then the 8×8 face with the hood overlay composited. */
|
||||
public static int[] portrait() {
|
||||
int[] pixels = new int[WIDTH * (HEIGHT + 2)];
|
||||
System.arraycopy(HOOD_TOP, 0, pixels, 0, HOOD_TOP.length);
|
||||
for (int index = 0; index < FACE.length; index++) {
|
||||
pixels[HOOD_TOP.length + index] = HOOD[index] == TRANSPARENT ? FACE[index] : HOOD[index];
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
public static int portraitHeight() {
|
||||
return HEIGHT + HOOD_TOP.length / WIDTH;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.mixin;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkExplosions;
|
||||
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(FireworkRocketEntity.class)
|
||||
public abstract class FireworkRocketLifetimeMixin {
|
||||
@Shadow
|
||||
private int life;
|
||||
@Shadow
|
||||
private int lifetime;
|
||||
|
||||
@Inject(method = "tick", at = @At("HEAD"))
|
||||
private void onlyfun$midFlightLifetime(CallbackInfo callback) {
|
||||
if (this.life != 0) return;
|
||||
FireworkRocketEntity rocket = (FireworkRocketEntity) (Object) this;
|
||||
if (!SkinFaceFireworkExplosions.isSkinFaceRocket(rocket.getItem())) return;
|
||||
this.lifetime = SkinFaceFireworkExplosions.midFlightLifetime(this.lifetime);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.mixin.client;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.client.SkinFaceFireworkClient;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkExplosions;
|
||||
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
|
||||
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;
|
||||
|
||||
@Mixin(FireworkRocketEntity.class)
|
||||
public abstract class FireworkRocketEntityMixin {
|
||||
@Inject(method = "handleEntityEvent", at = @At("HEAD"), cancellable = true)
|
||||
private void onlyfun$drawSkinFace(byte id, CallbackInfo callback) {
|
||||
if (id != 17) return;
|
||||
FireworkRocketEntity rocket = (FireworkRocketEntity) (Object) this;
|
||||
if (!rocket.level().isClientSide()) return;
|
||||
if (!SkinFaceFireworkExplosions.isSkinFaceRocket(rocket.getItem())) return;
|
||||
SkinFaceFireworkClient.explode(rocket);
|
||||
callback.cancel();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.mixin.client;
|
||||
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(Particle.class)
|
||||
public interface ParticleLifetimeAccessor {
|
||||
@Accessor("lifetime")
|
||||
void onlyfun$setLifetime(int lifetime);
|
||||
}
|
||||
+1
@@ -28,6 +28,7 @@ public final class OnlyFunCreativeTab {
|
||||
output.accept(OnlyFunRegistries.CAPE_LOOTBOX);
|
||||
output.accept(OnlyFunRegistries.SPAWN_EGG_LOOTBOX);
|
||||
output.accept(OnlyFunRegistries.ANOMALY_LOOTBOX);
|
||||
output.accept(OnlyFunRegistries.NOURRA_FIREWORK);
|
||||
output.accept(AbjectionRegistries.SHIT_BALL);
|
||||
output.accept(AbjectionRegistries.SHIT_CUBE_ITEM);
|
||||
})
|
||||
|
||||
+10
@@ -3,6 +3,8 @@ package fr.koka99cab.sanctuary26.onlyfun.registry;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.OnlyFunMod;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.block.LuckyBlockBlock;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.item.LootboxItem;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkExplosions;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.item.NourraFireworkItem;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.loot.OnlyFunGenerator;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.loot.OnlyFunSource;
|
||||
import java.util.function.Function;
|
||||
@@ -32,6 +34,9 @@ public final class OnlyFunRegistries {
|
||||
public static final Item CAPE_LOOTBOX = registerLootbox("cape_lootbox", OnlyFunSource.CAPE_LOOTBOX);
|
||||
public static final Item SPAWN_EGG_LOOTBOX = registerLootbox("spawn_egg_lootbox", OnlyFunSource.SPAWN_EGG_LOOTBOX);
|
||||
public static final Item ANOMALY_LOOTBOX = registerLootbox("anomaly_lootbox", OnlyFunSource.ANOMALY_LOOTBOX);
|
||||
public static final Item NOURRA_FIREWORK = registerItem("nourra_firework",
|
||||
properties -> new NourraFireworkItem(properties.stacksTo(64)
|
||||
.component(DataComponents.FIREWORKS, SkinFaceFireworkExplosions.defaultFireworks())));
|
||||
private static boolean initialized;
|
||||
|
||||
private OnlyFunRegistries() {
|
||||
@@ -61,6 +66,11 @@ public final class OnlyFunRegistries {
|
||||
OnlyFunMod.LOGGER.debug("[{}] Item and block registries initialized.", OnlyFunMod.DISPLAY_NAME);
|
||||
}
|
||||
|
||||
private static Item registerItem(String path, Function<Item.Properties, Item> factory) {
|
||||
ResourceKey<Item> key = ResourceKey.create(Registries.ITEM, OnlyFunMod.id(path));
|
||||
return Registry.register(BuiltInRegistries.ITEM, key, factory.apply(new Item.Properties().setId(key)));
|
||||
}
|
||||
|
||||
private static Item registerLootbox(String path, OnlyFunSource source) {
|
||||
ResourceKey<Item> key = ResourceKey.create(Registries.ITEM, OnlyFunMod.id(path));
|
||||
return Registry.register(BuiltInRegistries.ITEM, key,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "onlyfun:item/nourra_firework"
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@
|
||||
"item.onlyfun.cape_lootbox": "Cape Lootbox",
|
||||
"item.onlyfun.spawn_egg_lootbox": "Spawn Egg Lootbox",
|
||||
"item.onlyfun.anomaly_lootbox": "Anomaly Lootbox",
|
||||
"item.onlyfun.nourra_firework": "Nourra Firework",
|
||||
"tooltip.onlyfun.nourra_firework": "The burst paints the skin face, at normal or larger size.",
|
||||
"item.onlyfun.quality_gear": "Quality Gear",
|
||||
"item.onlyfun.impossible_tool": "Impossible Tool",
|
||||
"item.onlyfun.impossible_book": "Impossible Book",
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
"item.onlyfun.cape_lootbox": "Lootbox de capes",
|
||||
"item.onlyfun.spawn_egg_lootbox": "Lootbox d'œufs",
|
||||
"item.onlyfun.anomaly_lootbox": "Lootbox anomalie",
|
||||
"item.onlyfun.nourra_firework": "Feu d'artifice Nourra",
|
||||
"tooltip.onlyfun.nourra_firework": "L'explosion dessine le visage, en taille normale ou plus grande.",
|
||||
"item.onlyfun.quality_gear": "Équipement de qualité",
|
||||
"item.onlyfun.impossible_tool": "Outil impossible",
|
||||
"item.onlyfun.impossible_book": "Livre impossible",
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
"item.onlyfun.cape_lootbox": "Лутбокс плащей",
|
||||
"item.onlyfun.spawn_egg_lootbox": "Лутбокс яиц призыва",
|
||||
"item.onlyfun.anomaly_lootbox": "Лутбокс-аномалия",
|
||||
"item.onlyfun.nourra_firework": "Фейерверк Нурры",
|
||||
"tooltip.onlyfun.nourra_firework": "Взрыв рисует лицо скина обычного или увеличенного размера.",
|
||||
"item.onlyfun.quality_gear": "Качественное снаряжение",
|
||||
"item.onlyfun.impossible_tool": "Невозможный инструмент",
|
||||
"item.onlyfun.impossible_book": "Невозможная книга",
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "minecraft:item/generated",
|
||||
"textures": {
|
||||
"layer0": "onlyfun:item/nourra_firework"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 280 B |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "minecraft:crafting_shapeless",
|
||||
"category": "misc",
|
||||
"ingredients": [
|
||||
"minecraft:firework_rocket",
|
||||
"minecraft:light_blue_dye",
|
||||
"minecraft:paper"
|
||||
],
|
||||
"result": {
|
||||
"count": 1,
|
||||
"id": "onlyfun:nourra_firework"
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,13 @@
|
||||
"fr.koka99cab.sanctuary26.onlyfun.client.OnlyFunClient"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"onlyfun.mixins.json",
|
||||
{
|
||||
"config": "onlyfun.client.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.19.3",
|
||||
"minecraft": "~26.2",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "fr.koka99cab.sanctuary26.onlyfun.mixin.client",
|
||||
"compatibilityLevel": "JAVA_25",
|
||||
"client": [
|
||||
"FireworkRocketEntityMixin",
|
||||
"ParticleLifetimeAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "fr.koka99cab.sanctuary26.onlyfun.mixin",
|
||||
"compatibilityLevel": "JAVA_25",
|
||||
"mixins": [
|
||||
"FireworkRocketLifetimeMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.item;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.client.SkinFaceFireworkClient;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.world.item.component.Fireworks;
|
||||
|
||||
/** Executable check for the baked Nourra face and hood firework contract. */
|
||||
public final class SkinFaceFireworkSmoke {
|
||||
private SkinFaceFireworkSmoke() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
verify();
|
||||
}
|
||||
|
||||
public static void verify() {
|
||||
require(SkinFaceFireworkExplosions.WIDTH == 8 && SkinFaceFireworkExplosions.HEIGHT == 8,
|
||||
"Face grid is no longer 8×8");
|
||||
require(SkinFaceFireworkExplosions.FACE.length == 64, "Face palette length drifted");
|
||||
require(SkinFaceFireworkExplosions.HOOD.length == 64, "Hood overlay length drifted");
|
||||
require(SkinFaceFireworkExplosions.HOOD_TOP.length == 16, "Hood top strip length drifted");
|
||||
require(SkinFaceFireworkExplosions.FACE[5 * 8 + 2] == 0x4A9DFC
|
||||
&& SkinFaceFireworkExplosions.FACE[5 * 8 + 5] == 0x4A9DFC,
|
||||
"Blue eyes are missing from the inner face");
|
||||
require(SkinFaceFireworkExplosions.FACE[4 * 8 + 3] == 0xFFF0E1,
|
||||
"Skin tone is missing from the baked face");
|
||||
require(SkinFaceFireworkExplosions.HOOD[2] == 0xCBCBCB,
|
||||
"Hood overlay is missing from the top of the face");
|
||||
require(SkinFaceFireworkExplosions.HOOD[5 * 8 + 2] == 0x181818,
|
||||
"Hood no longer covers the left eye");
|
||||
require(SkinFaceFireworkExplosions.HOOD[5 * 8 + 5] == SkinFaceFireworkExplosions.TRANSPARENT,
|
||||
"Hood opening no longer leaves the right eye visible");
|
||||
require(SkinFaceFireworkExplosions.opaqueHoodPixels() == 43,
|
||||
"Opaque hood pixel count drifted");
|
||||
Fireworks fireworks = SkinFaceFireworkExplosions.defaultFireworks();
|
||||
require(fireworks.flightDuration() == 3 && fireworks.explosions().isEmpty(),
|
||||
"Default firework burst contract changed");
|
||||
require(SkinFaceFireworkExplosions.LIFETIME_TRIM_TICKS == 5
|
||||
&& SkinFaceFireworkExplosions.midFlightLifetime(45) == 40,
|
||||
"Mid-flight lifetime trim drifted");
|
||||
int[] portrait = SkinFaceFireworkExplosions.portrait();
|
||||
require(portrait.length == 80 && SkinFaceFireworkExplosions.portraitHeight() == 10,
|
||||
"Portrait grid drifted");
|
||||
require(portrait[16 + 5 * 8 + 5] == 0x4A9DFC,
|
||||
"Right eye is missing from the composited portrait");
|
||||
require(portrait[2] == 0xD8D8D8,
|
||||
"Hood top is missing from the portrait");
|
||||
require(SkinFaceFireworkClient.PIXEL == 1.85D, "Portrait scale drifted");
|
||||
require(SkinFaceFireworkClient.LARGE_PIXEL == 2.7D, "Large portrait scale drifted");
|
||||
require(SkinFaceFireworkClient.burstPixel(false) == SkinFaceFireworkClient.PIXEL
|
||||
&& SkinFaceFireworkClient.burstSparkSize(false) == SkinFaceFireworkClient.SPARK_SIZE,
|
||||
"Normal burst scale drifted");
|
||||
require(SkinFaceFireworkClient.burstPixel(true) == SkinFaceFireworkClient.LARGE_PIXEL
|
||||
&& SkinFaceFireworkClient.burstSparkSize(true) == SkinFaceFireworkClient.LARGE_SPARK_SIZE,
|
||||
"Large burst scale drifted");
|
||||
require(SkinFaceFireworkClient.SPARK_LIFETIME_TICKS == 38, "Spark lifetime drifted");
|
||||
System.out.println("Only Fun Nourra firework: 8×8 face with hood overlay ready.");
|
||||
}
|
||||
|
||||
private static void require(boolean condition, String message) {
|
||||
if (!condition) throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.koka99cab.sanctuary26.onlyfun.loot;
|
||||
|
||||
import fr.koka99cab.sanctuary26.onlyfun.item.SkinFaceFireworkSmoke;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.shop.OnlyFunShopCatalogSmoke;
|
||||
import fr.koka99cab.sanctuary26.onlyfun.world.CobbleDungeonPlacementSmoke;
|
||||
import java.util.EnumMap;
|
||||
@@ -18,6 +19,7 @@ public final class OnlyFunGeneratorSmoke {
|
||||
Bootstrap.bootStrap();
|
||||
OnlyFunShopCatalogSmoke.verify();
|
||||
CobbleDungeonPlacementSmoke.verify();
|
||||
SkinFaceFireworkSmoke.verify();
|
||||
OnlyFunGenerator.initialize();
|
||||
require(OnlyFunGenerator.ANOMALY_DENOMINATOR == 256, "Anomaly odds changed from 1/256");
|
||||
require(OnlyFunGenerator.PITY_START == 24, "Pity threshold changed");
|
||||
|
||||
Reference in New Issue
Block a user