particle update! but the math is borken. asking @Alwinfy for help

This commit is contained in:
gamma-delta 2022-03-04 18:44:51 -06:00
parent 3fb5f0d1b2
commit de3be9d04a
45 changed files with 695 additions and 372 deletions

View file

@ -1,7 +1,7 @@
eb78f6a2b68dd772a5016fc5a3db10488a65abfa assets/hexcasting/models/item/amethyst_dust.json
df940dd798240fac6fde700c57f8ae6aa43d1c9e assets/hexcasting/models/item/artifact.json
1a66f279c030ebd5ed6a596f63205094b2529819 assets/hexcasting/models/item/artifact_filled.json
9f3d25fb7fd5e4f3ee81142ac9396709ac068860 assets/hexcasting/models/item/battery.json
1b3a4bd9dd3c2af2894e0acc1d9d3c74d16c7625 assets/hexcasting/models/item/battery.json
dc72e3345c4375b37f3624040c9a8df435c958d1 assets/hexcasting/models/item/charged_amethyst.json
b9916a82d647db5d3c505de2eb5f0a528169e1db assets/hexcasting/models/item/cypher.json
5082df8eee8f56f8c0d74db71ccda37a165bc76f assets/hexcasting/models/item/cypher_filled.json
@ -21,7 +21,7 @@ c2ceb08a8662a7e4a311401b0baced1add6f2e35 assets/hexcasting/models/item/dye_color
f41fb9405b869644333872cd1ece40b1b07410d2 assets/hexcasting/models/item/dye_colorizer_red.json
5894e7a1cc38ae5b09c651791c65c983d984d20c assets/hexcasting/models/item/dye_colorizer_white.json
07959b86ed25559b4960c5f4aa7bab88bf9b5bf1 assets/hexcasting/models/item/dye_colorizer_yellow.json
5ee629510cbd0058a5736be2bf426d3a1b6717a0 assets/hexcasting/models/item/focus.json
f7f98a306a8a0529a54446b4876e624201525a1d assets/hexcasting/models/item/focus.json
0146e90177ed71b25d2936ff3b2d9975e47c5142 assets/hexcasting/models/item/focus_double.json
167b8e3f3367f12f8045369bea26d7a48e59b0d0 assets/hexcasting/models/item/focus_double_sealed.json
3ff6209fdfc13c6f5e4741a5892787cb5700a34c assets/hexcasting/models/item/focus_empty.json

View file

@ -3,105 +3,105 @@
{
"predicate": {
"hexcasting:mana": 0.0,
"hexcasting:max_mana": 1.0
"hexcasting:max_mana": 0.0
},
"model": "hexcasting:item/phial_small_0"
},
{
"predicate": {
"hexcasting:mana": 0.25,
"hexcasting:max_mana": 1.0
"hexcasting:max_mana": 0.0
},
"model": "hexcasting:item/phial_small_1"
},
{
"predicate": {
"hexcasting:mana": 0.5,
"hexcasting:max_mana": 1.0
"hexcasting:max_mana": 0.0
},
"model": "hexcasting:item/phial_small_2"
},
{
"predicate": {
"hexcasting:mana": 0.75,
"hexcasting:max_mana": 1.0
"hexcasting:max_mana": 0.0
},
"model": "hexcasting:item/phial_small_3"
},
{
"predicate": {
"hexcasting:mana": 1.0,
"hexcasting:max_mana": 1.0
"hexcasting:max_mana": 0.0
},
"model": "hexcasting:item/phial_small_4"
},
{
"predicate": {
"hexcasting:mana": 0.0,
"hexcasting:max_mana": 2.0
"hexcasting:max_mana": 1.0
},
"model": "hexcasting:item/phial_medium_0"
},
{
"predicate": {
"hexcasting:mana": 0.25,
"hexcasting:max_mana": 2.0
"hexcasting:max_mana": 1.0
},
"model": "hexcasting:item/phial_medium_1"
},
{
"predicate": {
"hexcasting:mana": 0.5,
"hexcasting:max_mana": 2.0
"hexcasting:max_mana": 1.0
},
"model": "hexcasting:item/phial_medium_2"
},
{
"predicate": {
"hexcasting:mana": 0.75,
"hexcasting:max_mana": 2.0
"hexcasting:max_mana": 1.0
},
"model": "hexcasting:item/phial_medium_3"
},
{
"predicate": {
"hexcasting:mana": 1.0,
"hexcasting:max_mana": 2.0
"hexcasting:max_mana": 1.0
},
"model": "hexcasting:item/phial_medium_4"
},
{
"predicate": {
"hexcasting:mana": 0.0,
"hexcasting:max_mana": 3.0
"hexcasting:max_mana": 2.0
},
"model": "hexcasting:item/phial_large_0"
},
{
"predicate": {
"hexcasting:mana": 0.25,
"hexcasting:max_mana": 3.0
"hexcasting:max_mana": 2.0
},
"model": "hexcasting:item/phial_large_1"
},
{
"predicate": {
"hexcasting:mana": 0.5,
"hexcasting:max_mana": 3.0
"hexcasting:max_mana": 2.0
},
"model": "hexcasting:item/phial_large_2"
},
{
"predicate": {
"hexcasting:mana": 0.75,
"hexcasting:max_mana": 3.0
"hexcasting:max_mana": 2.0
},
"model": "hexcasting:item/phial_large_3"
},
{
"predicate": {
"hexcasting:mana": 1.0,
"hexcasting:max_mana": 3.0
"hexcasting:max_mana": 2.0
},
"model": "hexcasting:item/phial_large_4"
}

View file

