updated weed joint, plane crash and atomic bomb explosion

This commit is contained in:
2026-03-11 09:36:32 +01:00
parent 485b15082e
commit 20a755064b
16 changed files with 638 additions and 63 deletions

View File

@@ -2,17 +2,30 @@ package dev.tggamesyt.szar;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MovementType;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import java.util.List;
public class AtomEntity extends Entity {
private static final int NUKE_RADIUS = 100;
// vanilla clamps explosion power at 5 internally for block destruction,
// but we can still call createExplosion with higher values for damage/visuals
// the absolute max that does anything meaningful is around 200F
private static final float MAX_EXPLOSION_POWER = 200.0F;
private boolean armed = false;
private boolean wasFallingFast = false;
public AtomEntity(EntityType<?> type, World world) {
super(type, world);
}
@@ -28,7 +41,6 @@ public class AtomEntity extends Entity {
this.setVelocity(this.getVelocity().add(0, -0.08, 0));
}
// Track if it was falling fast enough to count as impact
if (this.getVelocity().y < -0.5) {
wasFallingFast = true;
}
@@ -36,68 +48,94 @@ public class AtomEntity extends Entity {
this.move(MovementType.SELF, this.getVelocity());
if (!getWorld().isClient) {
if (this.isOnFire()) armed = true;
// 🔥 If on fire, arm it
if (this.isOnFire()) {
armed = true;
}
// 💥 Explode only if:
// 1. It was falling fast OR
// 2. It is armed
if (this.isOnGround() && (wasFallingFast || armed)) {
explode();
this.discard();
}
}
}
private void explode() {
ServerWorld world = (ServerWorld) this.getWorld();
Vec3d center = this.getPos();
// Visual / sound explosion only
world.createExplosion(
this,
getX(),
getY(),
getZ(),
50.0F, // just visuals
World.ExplosionSourceType.TNT
);
world.createExplosion(this, center.x, center.y, center.z,
MAX_EXPLOSION_POWER, World.ExplosionSourceType.TNT);
clearSphere(world, this.getBlockPos(), NUKE_RADIUS);
int rings = 6;
for (int ring = 1; ring <= rings; ring++) {
double ringRadius = (NUKE_RADIUS / (double) rings) * ring;
float ringPower = MAX_EXPLOSION_POWER * (1.0F - (ring / (float) rings));
ringPower = Math.max(ringPower, 4.0F);
int pointsOnRing = 6 + ring * 4;
for (int i = 0; i < pointsOnRing; i++) {
double angle = (2 * Math.PI / pointsOnRing) * i;
double ex = center.x + Math.cos(angle) * ringRadius;
double ey = center.y;
double ez = center.z + Math.sin(angle) * ringRadius;
world.createExplosion(this, ex, ey, ez, ringPower, World.ExplosionSourceType.TNT);
}
}
// no clearSphere call anymore
spawnRadiationZones(world, center);
}
private void spawnRadiationZones(ServerWorld world, Vec3d center) {
int zoneCount = 40;
for (int wave = 0; wave < 3; wave++) {
for (int i = 0; i < zoneCount; i++) {
double angle = world.random.nextDouble() * 2 * Math.PI;
double dist = world.random.nextDouble() * NUKE_RADIUS;
double rx = center.x + Math.cos(angle) * dist;
double rz = center.z + Math.sin(angle) * dist;
double ry;
if (wave == 0) {
// wave 1: starts on ground, falls with gravity — spawn at ground level
ry = center.y + world.random.nextDouble() * 5;
} else if (wave == 1) {
// wave 2: mid air, will fall then float
ry = center.y + 10 + world.random.nextDouble() * 20;
} else {
// wave 3: high up, no gravity at all
ry = center.y + 30 + world.random.nextDouble() * 40;
}
float proximity = (float)(1.0 - (dist / NUKE_RADIUS));
RadiationAreaEntity rad = new RadiationAreaEntity(Szar.RADIATION_AREA, world);
rad.setPosition(rx, ry, rz);
rad.setLifetime((int)(1200 + proximity * 4800));
rad.setRadius(3.0F + proximity * 8.0F);
rad.setWave(wave);
world.spawnEntity(rad);
}
}
}
private void clearSphere(ServerWorld world, BlockPos center, int radius) {
int rSquared = radius * radius;
BlockPos.Mutable mutable = new BlockPos.Mutable();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
for (int z = -radius; z <= radius; z++) {
if (x * x + y * y + z * z > rSquared) continue;
mutable.set(
center.getX() + x,
center.getY() + y,
center.getZ() + z
);
// Skip void / out of world
mutable.set(center.getX() + x, center.getY() + y, center.getZ() + z);
if (!world.isInBuildLimit(mutable)) continue;
world.setBlockState(mutable, net.minecraft.block.Blocks.AIR.getDefaultState(), 3);
}
}
}
}
@Override
protected void readCustomDataFromNbt(NbtCompound nbt) {}
@Override
protected void writeCustomDataToNbt(NbtCompound nbt) {}
}
}

