Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
190 lines
5.6 KiB
Java
190 lines
5.6 KiB
Java
package com.tiedup.remake.entities;
|
|
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
|
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
|
import net.minecraft.network.syncher.SynchedEntityData;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.level.Level;
|
|
|
|
/**
|
|
* Lightweight fishing bobber entity spawned by NPCs during the FISH command.
|
|
*
|
|
* <p>Floats on the water surface with a sinusoidal bob animation.
|
|
* After a random delay, triggers a bite event (splash particles + sound).
|
|
* The owning NPC's goal checks {@link #isBiting()} each tick and reels in
|
|
* when a bite is detected.</p>
|
|
*
|
|
* <p>Ephemeral entity: no NBT persistence. Auto-discards after {@value #MAX_LIFE} ticks.</p>
|
|
*/
|
|
public class NpcFishingBobber extends Entity {
|
|
|
|
private static final EntityDataAccessor<Integer> DATA_OWNER_ID =
|
|
SynchedEntityData.defineId(
|
|
NpcFishingBobber.class,
|
|
EntityDataSerializers.INT
|
|
);
|
|
|
|
private static final EntityDataAccessor<Boolean> DATA_BITING =
|
|
SynchedEntityData.defineId(
|
|
NpcFishingBobber.class,
|
|
EntityDataSerializers.BOOLEAN
|
|
);
|
|
|
|
/** Safety removal after 30 seconds */
|
|
private static final int MAX_LIFE = 600;
|
|
|
|
/** Minimum ticks before a bite can occur */
|
|
private static final int MIN_BITE_TIME = 100;
|
|
|
|
/** Maximum ticks before a bite can occur */
|
|
private static final int MAX_BITE_TIME = 300;
|
|
|
|
private int biteTimer;
|
|
private int lifeTimer;
|
|
|
|
/**
|
|
* Registration constructor.
|
|
*/
|
|
public NpcFishingBobber(EntityType<?> type, Level level) {
|
|
super(type, level);
|
|
}
|
|
|
|
/**
|
|
* Factory method: creates a bobber at the surface of the given water block.
|
|
*
|
|
* @param level The server level
|
|
* @param owner The NPC that owns this bobber
|
|
* @param waterPos The water block position (bobber sits on top)
|
|
* @return A new NpcFishingBobber positioned at the water surface
|
|
*/
|
|
public static NpcFishingBobber create(
|
|
Level level,
|
|
EntityDamsel owner,
|
|
BlockPos waterPos
|
|
) {
|
|
NpcFishingBobber bobber = new NpcFishingBobber(
|
|
ModEntities.NPC_FISHING_BOBBER.get(),
|
|
level
|
|
);
|
|
bobber.setPos(
|
|
waterPos.getX() + 0.5,
|
|
waterPos.getY() + 1.0, // surface of water
|
|
waterPos.getZ() + 0.5
|
|
);
|
|
bobber.entityData.set(DATA_OWNER_ID, owner.getId());
|
|
bobber.biteTimer =
|
|
MIN_BITE_TIME +
|
|
owner.getRandom().nextInt(MAX_BITE_TIME - MIN_BITE_TIME);
|
|
return bobber;
|
|
}
|
|
|
|
@Override
|
|
protected void defineSynchedData() {
|
|
this.entityData.define(DATA_OWNER_ID, 0);
|
|
this.entityData.define(DATA_BITING, false);
|
|
}
|
|
|
|
@Override
|
|
protected void readAdditionalSaveData(CompoundTag tag) {
|
|
// Ephemeral entity - no persistence
|
|
}
|
|
|
|
@Override
|
|
protected void addAdditionalSaveData(CompoundTag tag) {
|
|
// Ephemeral entity - no persistence
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
|
|
lifeTimer++;
|
|
if (lifeTimer >= MAX_LIFE) {
|
|
discard();
|
|
return;
|
|
}
|
|
|
|
// Sinusoidal bob on water surface
|
|
setDeltaMovement(0, Math.sin(tickCount * 0.15) * 0.01, 0);
|
|
|
|
// Server-side bite logic
|
|
if (!level().isClientSide && !isBiting()) {
|
|
biteTimer--;
|
|
if (biteTimer <= 0) {
|
|
setBiting(true);
|
|
|
|
// Splash particles
|
|
if (level() instanceof ServerLevel serverLevel) {
|
|
serverLevel.sendParticles(
|
|
ParticleTypes.FISHING,
|
|
getX(),
|
|
getY(),
|
|
getZ(),
|
|
5, // count
|
|
0.1,
|
|
0.0,
|
|
0.1, // spread
|
|
0.02 // speed
|
|
);
|
|
}
|
|
|
|
// Splash sound
|
|
level().playSound(
|
|
null,
|
|
getX(),
|
|
getY(),
|
|
getZ(),
|
|
SoundEvents.FISHING_BOBBER_SPLASH,
|
|
SoundSource.NEUTRAL,
|
|
1.0f,
|
|
1.0f
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the NPC that owns this bobber.
|
|
*
|
|
* @return The owner EntityDamsel, or null if not found
|
|
*/
|
|
public EntityDamsel getOwnerNpc() {
|
|
Entity entity = level().getEntity(entityData.get(DATA_OWNER_ID));
|
|
return entity instanceof EntityDamsel damsel ? damsel : null;
|
|
}
|
|
|
|
/**
|
|
* Whether a fish is biting this bobber.
|
|
*/
|
|
public boolean isBiting() {
|
|
return entityData.get(DATA_BITING);
|
|
}
|
|
|
|
/**
|
|
* Set the biting state.
|
|
*/
|
|
public void setBiting(boolean biting) {
|
|
entityData.set(DATA_BITING, biting);
|
|
}
|
|
|
|
@Override
|
|
public boolean isPickable() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Packet<ClientGamePacketListener> getAddEntityPacket() {
|
|
return new ClientboundAddEntityPacket(this);
|
|
}
|
|
}
|