diff --git a/common/src/main/java/dev/latvian/mods/kubejs/bindings/event/ClientEvents.java b/common/src/main/java/dev/latvian/mods/kubejs/bindings/event/ClientEvents.java index 64054a999..28a9b90b3 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/bindings/event/ClientEvents.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/bindings/event/ClientEvents.java @@ -6,6 +6,7 @@ import dev.latvian.mods.kubejs.client.DebugInfoEventJS; import dev.latvian.mods.kubejs.client.GenerateClientAssetsEventJS; import dev.latvian.mods.kubejs.client.LangEventJS; +import dev.latvian.mods.kubejs.client.ParticleProviderRegistryEventJS; import dev.latvian.mods.kubejs.client.painter.screen.PaintScreenEventJS; import dev.latvian.mods.kubejs.event.EventGroup; import dev.latvian.mods.kubejs.event.EventHandler; @@ -25,4 +26,5 @@ public interface ClientEvents { EventHandler PAINT_SCREEN = GROUP.client("paintScreen", () -> PaintScreenEventJS.class); EventHandler ATLAS_SPRITE_REGISTRY = GROUP.client("atlasSpriteRegistry", () -> AtlasSpriteRegistryEventJS.class).extra(Extra.REQUIRES_ID); EventHandler LANG = GROUP.client("lang", () -> LangEventJS.class).extra(Extra.REQUIRES_STRING); + EventHandler PARTICLE_PROVIDER_REGISTRY = GROUP.client("particleProviderRegistry", () -> ParticleProviderRegistryEventJS.class); } diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/GenerateClientAssetsEventJS.java b/common/src/main/java/dev/latvian/mods/kubejs/client/GenerateClientAssetsEventJS.java index 9361393e5..96a44af50 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/client/GenerateClientAssetsEventJS.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/GenerateClientAssetsEventJS.java @@ -58,4 +58,12 @@ public void defaultHandheldItemModel(ResourceLocation id) { model.texture("layer0", id.getNamespace() + ":item/" + id.getPath()); }); } + + public void particle(ResourceLocation id, Consumer consumer) { + generator.particle(id, consumer); + } + + public void sounds(String namespace, Consumer consumer) { + generator.sounds(namespace, consumer); + } } \ No newline at end of file diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/GeneratedClientResourcePack.java b/common/src/main/java/dev/latvian/mods/kubejs/client/GeneratedClientResourcePack.java index 0458536ab..9a56793ae 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/client/GeneratedClientResourcePack.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/GeneratedClientResourcePack.java @@ -97,6 +97,8 @@ public void generate(Map map) { ClientEvents.HIGH_ASSETS.post(ScriptType.CLIENT, new GenerateClientAssetsEventJS(generator)); + generator.buildSounds(); + for (var lang : ClientEvents.LANG.findUniqueExtraIds(ScriptType.CLIENT)) { var l = String.valueOf(lang); diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/KubeAnimatedParticle.java b/common/src/main/java/dev/latvian/mods/kubejs/client/KubeAnimatedParticle.java new file mode 100644 index 000000000..6e23e4918 --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/KubeAnimatedParticle.java @@ -0,0 +1,101 @@ +package dev.latvian.mods.kubejs.client; + +import dev.latvian.mods.kubejs.typings.Info; +import dev.latvian.mods.rhino.mod.util.color.Color; +import it.unimi.dsi.fastutil.floats.Float2IntFunction; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.SimpleAnimatedParticle; +import net.minecraft.client.particle.SpriteSet; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.util.RandomSource; + +public class KubeAnimatedParticle extends SimpleAnimatedParticle { + + private Float2IntFunction lightColorFunction; + + public KubeAnimatedParticle(ClientLevel clientLevel, SpriteSet spriteSet, double x, double y, double z) { + super(clientLevel, x, y, z, spriteSet, 0.0125F); + setLifetime(20); + setSpriteFromAge(spriteSet); + lightColorFunction = super::getLightColor; + } + + public void setGravity(float g) { + this.gravity = g; + } + + @Info(value = "Sets the friction value of the particle, the particle's motion is multiplied by this value every tick") + public void setFriction(float f) { + this.friction = f; + } + + public void setColor(Color color, boolean alpha) { + setColor(color.getRgbJS()); + if (alpha) { + setAlpha((color.getArgbJS() >>> 24) / 255.0F); + } + } + + public void setColor(Color color) { + setColor(color, false); + } + + public void setPhysicality(boolean hasPhysics) { + this.hasPhysics = hasPhysics; + } + + public void setFasterWhenYMotionBlocked(boolean b) { + speedUpWhenYMotionIsBlocked = b; + } + + public void setLightColor(Float2IntFunction function) { + lightColorFunction = function; + } + + // Getters for protected values + + public ClientLevel getLevel() { + return level; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public double getXSpeed() { + return xd; + } + + public double getYSpeed() { + return yd; + } + + public double getZSpeed() { + return zd; + } + + public SpriteSet getSpriteSet() { + return sprites; + } + + public TextureAtlasSprite getSprite() { + return sprite; + } + + public RandomSource getRandom() { + return random; + } + + @Override + public int getLightColor(float f) { + return lightColorFunction.get(f); + } +} diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClientEventHandler.java b/common/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClientEventHandler.java index 7376e82af..c02b9470c 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClientEventHandler.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClientEventHandler.java @@ -2,6 +2,7 @@ import dev.architectury.event.EventResult; import dev.architectury.event.events.client.ClientGuiEvent; +import dev.architectury.event.events.client.ClientLifecycleEvent; import dev.architectury.event.events.client.ClientPlayerEvent; import dev.architectury.event.events.client.ClientTooltipEvent; import dev.architectury.hooks.client.screen.ScreenAccess; @@ -57,6 +58,7 @@ public void init() { ClientGuiEvent.RENDER_POST.register(Painter.INSTANCE::guiScreenDraw); ClientGuiEvent.INIT_PRE.register(this::guiPreInit); ClientGuiEvent.INIT_POST.register(this::guiPostInit); + ClientLifecycleEvent.CLIENT_STARTED.register(this::clientStart); //ClientTextureStitchEvent.POST.register(this::postAtlasStitch); } @@ -170,6 +172,10 @@ private void guiPostInit(Screen screen, ScreenAccess access) { } } + private void clientStart(Minecraft mc) { + ClientEvents.PARTICLE_PROVIDER_REGISTRY.post(new ParticleProviderRegistryEventJS()); + } + /*private void postAtlasStitch(TextureAtlas atlas) { if (!ClientProperties.get().getExportAtlases()) { return; diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/ParticleGenerator.java b/common/src/main/java/dev/latvian/mods/kubejs/client/ParticleGenerator.java new file mode 100644 index 000000000..d63999a5a --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/ParticleGenerator.java @@ -0,0 +1,31 @@ +package dev.latvian.mods.kubejs.client; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.minecraft.resources.ResourceLocation; + +import java.util.ArrayList; +import java.util.List; + +public class ParticleGenerator { + + public transient List textures = new ArrayList<>(); + + public ParticleGenerator texture(ResourceLocation location) { + textures.add(location.toString()); + return this; + } + + public ParticleGenerator textures(List textures) { + this.textures = textures; + return this; + } + + public JsonObject toJson() { + final JsonObject json = new JsonObject(); + final JsonArray textures = new JsonArray(this.textures.size()); + this.textures.forEach(textures::add); + json.add("textures", textures); + return json; + } +} diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/ParticleProviderRegistryEventJS.java b/common/src/main/java/dev/latvian/mods/kubejs/client/ParticleProviderRegistryEventJS.java new file mode 100644 index 000000000..3bd8999c3 --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/ParticleProviderRegistryEventJS.java @@ -0,0 +1,41 @@ +package dev.latvian.mods.kubejs.client; + +import dev.architectury.registry.client.particle.ParticleProviderRegistry; +import dev.latvian.mods.kubejs.event.EventJS; +import net.minecraft.Util; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleType; + +import java.util.function.Consumer; + +public class ParticleProviderRegistryEventJS extends EventJS { + + public void register(ParticleType type, ParticleProvider provider) { + ParticleProviderRegistry.register(type, provider); + } + + public void registerSimple(ParticleType type, Consumer particle) { + ParticleProviderRegistry.register(type, set -> (t, level, x, y, z, dx, dy, dz) -> Util.make(Util.make(() -> { + var kube = new KubeAnimatedParticle(level, set, x, y, z); + kube.setParticleSpeed(dx, dy, dz); + return kube; + }), particle)); + } + + public void registerSimple(ParticleType type) { + registerSimple(type, particle -> {}); + } + + @FunctionalInterface + public interface ParticleProvider extends ParticleProviderRegistry.DeferredParticleProvider { + Particle create(T particleOptions, ClientLevel level, ParticleProviderRegistry.ExtendedSpriteSet spriteSet, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed); + + @Override + default net.minecraft.client.particle.ParticleProvider create(ParticleProviderRegistry.ExtendedSpriteSet spriteSet) { + return (t, level, x, y, z, xSpeed, ySpeed, zSpeed) -> create(t, level, spriteSet, x, y, z, xSpeed, ySpeed, zSpeed); + } + } +} diff --git a/common/src/main/java/dev/latvian/mods/kubejs/client/SoundGenerator.java b/common/src/main/java/dev/latvian/mods/kubejs/client/SoundGenerator.java new file mode 100644 index 000000000..510b35c9e --- /dev/null +++ b/common/src/main/java/dev/latvian/mods/kubejs/client/SoundGenerator.java @@ -0,0 +1,163 @@ +package dev.latvian.mods.kubejs.client; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.Util; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; + +public class SoundGenerator { + + private final Map entries = new HashMap<>(); + + public void addSound(String path, Consumer consumer) { + if (entries.containsKey(path)) { + consumer.accept(entries.get(path)); + } else { + entries.put(path, Util.make(new SoundEntry(), consumer)); + } + } + + public JsonObject toJson() { + final JsonObject json = new JsonObject(); + entries.forEach((path, entry) -> json.add(path, entry.toJson())); + return json; + } + + public static class SoundEntry { + + private boolean replace = false; + @Nullable + private String subtitle; + private final List sounds = new ArrayList<>(); + + public SoundEntry replace(boolean b) { + replace = b; + return this; + } + + public SoundEntry replace() { return replace(true); } + + public SoundEntry subtitle(String subtitle) { + this.subtitle = subtitle; + return this; + } + + public SoundEntry sounds(ResourceLocation... sounds) { + this.sounds.addAll(Stream.of(sounds).map(SoundInstance::new).toList()); + return this; + } + + public SoundEntry sound(ResourceLocation file, Consumer consumer) { + sounds.add(Util.make(new SoundInstance(file), consumer)); + return this; + } + + public JsonObject toJson() { + final JsonObject json = new JsonObject(); + if (replace) { + json.addProperty("replace", true); + } + if (subtitle != null) { + json.addProperty("subtitle", subtitle); + } + if (!sounds.isEmpty()) { + final JsonArray array = new JsonArray(sounds.size()); + sounds.forEach(instance -> array.add(instance.toJson())); + json.add("sounds" ,array); + } + return json; + } + } + + public static class SoundInstance { + + private final ResourceLocation fileLocation; + private boolean complex = false; + private float volume = 1.0F; + private float pitch = 1.0F; + private int weight = 1; + private boolean stream = false; + private int attenuationDistance = 16; + private boolean preload = false; + private boolean isEventReference = false; + + + public SoundInstance(ResourceLocation fileLocation) { + this.fileLocation = fileLocation; + } + + private SoundInstance complex() { + complex = true; + return this; + } + + public SoundInstance volume(float f) { + volume = Mth.clamp(f, 0.0F, 1.0F); + return complex(); + } + + public SoundInstance pitch(float f) { + pitch = Mth.clamp(f, 0.0F, 1.0F); + return complex(); + } + + public SoundInstance weight(int i) { + weight = i; + return complex(); + } + + public SoundInstance stream(boolean b) { + stream = b; + return complex(); + } + + public SoundInstance stream() { return stream(true); } + + public SoundInstance attenuationDistance(int i) { + attenuationDistance = i; + return complex(); + } + + public SoundInstance preload(boolean b) { + preload = b; + return complex(); + } + + public SoundInstance preload() { return preload(true); } + + public SoundInstance asReferenceToEvent() { + isEventReference = true; + return complex(); + } + + public JsonElement toJson() { + if (!complex) { + return new JsonPrimitive(fileLocation.toString()); + } + + final JsonObject json = new JsonObject(); + json.addProperty("name", fileLocation.toString()); + json.addProperty("volume", volume); + json.addProperty("pitch", pitch); + json.addProperty("weight", weight); + json.addProperty("stream", stream); + json.addProperty("attenuation_distance", attenuationDistance); + json.addProperty("preload", preload); + if (isEventReference) { + json.addProperty("type", "event"); + } + return json; + } + } +} diff --git a/common/src/main/java/dev/latvian/mods/kubejs/core/MinecraftClientKJS.java b/common/src/main/java/dev/latvian/mods/kubejs/core/MinecraftClientKJS.java index 2d31f0769..2d9737864 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/core/MinecraftClientKJS.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/core/MinecraftClientKJS.java @@ -3,6 +3,7 @@ import com.mojang.blaze3d.platform.InputConstants; import dev.latvian.mods.kubejs.bindings.event.ItemEvents; import dev.latvian.mods.kubejs.client.ClientProperties; +import dev.latvian.mods.kubejs.client.KubeAnimatedParticle; import dev.latvian.mods.kubejs.item.ItemClickedEventJS; import dev.latvian.mods.kubejs.net.FirstClickMessage; import dev.latvian.mods.kubejs.script.ScriptType; @@ -11,6 +12,8 @@ import dev.latvian.mods.rhino.util.RemapPrefixForJS; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.particle.SpriteSet; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import org.jetbrains.annotations.Nullable; @@ -84,6 +87,15 @@ public interface MinecraftClientKJS extends MinecraftEnvironmentKJS { return Screen.hasAltDown(); } + // PR Review: A different name may be better, or perhaps this should be in ClientLevelKJS + default KubeAnimatedParticle kjs$customParticle(SpriteSet spriteSet, double x, double y, double z) { + return kjs$customParticle(kjs$self().level, spriteSet, x, y, z); + } + + default KubeAnimatedParticle kjs$customParticle(ClientLevel level, SpriteSet spriteSet, double x, double y, double z) { + return new KubeAnimatedParticle(level, spriteSet, x, y, z); + } + @HideFromJS default void kjs$startAttack0() { if (ItemEvents.FIRST_LEFT_CLICKED.hasListeners()) { diff --git a/common/src/main/java/dev/latvian/mods/kubejs/generator/AssetJsonGenerator.java b/common/src/main/java/dev/latvian/mods/kubejs/generator/AssetJsonGenerator.java index 562648590..7384d7898 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/generator/AssetJsonGenerator.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/generator/AssetJsonGenerator.java @@ -4,6 +4,8 @@ import dev.latvian.mods.kubejs.KubeJSPaths; import dev.latvian.mods.kubejs.client.ModelGenerator; import dev.latvian.mods.kubejs.client.MultipartBlockStateGenerator; +import dev.latvian.mods.kubejs.client.ParticleGenerator; +import dev.latvian.mods.kubejs.client.SoundGenerator; import dev.latvian.mods.kubejs.client.StencilTexture; import dev.latvian.mods.kubejs.client.VariantBlockStateGenerator; import dev.latvian.mods.kubejs.script.data.GeneratedData; @@ -21,10 +23,12 @@ public class AssetJsonGenerator extends ResourceGenerator { private final Map stencils; + private final Map sounds; public AssetJsonGenerator(Map m) { super(ConsoleJS.CLIENT, m); this.stencils = new HashMap<>(); + this.sounds = new HashMap<>(); } public void blockState(ResourceLocation id, Consumer consumer) { @@ -47,6 +51,19 @@ public void itemModel(ResourceLocation id, Consumer consumer) { json(asItemModelLocation(id), gen.toJson()); } + public void particle(ResourceLocation id, Consumer consumer) { + var gen = Util.make(new ParticleGenerator(), consumer); + json(new ResourceLocation(id.getNamespace(), "particles/" + id.getPath()), gen.toJson()); + } + + public void sounds(String mod, Consumer consumer) { + if (sounds.containsKey(mod)) { + consumer.accept(sounds.get(mod)); + } else { + sounds.put(mod, Util.make(new SoundGenerator(), consumer)); + } + } + public static ResourceLocation asItemModelLocation(ResourceLocation id) { return new ResourceLocation(id.getNamespace(), "models/item/" + id.getPath()); } @@ -81,4 +98,8 @@ public void stencil(ResourceLocation target, String stencil, JsonObject colors) add(new ResourceLocation(target.getNamespace(), "textures/" + target.getPath() + ".png.mcmeta"), () -> st1.mcmeta, false); } } + + public void buildSounds() { + sounds.forEach((mod, gen) -> json(new ResourceLocation(mod, "sounds"), gen.toJson())); + } } diff --git a/common/src/main/java/dev/latvian/mods/kubejs/misc/ParticleTypeBuilder.java b/common/src/main/java/dev/latvian/mods/kubejs/misc/ParticleTypeBuilder.java index e257ccf48..147e40e70 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/misc/ParticleTypeBuilder.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/misc/ParticleTypeBuilder.java @@ -1,18 +1,24 @@ package dev.latvian.mods.kubejs.misc; +import dev.latvian.mods.kubejs.client.ParticleGenerator; +import dev.latvian.mods.kubejs.generator.AssetJsonGenerator; import dev.latvian.mods.kubejs.registry.BuilderBase; import dev.latvian.mods.kubejs.registry.RegistryInfo; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleType; import net.minecraft.resources.ResourceLocation; +import java.util.function.Consumer; + public class ParticleTypeBuilder extends BuilderBase> { public transient boolean overrideLimiter; public transient ParticleOptions.Deserializer deserializer; + public transient Consumer assetGen; public ParticleTypeBuilder(ResourceLocation i) { super(i); overrideLimiter = false; + assetGen = g -> g.texture(id); } @Override @@ -34,9 +40,19 @@ public ParticleTypeBuilder overrideLimiter(boolean o) { return this; } + public ParticleTypeBuilder textures(Consumer gen) { + assetGen = gen; + return this; + } + // TODO: Figure out if this is even needed public ParticleTypeBuilder deserializer(ParticleOptions.Deserializer d) { deserializer = d; return this; } + + @Override + public void generateAssetJsons(AssetJsonGenerator generator) { + generator.particle(id, assetGen); + } } diff --git a/common/src/main/java/dev/latvian/mods/kubejs/misc/SoundEventBuilder.java b/common/src/main/java/dev/latvian/mods/kubejs/misc/SoundEventBuilder.java index c470c7fc8..02b4f38a2 100644 --- a/common/src/main/java/dev/latvian/mods/kubejs/misc/SoundEventBuilder.java +++ b/common/src/main/java/dev/latvian/mods/kubejs/misc/SoundEventBuilder.java @@ -1,13 +1,26 @@ package dev.latvian.mods.kubejs.misc; +import dev.latvian.mods.kubejs.client.SoundGenerator; +import dev.latvian.mods.kubejs.generator.AssetJsonGenerator; import dev.latvian.mods.kubejs.registry.BuilderBase; import dev.latvian.mods.kubejs.registry.RegistryInfo; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; +import java.util.function.Consumer; + public class SoundEventBuilder extends BuilderBase { + + public transient Consumer gen; + public SoundEventBuilder(ResourceLocation i) { super(i); + gen = e -> e.sounds(id); + } + + public SoundEventBuilder sound(Consumer consumer) { + gen = consumer; + return this; } @Override @@ -19,4 +32,9 @@ public final RegistryInfo getRegistryType() { public SoundEvent createObject() { return SoundEvent.createVariableRangeEvent(id); } + + @Override + public void generateAssetJsons(AssetJsonGenerator generator) { + generator.sounds(id.getNamespace(), g -> g.addSound(id.getPath(), gen)); + } }