@ -6,78 +6,78 @@
},
"model": "hexcasting:item/focus_empty"
},
{
"predicate": {
"hexcasting:datatype": 99.99
},
"model": "hexcasting:item/focus_empty_sealed"
},
{
"predicate": {
"hexcasting:datatype": 0.99
},
"model": "hexcasting:item/focus_entity"
},
{
"predicate": {
"hexcasting:datatype": 100.99
},
"model": "hexcasting:item/focus_entity_sealed"
},
{
"predicate": {
"hexcasting:datatype": 1.99
},
"model": "hexcasting:item/focus_double"
},
{
"predicate": {
"hexcasting:datatype": 101.99
},
"model": "hexcasting:item/focus_double_sealed"
},
{
"predicate": {
"hexcasting:datatype": 2.99
},
"model": "hexcasting:item/focus_vec3"
},
{
"predicate": {
"hexcasting:datatype": 102.99
},
"model": "hexcasting:item/focus_vec3_sealed"
},
{
"predicate": {
"hexcasting:datatype": 3.99
},
"model": "hexcasting:item/focus_widget"
},
{
"predicate": {
"hexcasting:datatype": 103.99
},
"model": "hexcasting:item/focus_widget_sealed"
},
{
"predicate": {
"hexcasting:datatype": 4.99
},
"model": "hexcasting:item/focus_list"
},
{
"predicate": {
"hexcasting:datatype": 104.99
},
"model": "hexcasting:item/focus_list_sealed"
},
{
"predicate": {
"hexcasting:datatype": 5.99
},
"model": "hexcasting:item/focus_pattern"
},
{
"predicate": {
"hexcasting:datatype": 99.99
},
"model": "hexcasting:item/focus_empty_sealed"
},
{
"predicate": {
"hexcasting:datatype": 100.99
},
"model": "hexcasting:item/focus_entity_sealed"
},
{
"predicate": {
"hexcasting:datatype": 101.99
},
"model": "hexcasting:item/focus_double_sealed"
},
{
"predicate": {
"hexcasting:datatype": 102.99
},
"model": "hexcasting:item/focus_vec3_sealed"
},
{
"predicate": {
"hexcasting:datatype": 103.99
},
"model": "hexcasting:item/focus_widget_sealed"
},
{
"predicate": {
"hexcasting:datatype": 104.99
},
"model": "hexcasting:item/focus_list_sealed"
},
{
"predicate": {
"hexcasting:datatype": 105.99

View file

@ -0,0 +1,17 @@
package at.petrak.hexcasting.api
import net.minecraft.world.phys.Vec3
data class ParticleSpray(val pos: Vec3, val vel: Vec3, val fuzziness: Double, val spread: Double) {
companion object {
@JvmStatic
fun Burst(pos: Vec3, size: Double): ParticleSpray {
return ParticleSpray(pos, Vec3(size, 0.0, 0.0), 0.0, 6.28)
}
@JvmStatic
fun Cloud(pos: Vec3, size: Double): ParticleSpray {
return ParticleSpray(pos, Vec3.ZERO, size, 0.0)
}
}
}

View file

@ -3,28 +3,31 @@ package at.petrak.hexcasting.api
import at.petrak.hexcasting.common.casting.CastException
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.OperatorSideEffect
import net.minecraft.world.phys.Vec3
interface SpellOperator : Operator {
val argc: Int
fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>>
fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>>
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
if (this.argc > stack.size)
throw CastException(CastException.Reason.NOT_ENOUGH_ARGS, this.argc, stack.size)
val args = stack.takeLast(this.argc)
for (_i in 0 until this.argc) stack.removeLast()
val (spell, mana, particlePoses) = this.execute(args, ctx)
val (spell, mana, particles) = this.execute(args, ctx)
val sideEffects = mutableListOf(
OperatorSideEffect.ConsumeMana(mana),
OperatorSideEffect.AttemptSpell(spell, this.isGreat)
)
for (pos in particlePoses) {
sideEffects.add(OperatorSideEffect.Particles(pos))
for (spray in particles) {
sideEffects.add(OperatorSideEffect.Particles(spray))
}
return OperationResult(stack, sideEffects)
}
}

View file

@ -2,16 +2,15 @@ package at.petrak.hexcasting.client;
import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.api.SpellDatum;
import at.petrak.hexcasting.client.particles.ConjureParticle;
import at.petrak.hexcasting.common.blocks.HexBlocks;
import at.petrak.hexcasting.common.items.HexItems;
import at.petrak.hexcasting.common.items.ItemFocus;
import at.petrak.hexcasting.common.items.ItemScroll;
import at.petrak.hexcasting.common.items.magic.ItemManaBattery;
import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell;
import at.petrak.hexcasting.common.particles.ConjureParticle;
import at.petrak.hexcasting.common.particles.HexParticles;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.ParticleEngine;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.item.ItemProperties;
@ -89,8 +88,9 @@ public class RegisterClientStuff {
@SubscribeEvent(priority = EventPriority.LOWEST)
public static void registerParticles(ParticleFactoryRegisterEvent event) {
ParticleEngine particleManager = Minecraft.getInstance().particleEngine;
particleManager.register(HexParticles.CONJURE_BLOCK_PARTICLE.get(), ConjureParticle.BlockProvider::new);
particleManager.register(HexParticles.CONJURE_LIGHT_PARTICLE.get(), ConjureParticle.LightProvider::new);
// does whatever a particle can
var particleMan = Minecraft.getInstance().particleEngine;
particleMan.register(HexParticles.LIGHT_PARTICLE.get(), ConjureParticle.Provider::new);
particleMan.register(HexParticles.CONJURE_PARTICLE.get(), ConjureParticle.Provider::new);
}
}

View file

@ -0,0 +1,129 @@
package at.petrak.hexcasting.client.particles;
import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.common.particles.ConjureParticleOptions;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.particle.*;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.util.FastColor;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL11;
import java.util.Random;
public class ConjureParticle extends TextureSheetParticle {
private static final Random RANDOM = new Random();
private final SpriteSet sprites;
private final boolean light;
ConjureParticle(ClientLevel pLevel, double x, double y, double z, double dx, double dy, double dz,
SpriteSet pSprites, int color, boolean light) {
super(pLevel, x, y, z, dx, dy, dz);
this.light = light;
this.quadSize *= light ? 0.9f : 0.75f;
this.setParticleSpeed(dx, dy, dz);
var r = FastColor.ARGB32.red(color);
var g = FastColor.ARGB32.green(color);
var b = FastColor.ARGB32.blue(color);
var a = FastColor.ARGB32.alpha(color);
this.setColor(r / 255f, g / 255f, b / 255f);
this.setAlpha(a / 255f);
this.friction = 0.96F;
this.gravity = light ? -0.01F : 0F;
this.speedUpWhenYMotionIsBlocked = true;
this.sprites = pSprites;
this.roll = RANDOM.nextFloat(360);
this.oRoll = this.roll;
this.lifetime = (int) ((light ? 64.0D : 32.0D) / ((Math.random() + 3f) * 0.25f));
this.hasPhysics = false;
this.setSpriteFromAge(pSprites);
}
public @NotNull ParticleRenderType getRenderType() {
return this.light ? I_STOLE_THIS_FROM_PSI : ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT;
}
public void tick() {
super.tick();
this.setSpriteFromAge(this.sprites);
if (light) {
this.quadSize *= 0.96f;
this.alpha = 1.0f - ((float) this.age / (float) this.lifetime);
}
}
public void setSpriteFromAge(@NotNull SpriteSet pSprite) {
if (!this.removed) {
int age = this.age * 4;
if (age > this.lifetime) {
age /= 4;
}
this.setSprite(pSprite.get(age, this.lifetime));
}
}
@OnlyIn(Dist.CLIENT)
public static class Provider implements ParticleProvider<ConjureParticleOptions> {
private final SpriteSet sprite;
public Provider(SpriteSet pSprites) {
this.sprite = pSprites;
}
@Nullable
@Override
public Particle createParticle(ConjureParticleOptions type, ClientLevel level,
double pX, double pY, double pZ,
double pXSpeed, double pYSpeed, double pZSpeed) {
return new ConjureParticle(level, pX, pY, pZ, pXSpeed, pYSpeed, pZSpeed, this.sprite, type.color(),
type.isLight());
}
}
// pretty sure this prevents the gross culling
// https://github.com/VazkiiMods/Psi/blob/1.18/src/main/java/vazkii/psi/client/fx/FXWisp.java
private static final ParticleRenderType I_STOLE_THIS_FROM_PSI = new ParticleRenderType() {
@Override
public void begin(BufferBuilder buf, TextureManager texMan) {
RenderSystem.depthMask(false);
RenderSystem.enableBlend();
RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES);
texMan.bindForSetup(TextureAtlas.LOCATION_PARTICLES);
texMan.getTexture(TextureAtlas.LOCATION_PARTICLES).setFilter(true, false);
buf.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE);
}
@Override
public void end(Tesselator tess) {
tess.end();
Minecraft.getInstance()
.getTextureManager()
.getTexture(TextureAtlas.LOCATION_PARTICLES)
.restoreLastBlurMipmap();
RenderSystem.disableBlend();
RenderSystem.depthMask(true);
}
@Override
public String toString() {
return HexMod.MOD_ID + ":conjure";
}
};
}