View File

@@ -165,6 +165,37 @@ public class Joint extends SpyglassItem {
// Optional: play inhale / stop sound
user.playSound(SoundEvents.ITEM_HONEY_BOTTLE_DRINK, 1.0F, 1.0F);
if (world.isClient) {
// get the direction the player is facing
double yawRad = Math.toRadians(user.getYaw());
// position in front of the player's face
double baseX = user.getX() - Math.sin(yawRad) * 0.5;
double baseY = user.getEyeY() - 0.1; // slightly below eye level (mouth)
double baseZ = user.getZ() + Math.cos(yawRad) * 0.5;
for (int p = 0; p < 8; p++) {
// randomize spread slightly
double offsetX = (RANDOM.nextDouble() - 0.5) * 0.15;
double offsetY = (RANDOM.nextDouble() - 0.5) * 0.1;
double offsetZ = (RANDOM.nextDouble() - 0.5) * 0.15;
// velocity: mostly upward, slight outward drift
double velX = (RANDOM.nextDouble() - 0.5) * 0.02;
double velY = 0.04 + RANDOM.nextDouble() * 0.03; // upward
double velZ = (RANDOM.nextDouble() - 0.5) * 0.02;
world.addParticle(
net.minecraft.particle.ParticleTypes.CAMPFIRE_COSY_SMOKE,
baseX + offsetX,
baseY + offsetY,
baseZ + offsetZ,
velX,
velY,
velZ
);
}
}
}
}

View File

@@ -4,23 +4,33 @@ import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.*;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.damage.DamageType;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class PlaneEntity extends Entity {
import java.util.List;
import static dev.tggamesyt.szar.Szar.MOD_ID;
public class PlaneEntity extends Entity {
public static final RegistryKey<DamageType> PLANE_CRASH =
RegistryKey.of(RegistryKeys.DAMAGE_TYPE, new Identifier(MOD_ID, "plane_crash"));
private static final TrackedData<Float> ENGINE_TARGET =
DataTracker.registerData(PlaneEntity.class, TrackedDataHandlerRegistry.FLOAT);
private static final TrackedData<Integer> DAMAGE_WOBBLE_TICKS =
@@ -193,7 +203,36 @@ public class PlaneEntity extends Entity {
boolean crash = (horizontalImpact > 1.5 && horizontalCollision) || (verticalImpact > explodeSpeed && verticalCollision);
if (crash) {
getWorld().createExplosion(this, getX(), getY(), getZ(), 9.0f, World.ExplosionSourceType.TNT);
PlayerEntity pilot = getControllingPassenger();
RegistryEntry<DamageType> planeCrashType =
getWorld().getRegistryManager()
.get(RegistryKeys.DAMAGE_TYPE)
.entryOf(PLANE_CRASH);
float explosionRadius = 9.0f;
List<Entity> targets = getWorld().getOtherEntities(this, getBoundingBox().expand(explosionRadius));
if (pilot != null) targets.add(pilot); // make sure pilot is included
for (Entity entity : targets) {
if (!(entity instanceof LivingEntity living)) continue;
double dist = entity.getPos().distanceTo(getPos());
if (dist > explosionRadius) continue;
double exposure = 1.0;
double intensity = (1.0 - (dist / explosionRadius)) * exposure;
float damage = (float)((intensity * intensity + intensity) / 2.0 * 7.0 * explosionRadius + 1.0);
DamageSource crashSource = pilot != null && entity != pilot
? new DamageSource(planeCrashType, pilot) // "X was killed when Pilot crashed"
: new DamageSource(planeCrashType); // pilot just gets "X crashed their plane"
living.damage(crashSource, damage);
}
getWorld().createExplosion(null, getX(), getY(), getZ(), explosionRadius, World.ExplosionSourceType.TNT);
remove(RemovalReason.KILLED);
return;
}

View File

@@ -0,0 +1,142 @@
package dev.tggamesyt.szar;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MovementType;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.DustParticleEffect;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.joml.Vector3f;
import java.util.List;
public class RadiationAreaEntity extends Entity {
private int lifetime = 2400;
private float radius = 5.0F;
private int age = 0;
private int wave = 0; // 0 = full gravity, 1 = gravity then float, 2 = no gravity
public RadiationAreaEntity(EntityType<?> type, World world) {
super(type, world);
this.noClip = true;
this.setNoGravity(false); // default on, wave logic overrides
}
public void setLifetime(int ticks) { this.lifetime = ticks; }
public void setRadius(float r) { this.radius = r; }
public void setWave(int wave) {
this.wave = wave;
// wave 2 starts with no gravity immediately
if (wave == 2) this.setNoGravity(true);
}
@Override
public void tick() {
super.tick();
age++;
// ── Gravity behaviour per wave ──────────────────────────────────
if (wave == 0) {
// full gravity always — entity falls and stays on ground
this.setNoGravity(false);
if (!getWorld().isClient) {
Vec3d vel = this.getVelocity().add(0, -0.04, 0).multiply(0.98);
this.setVelocity(vel);
this.move(MovementType.SELF, vel);
}
} else if (wave == 1) {
if (age <= 45) {
// first 45 ticks: fall with gravity
this.setNoGravity(false);
if (!getWorld().isClient) {
Vec3d vel = this.getVelocity().add(0, -0.04, 0).multiply(0.98);
this.setVelocity(vel);
this.move(MovementType.SELF, vel);
}
} else {
// after 45 ticks: float in place
this.setNoGravity(true);
this.setVelocity(this.getVelocity().multiply(0.1)); // bleed off remaining velocity
}
}
// wave 2: noGravity already set to true, no movement needed
if (!getWorld().isClient) {
if (age >= lifetime) {
this.discard();
return;
}
// apply radiation every second
if (age % 20 == 0) {
List<LivingEntity> nearby = getWorld().getEntitiesByClass(
LivingEntity.class,
new Box(getPos().subtract(radius, radius, radius),
getPos().add(radius, radius, radius)),
e -> e.squaredDistanceTo(this) < radius * radius
);
for (LivingEntity entity : nearby) {
entity.addStatusEffect(new StatusEffectInstance(
Szar.RADIATION, 200, 1, false, true, true));
}
}
// particles
ServerWorld serverWorld = (ServerWorld) getWorld();
for (int i = 0; i < 5; i++) {
double px = getX() + (getWorld().random.nextDouble() - 0.5) * radius * 2;
double py = getY() + getWorld().random.nextDouble() * 2;
double pz = getZ() + (getWorld().random.nextDouble() - 0.5) * radius * 2;
if (getPos().squaredDistanceTo(px, py, pz) > radius * radius) continue;
// green glowing dust particles
serverWorld.spawnParticles(
new DustParticleEffect(
new Vector3f(0.224f, 1.0f, 0.078f),
1.5F
),
px, py, pz, 1, 0, 0.02, 0, 0.0
);
// warped spore for extra green floaty effect
serverWorld.spawnParticles(ParticleTypes.WARPED_SPORE,
px, py, pz, 1, 0, 0.05, 0, 0.01);
// occasional smoke
if (getWorld().random.nextInt(10) == 0) {
serverWorld.spawnParticles(ParticleTypes.CAMPFIRE_COSY_SMOKE,
px, py + 1, pz, 1, 0, 0.05, 0, 0.01);
}
}
}
}
@Override
protected void initDataTracker() {}
@Override
protected void readCustomDataFromNbt(NbtCompound nbt) {
this.lifetime = nbt.getInt("Lifetime");
this.radius = nbt.getFloat("Radius");
this.age = nbt.getInt("Age");
this.wave = nbt.getInt("Wave");
}
@Override
protected void writeCustomDataToNbt(NbtCompound nbt) {
nbt.putInt("Lifetime", lifetime);
nbt.putFloat("Radius", radius);
nbt.putInt("Age", age);
nbt.putInt("Wave", wave);
}
}

View File

@@ -1032,6 +1032,13 @@ public class Szar implements ModInitializer {
new DrogEffect()
);
public static final StatusEffect ARRESTED = Registry.register(Registries.STATUS_EFFECT, new Identifier(MOD_ID, "arrested"), new ArrestedEffect());
public static final EntityType<RadiationAreaEntity> RADIATION_AREA = Registry.register(
Registries.ENTITY_TYPE,
new Identifier(MOD_ID, "radiation_area"),
FabricEntityTypeBuilder.create(SpawnGroup.MISC, RadiationAreaEntity::new)
.dimensions(EntityDimensions.fixed(0.5F, 0.5F))
.build()
);
public static final StatusEffect RADIATION = Registry.register(
Registries.STATUS_EFFECT,
new Identifier(MOD_ID, "radiation"),

View File

@@ -85,5 +85,8 @@
"block.szar.roulette": "Roulette",
"item.szar.firtana": "Firtana",
"item.szar.hello": "Music Disc",
"item.szar.hello.desc": "Alex Savage - OMFG - Hello (Dark Remix)"
"item.szar.hello.desc": "Alex Savage - OMFG - Hello (Dark Remix)",
"death.attack.plane_crash": "%1$s crashed their plane",
"death.attack.plane_crash.player": "%1$s was killed when %2$s crashed their plane"
}

View File

@@ -7,17 +7,51 @@
},
"elements": [
{
"from": [7, 0, 7],
"to": [9, 12, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 0, 7]},
"from": [7, 8.5, 7],
"to": [9, 13.5, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [7, 8.5, 7]},
"faces": {
"north": {"uv": [0, 0, 2, 12], "texture": "#0"},
"east": {"uv": [2, 0, 4, 12], "texture": "#0"},
"south": {"uv": [4, 0, 6, 12], "texture": "#0"},
"west": {"uv": [6, 0, 8, 12], "texture": "#0"},
"north": {"uv": [0, 0, 2, 5], "texture": "#0"},
"east": {"uv": [2, 0, 4, 5], "texture": "#0"},
"south": {"uv": [4, 0, 6, 5], "texture": "#0"},
"west": {"uv": [6, 0, 8, 5], "texture": "#0"},
"up": {"uv": [10, 2, 8, 0], "texture": "#0"}
}
},
{
"from": [6.9, 2.4, 6.9],
"to": [9.1, 8.6, 9.1],
"rotation": {"angle": 0, "axis": "y", "origin": [6.9, 2.4, 6.9]},
"faces": {
"north": {"uv": [0, 5, 2, 12], "texture": "#0"},
"east": {"uv": [2, 5, 4, 12], "texture": "#0"},
"south": {"uv": [4, 5, 6, 12], "texture": "#0"},
"west": {"uv": [6, 5, 8, 12], "texture": "#0"},
"up": {"uv": [10, 2, 8, 0], "texture": "#0"},
"down": {"uv": [10, 2, 8, 4], "texture": "#0"}
}
}
]
],
"gui_light": "front",
"display": {
"thirdperson_righthand": {
"translation": [0, -2, 0]
},
"ground": {
"rotation": [90, 0, 0]
},
"gui": {
"rotation": [-67.5, 0, 45],
"scale": [1.5, 1.5, 1.5]
},
"head": {
"rotation": [90, 0, 0],
"translation": [0, 0, -16],
"scale": [1.6, 1.6, 1.6]
},
"fixed": {
"translation": [0, 0, -1.5],
"scale": [1.5, 1.5, 1.5]
}
}
}

View File

@@ -0,0 +1,5 @@
{
"message_id": "plane_crash",
"scaling": "never",
"exhaustion": 0.0
}