View file

@ -44,7 +44,7 @@ public class BlockConjured extends Block implements SimpleWaterloggedBlock, Enti
@Override
public void stepOn(Level pLevel, @NotNull BlockPos pPos, @NotNull BlockState pState, @NotNull Entity pEntity) {
BlockEntity tile = pLevel.getBlockEntity(pPos);
if (tile instanceof BlockEntityConjured bec) {
if (tile instanceof BlockEntityConjured bec && !bec.getBlockState().getValue(LIGHT)) {
bec.walkParticle(pEntity);
}
super.stepOn(pLevel, pPos, pState, pEntity);

View file

@ -1,12 +1,11 @@
package at.petrak.hexcasting.common.blocks;
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer;
import at.petrak.hexcasting.common.particles.HexParticles;
import at.petrak.hexcasting.common.particles.ConjureParticleOptions;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.util.FastColor;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -33,14 +32,12 @@ public class BlockEntityConjured extends BlockEntity {
int color = this.colorizer.getColor(pEntity.tickCount, pEntity.position()
.add(new Vec3(RANDOM.nextFloat(), RANDOM.nextFloat(), RANDOM.nextFloat()).scale(
RANDOM.nextFloat() * 3)));
int r = FastColor.ARGB32.red(color);
int g = FastColor.ARGB32.green(color);
int b = FastColor.ARGB32.blue(color);
assert level != null;
level.addParticle(HexParticles.CONJURE_BLOCK_PARTICLE.get(),
level.addParticle(new ConjureParticleOptions(color, false),
pEntity.getX() + (RANDOM.nextFloat() * 0.6D) - 0.3D,
getBlockPos().getY() + (RANDOM.nextFloat() * 0.05D) + 0.95D,
pEntity.getZ() + (RANDOM.nextFloat() * 0.6D) - 0.3D, r / 255d, g / 255d, b / 255d);
pEntity.getZ() + (RANDOM.nextFloat() * 0.6D) - 0.3D,
0.0, 0.0, 0.0);
}
}
}
@ -50,21 +47,20 @@ public class BlockEntityConjured extends BlockEntity {
int color = this.colorizer.getColor(RANDOM.nextFloat() * 16384,
new Vec3(RANDOM.nextFloat(), RANDOM.nextFloat(), RANDOM.nextFloat()).scale(
RANDOM.nextFloat() * 3));
double r = FastColor.ARGB32.red(color) / 255d;
double g = FastColor.ARGB32.green(color) / 255d;
double b = FastColor.ARGB32.blue(color) / 255d;
assert level != null;
if (getBlockState().getValue(BlockConjured.LIGHT)) {
level.addParticle(HexParticles.CONJURE_LIGHT_PARTICLE.get(),
level.addParticle(new ConjureParticleOptions(color, true),
(double) getBlockPos().getX() + 0.4D + (RANDOM.nextFloat() * 0.2D),
(double) getBlockPos().getY() + 0.4D + (RANDOM.nextFloat() * 0.2D),
(double) getBlockPos().getZ() + 0.4D + (RANDOM.nextFloat() * 0.2D), r, g, b);
(double) getBlockPos().getZ() + 0.4D + (RANDOM.nextFloat() * 0.2D),
0.0, 0.0, 0.0);
} else {
if (RANDOM.nextBoolean()) {
level.addParticle(HexParticles.CONJURE_BLOCK_PARTICLE.get(),
if (RANDOM.nextFloat() < 0.7) {
level.addParticle(new ConjureParticleOptions(color, false),
(double) getBlockPos().getX() + RANDOM.nextFloat(),
(double) getBlockPos().getY() + RANDOM.nextFloat(),
(double) getBlockPos().getZ() + RANDOM.nextFloat(), r, g, b);
(double) getBlockPos().getZ() + RANDOM.nextFloat(),
0.0, 0.0, 0.0);
}
}
}
@ -76,14 +72,12 @@ public class BlockEntityConjured extends BlockEntity {
int color = this.colorizer.getColor(entity.tickCount, entity.position()
.add(new Vec3(RANDOM.nextFloat(), RANDOM.nextFloat(), RANDOM.nextFloat()).scale(
RANDOM.nextFloat() * 3)));
double r = FastColor.ARGB32.red(color) / 255d;
double g = FastColor.ARGB32.green(color) / 255d;
double b = FastColor.ARGB32.blue(color) / 255d;
assert level != null;
level.addParticle(HexParticles.CONJURE_BLOCK_PARTICLE.get(),
level.addParticle(new ConjureParticleOptions(color, false),
entity.getX() + (RANDOM.nextFloat() * 0.8D) - 0.2D,
getBlockPos().getY() + (RANDOM.nextFloat() * 0.05D) + 0.95D,
entity.getZ() + (RANDOM.nextFloat() * 0.8D) - 0.2D, r, g, b);
entity.getZ() + (RANDOM.nextFloat() * 0.8D) - 0.2D,
0.0, 0.0, 0.0);
}
}
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.PatternRegistry
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer
@ -65,7 +66,16 @@ class CastingHarness private constructor(
val (stack2, sideEffectsUnmut) = operator.operate(this.stack.toMutableList(), this.ctx)
// Stick a poofy particle effect at the caster position
val sideEffects = sideEffectsUnmut.toMutableList()
sideEffects.add(OperatorSideEffect.Particles(this.ctx.position))
sideEffects.add(
OperatorSideEffect.Particles(
ParticleSpray(
this.ctx.caster.eyePosition.add(this.ctx.caster.lookAngle.scale(0.5)),
this.ctx.caster.lookAngle,
0.0,
0.1
)
)
)
val fd = this.getFunctionalData().copy(
stack = stack2,

View file

@ -1,20 +1,15 @@
package at.petrak.hexcasting.common.casting
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.common.particles.HexParticles
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgCastParticleAck
import at.petrak.hexcasting.datagen.Advancements
import com.mojang.math.Quaternion
import com.mojang.math.Vector3f
import net.minecraft.Util
import net.minecraft.network.chat.TextComponent
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.util.Mth
import net.minecraft.world.phys.Vec3
import kotlin.math.acos
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
import net.minecraftforge.network.PacketDistributor
import kotlin.random.Random
import kotlin.random.nextInt
@ -46,7 +41,7 @@ sealed class OperatorSideEffect {
override fun performEffect(harness: CastingHarness): Boolean {
val overcastOk = harness.ctx.canOvercast
val leftoverMana = harness.withdrawMana(this.amount, overcastOk)
if (leftoverMana > 0 && overcastOk) {
if (leftoverMana > 0 && !overcastOk) {
harness.ctx.caster.sendMessage(
TranslatableComponent("hexcasting.message.cant_overcast"),
Util.NIL_UUID
@ -56,43 +51,21 @@ sealed class OperatorSideEffect {
}
}
data class Particles(val position: Vec3, val velocity: Vec3, val fuzziness: Double, val spread: Double) :
data class Particles(val spray: ParticleSpray) :
OperatorSideEffect() {
override fun performEffect(harness: CastingHarness): Boolean {
val colorizer = harness.getColorizer()
val pos = spray.pos
for (i in 0 until 20) {
// For the colors, pick any random time to get a mix of colors
val color = colorizer.getColor(Random.nextFloat() * 256f, Vec3.ZERO)
// https://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d
fun randomInCircle(maxTh: Double = Mth.TWO_PI.toDouble()): Vec3 {
val th = Random.nextDouble(0.0, maxTh)
val z = Random.nextDouble(-1.0, 1.0)
return Vec3(sqrt(1.0 - z * z) * cos(th), sqrt(1.0 - z * z) * sin(th), z)
}
val offset = randomInCircle().scale(fuzziness)
val pos = position.add(offset)
// https://math.stackexchange.com/questions/56784/generate-a-random-direction-within-a-cone
val northCone = randomInCircle(spread)
val velNorm = velocity.normalize()
val zp = Vec3(0.0, 0.0, 1.0)
val rotAxis = velNorm.cross(zp)
val th = acos(velNorm.dot(zp))
val dagn = Quaternion(Vector3f(rotAxis), th.toFloat(), false)
val velf = Vector3f(northCone)
velf.transform(dagn)
val vel = Vec3(velf).scale(velocity.length())
// TODO: this doesn't work because xyz velocity is a lie
harness.ctx.world.addParticle(
HexParticles.CONJURE_BLOCK_PARTICLE.get(),
pos.x, pos.y, pos.z,
vel.x, vel.y, vel.z,
HexMessages.getNetwork().send(PacketDistributor.NEAR.with {
PacketDistributor.TargetPoint(
pos.x,
pos.y,
pos.z,
128.0 * 128.0,
harness.ctx.world.dimension()
)
}
}, MsgCastParticleAck(spray, colorizer))
return false
}
@ -113,7 +86,7 @@ sealed class OperatorSideEffect {
else -> {}
}
return false
return true
}
}
}

View file

@ -81,8 +81,7 @@ public record FrozenColorizer(Item item, UUID owner) {
int baseIdx = Mth.floor(fIdx);
float tRaw = fIdx - baseIdx;
// float t = -(float) (Math.cbrt(Mth.cos(tRaw * Mth.PI)) / 2) + 0.5f;
float t = tRaw;
float t = tRaw < 0.5 ? 4 * tRaw * tRaw * tRaw : (float) (1 - Math.pow(-2 * tRaw + 2, 3) / 2);
int start = colors[baseIdx % colors.length];
int end = colors[(baseIdx + 1) % colors.length];

View file

@ -1,16 +1,19 @@
package at.petrak.hexcasting.common.casting.operators
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.items.ItemDataHolder
import net.minecraft.world.phys.Vec3
// we make this a spell cause imo it's a little ... anticlimactic for it to just make no noise
object OpWrite : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
return Triple(
Spell(args[0]),
0,

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -16,13 +17,23 @@ object OpAddMotion : SpellOperator {
override val argc: Int
get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Entity>(0)
val motion = args.getChecked<Vec3>(1)
return Triple(
Spell(target, motion),
(motion.lengthSqr() * 10_000f).toInt(),
listOf(target.position()),
listOf(
ParticleSpray(
target.position().add(0.0, target.eyeHeight / 2.0, 0.0),
motion.normalize(),
0.0,
0.1
)
),
)
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -16,7 +17,10 @@ import kotlin.math.roundToInt
object OpBlink : SpellOperator {
override val argc = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Entity>(0)
val delta = args.getChecked<Double>(1)
@ -25,10 +29,12 @@ object OpBlink : SpellOperator {
ctx.assertVecInRange(target.position())
ctx.assertVecInRange(target.position().add(dvec))
val targetMiddlePos = target.position().add(0.0, target.eyeHeight / 2.0, 0.0)
return Triple(
Spell(target, delta),
50_000 * delta.roundToInt(),
listOf(target.position().add(dvec))
listOf(ParticleSpray.Cloud(targetMiddlePos, 2.0), ParticleSpray.Burst(targetMiddlePos.add(dvec), 2.0))
)
}

View file

@ -2,6 +2,7 @@ package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -14,13 +15,18 @@ object OpBreakBlock : SpellOperator {
override val argc: Int
get() = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val pos = args.getChecked<Vec3>(0)
ctx.assertVecInRange(pos)
val centered = Vec3.atCenterOf(BlockPos(pos))
return Triple(
Spell(pos),
20_000,
listOf(pos)
listOf(ParticleSpray.Burst(centered, 1.0))
)
}

View file

@ -1,5 +1,6 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -8,13 +9,15 @@ import at.petrak.hexcasting.common.casting.colors.FrozenColorizer
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgColorizerUpdateAck
import net.minecraft.world.phys.Vec3
import net.minecraftforge.network.PacketDistributor
object OpColorize : SpellOperator {
override val argc = 0
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
return Triple(
Spell,
10_000,

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -13,14 +14,17 @@ import net.minecraft.world.phys.Vec3
class OpConjure(val light: Boolean) : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target, light),
10_000,
listOf(target)
listOf(ParticleSpray.Cloud(Vec3.atCenterOf(BlockPos(target)), 1.0))
)
}

View file

@ -2,6 +2,7 @@ package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -13,14 +14,17 @@ import net.minecraft.world.phys.Vec3
object OpCreateWater : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
10_000,
listOf(target)
listOf(ParticleSpray.Burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
)
}

View file

@ -1,10 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator
import at.petrak.hexcasting.api.*
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
@ -22,14 +19,17 @@ import net.minecraft.world.phys.Vec3
object OpDestroyWater : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
200_000,
listOf(target)
listOf(ParticleSpray.Burst(target, 3.0))
)
}

View file

@ -1,17 +1,20 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastException
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell
import net.minecraft.world.phys.Vec3
class OpErase : SpellOperator {
override val argc = 0
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
if (otherHandItem.item !is ItemPackagedSpell) {
throw CastException(CastException.Reason.BAD_OFFHAND_ITEM, ItemPackagedSpell::class.java, otherHandItem)

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -13,14 +14,17 @@ class OpExplode(val fire: Boolean) : SpellOperator {
override val argc: Int
get() = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val pos = args.getChecked<Vec3>(0)
val strength = args.getChecked<Double>(1)
ctx.assertVecInRange(pos)
return Triple(
Spell(pos, strength, this.fire),
((1 + strength + if (this.fire) 2 else 0) * 50_000.0).toInt(),
listOf(pos)
listOf(ParticleSpray.Burst(pos, strength))
)
}

View file

@ -1,10 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator
import at.petrak.hexcasting.api.*
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
@ -21,14 +18,17 @@ import net.minecraft.world.phys.Vec3
object OpExtinguish : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
200_000,
listOf(target)
listOf(ParticleSpray.Burst(target, 1.0))
)
}
@ -48,37 +48,58 @@ object OpExtinguish : SpellOperator {
val distFromFocus =
ctx.caster.position().distanceToSqr(Vec3(here.x.toDouble(), here.y.toDouble(), here.z.toDouble()))
val distFromTarget =
target.distanceTo(Vec3(here.x.toDouble(), here.y.toDouble(), here.z.toDouble())) // max distance to prevent runaway shenanigans
target.distanceTo(
Vec3(
here.x.toDouble(),
here.y.toDouble(),
here.z.toDouble()
)
) // max distance to prevent runaway shenanigans
if (distFromFocus < Operator.MAX_DISTANCE * Operator.MAX_DISTANCE && seen.add(here) && distFromTarget < 10) {
// never seen this pos in my life
val blockstate = ctx.world.getBlockState(here)
val success =
when (blockstate.block) {
is BaseFireBlock -> {ctx.world.setBlock(here, Blocks.AIR.defaultBlockState(), 3); true}
is CampfireBlock -> {if (blockstate.getValue(CampfireBlock.LIT)) { // check if campfire is lit before putting it out
val wilson = Items.WOODEN_SHOVEL // summon shovel from the ether to do our bidding
val hereVec = (Vec3(here.x.toDouble(), here.y.toDouble(), here.z.toDouble()))
wilson.useOn(UseOnContext(ctx.world, null, InteractionHand.MAIN_HAND, ItemStack(wilson.asItem()), BlockHitResult(hereVec, Direction.UP, here, false))); true}
else false}
is AbstractCandleBlock -> {
if (blockstate.getValue(AbstractCandleBlock.LIT)) { // same check for candles
AbstractCandleBlock.extinguish(null, blockstate, ctx.world, here); true}
else false}
is NetherPortalBlock -> {ctx.world.setBlock(here, Blocks.AIR.defaultBlockState(), 3); true}
else -> false
}
when (blockstate.block) {
is BaseFireBlock -> {
ctx.world.setBlock(here, Blocks.AIR.defaultBlockState(), 3); true
}
is CampfireBlock -> {
if (blockstate.getValue(CampfireBlock.LIT)) { // check if campfire is lit before putting it out
val wilson = Items.WOODEN_SHOVEL // summon shovel from the ether to do our bidding
val hereVec = (Vec3(here.x.toDouble(), here.y.toDouble(), here.z.toDouble()))
wilson.useOn(
UseOnContext(
ctx.world,
null,
InteractionHand.MAIN_HAND,
ItemStack(wilson.asItem()),
BlockHitResult(hereVec, Direction.UP, here, false)
)
); true
} else false
}
is AbstractCandleBlock -> {
if (blockstate.getValue(AbstractCandleBlock.LIT)) { // same check for candles
AbstractCandleBlock.extinguish(null, blockstate, ctx.world, here); true
} else false
}
is NetherPortalBlock -> {
ctx.world.setBlock(here, Blocks.AIR.defaultBlockState(), 3); true
}
else -> false
}
if (success) {
ctx.world.sendParticles(
ParticleTypes.SMOKE,
here.x + 0.5 + Math.random() * 0.4 - 0.2,
here.y + 0.5 + Math.random() * 0.4 - 0.2,
here.z + 0.5 + Math.random() * 0.4 - 0.2,
2,
0.0,
0.05,
0.0,
0.0
ParticleTypes.SMOKE,
here.x + 0.5 + Math.random() * 0.4 - 0.2,
here.y + 0.5 + Math.random() * 0.4 - 0.2,
here.z + 0.5 + Math.random() * 0.4 - 0.2,
2,
0.0,
0.05,
0.0,
0.0
)
successes++
}

View file

@ -2,6 +2,7 @@ package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -18,14 +19,17 @@ import net.minecraft.world.phys.Vec3
object OpIgnite : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
10_000,
listOf(target)
listOf(ParticleSpray.Burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
)
}
@ -35,7 +39,15 @@ object OpIgnite : SpellOperator {
val maxwell = Items.FIRE_CHARGE
if (maxwell is FireChargeItem) {
// help
maxwell.useOn(UseOnContext(ctx.world, null, InteractionHand.MAIN_HAND, ItemStack(maxwell.asItem()), BlockHitResult(target, Direction.UP, BlockPos(target), false)))
maxwell.useOn(
UseOnContext(
ctx.world,
null,
InteractionHand.MAIN_HAND,
ItemStack(maxwell.asItem()),
BlockHitResult(target, Direction.UP, BlockPos(target), false)
)
)
} else {
HexMod.getLogger().warn("Items.FIRE_CHARGE wasn't a FireChargeItem?")
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -12,11 +13,13 @@ import at.petrak.hexcasting.common.items.magic.ItemManaHolder
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.phys.Vec3
object OpMakeBattery : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
if (otherHandItem.item != Items.GLASS_BOTTLE) {
throw CastException(CastException.Reason.BAD_OFFHAND_ITEM_ITEM, Items.GLASS_BOTTLE, otherHandItem)
@ -27,7 +30,7 @@ object OpMakeBattery : SpellOperator {
val entity = args.getChecked<ItemEntity>(0)
return Triple(Spell(entity), 100_000, listOf(entity.position()))
return Triple(Spell(entity), 100_000, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
}
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -11,11 +12,13 @@ import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell
import at.petrak.hexcasting.hexmath.HexPattern
import net.minecraft.nbt.ListTag
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.phys.Vec3
class OpMakePackagedSpell<T : ItemPackagedSpell>(val type: Class<T>, val cost: Int) : SpellOperator {
override val argc = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
if (!type.isAssignableFrom(otherHandItem.item.javaClass)) {
throw CastException(CastException.Reason.BAD_OFFHAND_ITEM, type, otherHandItem)
@ -24,7 +27,7 @@ class OpMakePackagedSpell<T : ItemPackagedSpell>(val type: Class<T>, val cost: I
val entity = args.getChecked<ItemEntity>(0)
val patterns = args.getChecked<List<SpellDatum<*>>>(1).map { it.tryGet<HexPattern>() }
return Triple(Spell(entity, patterns), cost, listOf(entity.position()))
return Triple(Spell(entity, patterns), cost, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
}
private data class Spell(val itemEntity: ItemEntity, val patterns: List<HexPattern>) : RenderedSpell {

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -22,13 +23,16 @@ object OpPlaceBlock : SpellOperator {
override val argc: Int
get() = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val pos = args.getChecked<Vec3>(0)
ctx.assertVecInRange(pos)
return Triple(
Spell(pos),
10_000,
listOf(pos)
listOf(ParticleSpray.Cloud(Vec3.atCenterOf(BlockPos(pos)), 1.0))
)
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -8,14 +9,16 @@ import at.petrak.hexcasting.common.casting.CastingContext
import net.minecraft.world.effect.MobEffect
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.phys.Vec3
import kotlin.math.max
class OpPotionEffect(val effect: MobEffect, val baseCost: Int, val potency: Boolean) : SpellOperator {
override val argc: Int
get() = if (this.potency) 3 else 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<LivingEntity>(0)
val duration = max(args.getChecked(1), 0.0)
val potency = if (this.potency)
@ -26,7 +29,7 @@ class OpPotionEffect(val effect: MobEffect, val baseCost: Int, val potency: Bool
return Triple(
Spell(effect, target, duration, potency),
cost.toInt(),
listOf(target.position())
listOf(ParticleSpray.Cloud(target.position().add(0.0, target.eyeHeight / 2.0, 0.0), 1.0))
)
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -10,11 +11,13 @@ import at.petrak.hexcasting.common.casting.ManaHelper
import at.petrak.hexcasting.common.items.magic.ItemManaHolder
import net.minecraft.util.Mth
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.phys.Vec3
object OpRecharge : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
if (otherHandItem.item !is ItemManaHolder) {
throw CastException(CastException.Reason.BAD_OFFHAND_ITEM, ItemManaHolder::class.java, otherHandItem)
@ -22,7 +25,7 @@ object OpRecharge : SpellOperator {
val entity = args.getChecked<ItemEntity>(0)
return Triple(Spell(entity), 100_000, listOf(entity.position()))
return Triple(Spell(entity), 100_000, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
}
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -17,14 +18,17 @@ import net.minecraft.world.phys.Vec3
object OpTheOnlyReasonAnyoneDownloadedPsi : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
10_000,
listOf(target)
listOf(ParticleSpray.Burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
)
}

View file

@ -2,6 +2,7 @@ package at.petrak.hexcasting.common.casting.operators.spells.great
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -14,14 +15,17 @@ import net.minecraft.world.phys.Vec3
object OpCreateLava : SpellOperator {
override val argc = 1
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
100_000,
listOf(target),
listOf(ParticleSpray.Burst(Vec3.atCenterOf(BlockPos(target)), 1.0)),
)
}

View file

@ -3,6 +3,7 @@ package at.petrak.hexcasting.common.casting.operators.spells.great
import at.petrak.hexcasting.HexUtils
import at.petrak.hexcasting.HexUtils.serializeToNBT
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -26,7 +27,10 @@ import kotlin.math.roundToInt
object OpFlight : SpellOperator {
override val argc = 3
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<ServerPlayer>(0)
val timeRaw = max(args.getChecked(1), 0.0)
val radiusRaw = max(args.getChecked(2), 0.0)
@ -36,7 +40,7 @@ object OpFlight : SpellOperator {
return Triple(
Spell(target, time, radiusRaw, ctx.position),
10_000 * (timeRaw * radiusRaw + 1.0).roundToInt(),
listOf()
listOf(ParticleSpray(target.position(), Vec3(0.0, 2.0, 0.0), 0.0, 0.1))
)
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells.great
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -13,13 +14,16 @@ object OpLightning : SpellOperator {
override val argc = 1
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target),
150_000,
listOf(target)
listOf(ParticleSpray(target.add(0.0, 2.0, 0.0), Vec3(0.0, -1.0, 0.0), 0.5, 0.1))
)
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells.great
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -15,15 +16,20 @@ import net.minecraftforge.network.PacketDistributor
object OpTeleport : SpellOperator {
override val argc = 2
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val teleportee = args.getChecked<Entity>(0)
val delta = args.getChecked<Vec3>(1)
ctx.assertVecInRange(teleportee.position())
val targetMiddlePos = teleportee.position().add(0.0, teleportee.eyeHeight / 2.0, 0.0)
return Triple(
Spell(teleportee, delta),
1_000_000,
listOf(teleportee.position().add(delta))
listOf(ParticleSpray.Cloud(targetMiddlePos, 2.0), ParticleSpray.Burst(targetMiddlePos.add(delta), 2.0))
)
}

View file

@ -1,19 +1,24 @@
package at.petrak.hexcasting.common.casting.operators.spells.great
import at.petrak.hexcasting.api.OperationResult
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.casting.OperatorSideEffect
import net.minecraft.world.phys.Vec3
class OpWeather(val rain: Boolean) : SpellOperator {
override val argc = 0
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
return Triple(Spell(rain), ((if (this.rain) 2 else 1) * 50_000), listOf()) // return an empty list for shits and gigs ig
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
return Triple(
Spell(rain),
((if (this.rain) 2 else 1) * 50_000),
listOf()
)
}
private data class Spell(val rain: Boolean) : RenderedSpell {
@ -24,20 +29,4 @@ class OpWeather(val rain: Boolean) : SpellOperator {
}
}
}
override fun operate(stack: MutableList<SpellDatum<*>>, ctx: CastingContext): OperationResult {
val args = stack.takeLast(this.argc)
for (_i in 0 until this.argc) stack.removeLast()
val (spell, mana, particlePoses) = this.execute(args, ctx)
val sideEffects = mutableListOf(
OperatorSideEffect.ConsumeMana(mana),
OperatorSideEffect.AttemptSpell(spell, this.isGreat)
)
for (pos in particlePoses) {
sideEffects.add(OperatorSideEffect.Particles(pos))
}
return OperationResult(stack, sideEffects)
}
}

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.common.casting.operators.spells.sentinel
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -13,14 +14,17 @@ import net.minecraftforge.network.PacketDistributor
class OpCreateSentinel(val extendsRange: Boolean) : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Triple(
Spell(target, this.extendsRange),
10_000,
listOf(target)
listOf(ParticleSpray.Burst(target, 2.0))
)
}

View file

@ -1,5 +1,6 @@
package at.petrak.hexcasting.common.casting.operators.spells.sentinel
import at.petrak.hexcasting.api.ParticleSpray
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
@ -7,16 +8,22 @@ import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.lib.HexCapabilities
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgSentinelStatusUpdateAck
import net.minecraft.world.phys.Vec3
import net.minecraftforge.network.PacketDistributor
object OpDestroySentinel : SpellOperator {
override val argc = 0
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Triple<RenderedSpell, Int, List<Vec3>> {
override fun execute(
args: List<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val particles = mutableListOf<ParticleSpray>()
val maybeCap = ctx.caster.getCapability(HexCapabilities.SENTINEL).resolve()
maybeCap.ifPresent { particles.add(ParticleSpray.Cloud(it.position, 2.0)) }
return Triple(
Spell,
1_000,
listOf()
particles
)
}

View file

@ -37,5 +37,7 @@ public class HexMessages {
MsgSentinelStatusUpdateAck::deserialize, MsgSentinelStatusUpdateAck::handle);
NETWORK.registerMessage(messageIdx++, MsgColorizerUpdateAck.class, MsgColorizerUpdateAck::serialize,
MsgColorizerUpdateAck::deserialize, MsgColorizerUpdateAck::handle);
NETWORK.registerMessage(messageIdx++, MsgCastParticleAck.class, MsgCastParticleAck::serialize,
MsgCastParticleAck::deserialize, MsgCastParticleAck::handle);
}
}

View file

@ -0,0 +1,94 @@
package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.ParticleSpray;
import at.petrak.hexcasting.common.casting.colors.FrozenColorizer;
import at.petrak.hexcasting.common.particles.ConjureParticleOptions;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkEvent;
import java.util.Random;
import java.util.function.Supplier;
/**
* Sent server->client to spray particles everywhere.
*/
public record MsgCastParticleAck(ParticleSpray spray, FrozenColorizer colorizer) {
private static final Random RANDOM = new Random();
public static MsgCastParticleAck deserialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
var posX = buf.readDouble();
var posY = buf.readDouble();
var posZ = buf.readDouble();
var velX = buf.readDouble();
var velY = buf.readDouble();
var velZ = buf.readDouble();
var fuzziness = buf.readDouble();
var spread = buf.readDouble();
var tag = buf.readAnySizeNbt();
var colorizer = FrozenColorizer.deserialize(tag);
return new MsgCastParticleAck(
new ParticleSpray(new Vec3(posX, posY, posZ), new Vec3(velX, velY, velZ), fuzziness, spread), colorizer);
}
public void serialize(ByteBuf buffer) {
var buf = new FriendlyByteBuf(buffer);
buf.writeDouble(this.spray.getPos().x);
buf.writeDouble(this.spray.getPos().y);
buf.writeDouble(this.spray.getPos().z);
buf.writeDouble(this.spray.getVel().x);
buf.writeDouble(this.spray.getVel().y);
buf.writeDouble(this.spray.getVel().z);
buf.writeDouble(this.spray.getFuzziness());
buf.writeDouble(this.spray.getSpread());
buf.writeNbt(this.colorizer.serialize());
}
public void handle(Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() ->
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
for (int i = 0; i < 20; i++) {
// For the colors, pick any random time to get a mix of colors
var color = colorizer.getColor(RANDOM.nextFloat() * 256f, Vec3.ZERO);
var offset = randomInCircle(Mth.TWO_PI).scale(spray.getSpread());
var pos = spray.getPos().add(offset);
// https://math.stackexchange.com/questions/56784/generate-a-random-direction-within-a-cone
var northCone = randomInCircle(spray.getSpread());
var velNorm = spray.getVel().normalize();
var zp = new Vec3(0.0, 0.0, 1.0);
var rotAxis = velNorm.cross(zp);
var th = Math.acos(velNorm.dot(zp));
var dagn = new Quaternion(new Vector3f(rotAxis), (float) th, false);
var velf = new Vector3f(northCone);
velf.transform(dagn);
var vel = new Vec3(velf).scale(spray.getVel().length());
Minecraft.getInstance().level.addParticle(
new ConjureParticleOptions(color, false),
pos.x, pos.y, pos.z,
vel.x, vel.y, vel.z
);
}
})
);
ctx.get().setPacketHandled(true);
}
// https://math.stackexchange.com/questions/44689/how-to-find-a-random-axis-or-unit-vector-in-3d
private static Vec3 randomInCircle(double maxTh) {
var th = RANDOM.nextDouble(0.0, maxTh + 0.001);
var z = RANDOM.nextDouble(-1.0, 1.0);
return new Vec3(Math.sqrt(1.0 - z * z) * Math.cos(th), Math.sqrt(1.0 - z * z) * Math.sin(th), z);
}
}

View file

@ -1,83 +0,0 @@
package at.petrak.hexcasting.common.particles;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.particle.*;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
public class ConjureParticle extends TextureSheetParticle {
private static final Random RANDOM = new Random();
private final SpriteSet sprites;
private final boolean light;
ConjureParticle(ClientLevel pLevel, double pX, double pY, double pZ, SpriteSet pSprites, boolean light) {
super(pLevel, pX, pY, pZ, (0.5D - RANDOM.nextDouble()) * .002, 0, (0.5D - RANDOM.nextDouble()) * .002);
this.friction = 0.96F;
this.gravity = light ? -0.01F : 0F;
this.speedUpWhenYMotionIsBlocked = true;
this.sprites = pSprites;
this.yd *= 0F;
this.xd *= light ? 0.001f : 0.1F;
this.zd *= light ? 0.001f : 0.1F;
this.roll = RANDOM.nextFloat(360);
this.oRoll = this.roll;
this.light = light;
this.quadSize *= light ? 0.9f : 0.75f;
this.lifetime = (int) ((light ? 64.0D : 32.0D) / ((Math.random() + 3f) * 0.25f));
this.hasPhysics = false;
this.setSpriteFromAge(pSprites);
}
public @NotNull ParticleRenderType getRenderType() {
return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT;
}
public void tick() {
super.tick();
this.setSpriteFromAge(this.sprites);
if (light) this.quadSize *= 0.96f;
}
public void setSpriteFromAge(@NotNull SpriteSet pSprite) {
if (!this.removed) {
int age = this.age * 4;
if (age > this.lifetime) age /= 4;
this.setSprite(pSprite.get(age, this.lifetime));
}
}
@OnlyIn(Dist.CLIENT)
public static class BlockProvider implements ParticleProvider<SimpleParticleType> {
private final SpriteSet sprite;
public BlockProvider(SpriteSet pSprites) {
this.sprite = pSprites;
}
public Particle createParticle(@NotNull SimpleParticleType pType, @NotNull ClientLevel pLevel, double pX, double pY, double pZ, double pXSpeed, double pYSpeed, double pZSpeed) {
ConjureParticle particle = new ConjureParticle(pLevel, pX, pY, pZ, this.sprite, false);
particle.setColor((float) pXSpeed, (float) pYSpeed, (float) pZSpeed);
return particle;
}
}
@OnlyIn(Dist.CLIENT)
public static class LightProvider implements ParticleProvider<SimpleParticleType> {
private final SpriteSet sprite;
public LightProvider(SpriteSet pSprites) {
this.sprite = pSprites;
}
public Particle createParticle(@NotNull SimpleParticleType pType, @NotNull ClientLevel pLevel, double pX, double pY, double pZ, double pXSpeed, double pYSpeed, double pZSpeed) {
ConjureParticle particle = new ConjureParticle(pLevel, pX, pY, pZ, this.sprite, true);
particle.setColor((float) pXSpeed, (float) pYSpeed, (float) pZSpeed);
return particle;
}
}
}

View file

@ -0,0 +1,71 @@
package at.petrak.hexcasting.common.particles;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleType;
import net.minecraft.network.FriendlyByteBuf;
import java.util.Locale;
public record ConjureParticleOptions(int color, boolean isLight) implements ParticleOptions {
@Override
public ParticleType<?> getType() {
return (this.isLight ? HexParticles.LIGHT_PARTICLE : HexParticles.CONJURE_PARTICLE).get();
}
@Override
public void writeToNetwork(FriendlyByteBuf buf) {
buf.writeInt(this.color);
buf.writeBoolean(this.isLight);
}
@Override
public String writeToString() {
return String.format(Locale.ROOT, "%s %s", this.color, this.isLight);
}
public static final Deserializer<ConjureParticleOptions> DESERIALIZER = new Deserializer<>() {
@Override
public ConjureParticleOptions fromCommand(ParticleType<ConjureParticleOptions> type,
StringReader reader) throws CommandSyntaxException {
reader.expect(' ');
var color = reader.readInt();
reader.expect(' ');
var isLight = reader.readBoolean();
return new ConjureParticleOptions(color, isLight);
}
@Override
public ConjureParticleOptions fromNetwork(ParticleType<ConjureParticleOptions> type,
FriendlyByteBuf buf) {
var col = buf.readInt();
var isLight = buf.readBoolean();
return new ConjureParticleOptions(col, isLight);
}
};
public static class Type extends ParticleType<ConjureParticleOptions> {
public Type(boolean pOverrideLimiter) {
super(pOverrideLimiter, DESERIALIZER);
}
public static final Codec<ConjureParticleOptions> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
Codec.INT.fieldOf("color")
.forGetter((ConjureParticleOptions o) -> o.color),
Codec.BOOL.fieldOf("isLight").forGetter(ConjureParticleOptions::isLight)
)
.apply(instance, ConjureParticleOptions::new)
);
@Override
public Codec<ConjureParticleOptions> codec() {
return CODEC;
}
}
}

View file

@ -1,23 +1,17 @@
package at.petrak.hexcasting.common.particles;
import at.petrak.hexcasting.HexMod;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.FlameParticle;
import net.minecraft.client.particle.ParticleEngine;
import net.minecraft.core.particles.ParticleType;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ParticleFactoryRegisterEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
public class HexParticles {
public static final DeferredRegister<ParticleType<?>> PARTICLES = DeferredRegister.create(ForgeRegistries.PARTICLE_TYPES, HexMod.MOD_ID);
public static final DeferredRegister<ParticleType<?>> PARTICLES = DeferredRegister.create(
ForgeRegistries.PARTICLE_TYPES, HexMod.MOD_ID);
public static final RegistryObject<SimpleParticleType> CONJURE_LIGHT_PARTICLE = PARTICLES.register("conjure_light_particle", () -> new SimpleParticleType(true));
public static final RegistryObject<SimpleParticleType> CONJURE_BLOCK_PARTICLE = PARTICLES.register("conjure_block_particle", () -> new SimpleParticleType(true));
public static final RegistryObject<ConjureParticleOptions.Type> CONJURE_PARTICLE = PARTICLES.register(
"conjure_block_particle", () -> new ConjureParticleOptions.Type(false));
public static final RegistryObject<ConjureParticleOptions.Type> LIGHT_PARTICLE = PARTICLES.register(
"conjure_light_particle", () -> new ConjureParticleOptions.Type(false));
}

View file

@ -46,22 +46,22 @@ public class ItemModels extends ItemModelProvider {
String[] focusTypes = new String[]{
"empty", "entity", "double", "vec3", "widget", "list", "pattern"
};
for (int i = 0, stringsLength = focusTypes.length; i < stringsLength; i++) {
String type = focusTypes[i];
simpleItem(modLoc("focus_" + type));
simpleItem(modLoc("focus_" + type + "_sealed"));
getBuilder(HexItems.FOCUS.get().getRegistryName().getPath())
.override()
.predicate(ItemFocus.DATATYPE_PRED, -0.01f + i)
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_" + type)))
.end()
.override()
.predicate(ItemFocus.DATATYPE_PRED, -0.01f + 100 + i)
.model(new ModelFile.UncheckedModelFile(modLoc("item/focus_" + type + "_sealed")))
.end();
// For stupid bad reasons we need to do this in ascending order.
for (int sealedIdx = 0; sealedIdx <= 1; sealedIdx++) {
var sealed = sealedIdx == 1;
for (int i = 0, stringsLength = focusTypes.length; i < stringsLength; i++) {
var type = focusTypes[i];
var name = "focus_" + type + (sealed ? "_sealed" : "");
simpleItem(modLoc(name));
getBuilder(HexItems.FOCUS.get().getRegistryName().getPath())
.override()
.predicate(ItemFocus.DATATYPE_PRED, -0.01f + i + (sealed ? 100 : 0))
.model(new ModelFile.UncheckedModelFile(modLoc("item/" + name)))
.end();
}
}
Pair<RegistryObject<Item>, String>[] packagers = new Pair[]{
new Pair(HexItems.CYPHER, "cypher"),
new Pair(HexItems.TRINKET, "trinket"),

View file

@ -48,7 +48,7 @@
"item.hexcasting.uuid_colorizer": "Soulglimmer Pigment",
"block.hexcasting.conjured": "Conjured Block",
"itemGroup.hexcasting": "Hexcasting",
"hexcasting.subtitles.start_pattern": "Starting pattern",
"hexcasting.subtitles.add_line": "Adding line",
"hexcasting.subtitles.add_pattern": "Adding pattern",
@ -302,6 +302,7 @@
"hexcasting.page.focus2": "$(italic)Poison apples, poison worms./$",
"hexcasting.page.focus3": "It occurs to me that I could conceivably store a whole list of patterns in a $(item)Focus/$, then recall them and evaluate them with $(l:patterns/meta#OpEval)$(action)Hermes' Gambit/$. This way I can cast complex spells, or parts of spells, without having to draw them over and over.$(br2)I could use this like a slightly less convenient $(l:items/hexcasting#artifact)$(item)Artifact/$, but I think I could get much better dividends by putting common \"phrases\" in a $(item)Focus/$, like the patterns for figuring out where I'm looking.",
"hexcasting.page.focus4": "Also, if I store an entity in a $(item)Focus/$ and try to recall it after the referenced entity has died or otherwise disappeared, the $(action)Scribe's Reflection/$ will add Null to the stack instead.",
"hexcasting.page.focus5": "Finally, it seems if I wish to protect a focus from accidentally being overwritten, I can seal it with wax by crafting it with a $(item)Honeycomb/$. Attempting to use $(action)Scribe's Gambit/$ on a sealed focus will fail.",
"hexcasting.entry.hexcasting": "Hexcasting Items",
"hexcasting.page.hexcasting1": "Although the flexibility that casting _Hexes \"on the go\" with my $(l:items/staff)$(item)Staff/$ is quite helpful, it's a huge pain to have to wave it around repeatedly just to accomplish a common task. If I could save a common spell for later reuse, it would simplify things a lot-- and allow me to share my _Hexes with friends, too.",

View file

@ -1,26 +1,30 @@
{
"name": "hexcasting.entry.focus",
"icon": "hexcasting:focus",
"category": "hexcasting:items",
"sortnum": 2,
"advancement": "hexcasting:root",
"pages": [
{
"type": "patchouli:text",
"text": "hexcasting.page.focus1"
},
{
"type": "patchouli:crafting",
"recipe": "hexcasting:focus",
"text": "hexcasting.page.focus2"
},
{
"type": "patchouli:text",
"text": "hexcasting.page.focus3"
},
{
"type": "patchouli:text",
"text": "hexcasting.page.focus4"
}
]
"name": "hexcasting.entry.focus",
"icon": "hexcasting:focus",
"category": "hexcasting:items",
"sortnum": 2,
"advancement": "hexcasting:root",
"pages": [
{
"type": "patchouli:text",
"text": "hexcasting.page.focus1"
},
{
"type": "patchouli:crafting",
"recipe": "hexcasting:focus",
"text": "hexcasting.page.focus2"
},
{
"type": "patchouli:text",
"text": "hexcasting.page.focus3"
},
{
"type": "patchouli:text",
"text": "hexcasting.page.focus4"
},
{
"type": "patchouli:text",
"text": "hexcasting.page.focus5"
}
]
}