Clean repo for open source release
Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
This commit is contained in:
245
src/main/java/com/tiedup/remake/entities/LeashProxyEntity.java
Normal file
245
src/main/java/com/tiedup/remake/entities/LeashProxyEntity.java
Normal file
@@ -0,0 +1,245 @@
|
||||
package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.ServerScoreboard;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.entity.animal.Turtle;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.scores.PlayerTeam;
|
||||
|
||||
/**
|
||||
* Invisible proxy entity that follows a player and holds the leash.
|
||||
*
|
||||
* Based on PlayerCollars LeashProxyEntity implementation.
|
||||
* Uses Turtle as base because it's a simple, leashable mob.
|
||||
*
|
||||
* Key features:
|
||||
* - Baby turtle (small hitbox)
|
||||
* - Invisible and invulnerable
|
||||
* - No physics or collision
|
||||
* - Follows target player's position (offset to neck height)
|
||||
* - Leash renders from this entity to the holder
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
public final class LeashProxyEntity extends Turtle {
|
||||
|
||||
/** Team name for preventing collision display */
|
||||
public static final String TEAM_NAME = "tiedup_leash_proxy";
|
||||
|
||||
/** The player this proxy follows */
|
||||
private final LivingEntity target;
|
||||
|
||||
/**
|
||||
* Create a new leash proxy for a target player.
|
||||
*
|
||||
* @param target The player to follow
|
||||
*/
|
||||
public LeashProxyEntity(LivingEntity target) {
|
||||
super(EntityType.TURTLE, target.level());
|
||||
this.target = target;
|
||||
|
||||
// Make it invisible and invulnerable
|
||||
setHealth(1.0F);
|
||||
setInvulnerable(true);
|
||||
setBaby(true);
|
||||
setInvisible(true);
|
||||
noPhysics = true;
|
||||
|
||||
// Add to team to prevent collision nameplate display
|
||||
MinecraftServer server = getServer();
|
||||
if (server != null) {
|
||||
ServerScoreboard scoreboard = server.getScoreboard();
|
||||
|
||||
PlayerTeam team = scoreboard.getPlayerTeam(TEAM_NAME);
|
||||
if (team == null) {
|
||||
team = scoreboard.addPlayerTeam(TEAM_NAME);
|
||||
}
|
||||
if (team.getCollisionRule() != PlayerTeam.CollisionRule.NEVER) {
|
||||
team.setCollisionRule(PlayerTeam.CollisionRule.NEVER);
|
||||
}
|
||||
|
||||
scoreboard.addPlayerToTeam(getScoreboardName(), team);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Position Sync ====================
|
||||
|
||||
/**
|
||||
* Update proxy position to match target.
|
||||
*
|
||||
* @return true if proxy should be removed (target invalid)
|
||||
*/
|
||||
private boolean proxyUpdate() {
|
||||
if (proxyIsRemoved()) return false;
|
||||
|
||||
if (target == null) return true;
|
||||
if (target.level() != level() || !target.isAlive()) return true;
|
||||
|
||||
// If target is a player who disconnected, remove proxy
|
||||
if (
|
||||
target instanceof ServerPlayer serverPlayer &&
|
||||
serverPlayer.hasDisconnected()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Vec3 posActual = this.position();
|
||||
|
||||
// Dynamic Y offset based on bind type
|
||||
// DOGBINDER = lower height for 4-legged pose (0.6)
|
||||
// Default = neck height (1.3)
|
||||
double yOffset = 1.3D;
|
||||
if (target instanceof ServerPlayer player) {
|
||||
IBondageState state = KidnappedHelper.getKidnappedState(player);
|
||||
if (state != null && state.isTiedUp()) {
|
||||
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
if (
|
||||
!bind.isEmpty() &&
|
||||
bind.getItem() == ModItems.getBind(BindVariant.DOGBINDER)
|
||||
) {
|
||||
yOffset = 0.35D; // Lower for 4-legged dogwalk pose (back/hip level)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vec3 posTarget = target.position().add(0.0D, yOffset, -0.15D);
|
||||
|
||||
if (!Objects.equals(posActual, posTarget)) {
|
||||
setRot(0.0F, 0.0F);
|
||||
setPos(posTarget.x(), posTarget.y(), posTarget.z());
|
||||
setBoundingBox(
|
||||
getDimensions(Pose.DYING).makeBoundingBox(posTarget)
|
||||
);
|
||||
}
|
||||
|
||||
// Update leash (handles vanilla leash physics)
|
||||
tickLeash();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (this.level().isClientSide) return;
|
||||
|
||||
if (proxyUpdate() && !proxyIsRemoved()) {
|
||||
proxyRemove();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Lifecycle ====================
|
||||
|
||||
/**
|
||||
* Check if this proxy has been removed.
|
||||
*/
|
||||
public boolean proxyIsRemoved() {
|
||||
return this.isRemoved();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this proxy entity.
|
||||
*/
|
||||
public void proxyRemove() {
|
||||
super.remove(RemovalReason.DISCARDED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override remove() to prevent accidental removal.
|
||||
* Use proxyRemove() for intentional removal.
|
||||
*/
|
||||
@Override
|
||||
public void remove(RemovalReason reason) {
|
||||
// Only allow removal via proxyRemove() or if truly killed
|
||||
if (
|
||||
reason == RemovalReason.KILLED ||
|
||||
reason == RemovalReason.CHANGED_DIMENSION
|
||||
) {
|
||||
super.remove(reason);
|
||||
}
|
||||
// Ignore other removal attempts
|
||||
}
|
||||
|
||||
// ==================== Override to Prevent Behavior ====================
|
||||
|
||||
@Override
|
||||
public float getHealth() {
|
||||
return 1.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropLeash(boolean sendPacket, boolean dropItem) {
|
||||
// Don't drop leash - managed by mixin
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeLeashed(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
// No AI goals
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPush(Entity entity) {
|
||||
// No pushing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void push(Entity entity) {
|
||||
// No pushing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerTouch(Player player) {
|
||||
// No interaction
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPushable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vec3 getLeashOffset() {
|
||||
// The proxy is already positioned at the player's neck (Y+1.3)
|
||||
// No additional offset needed - leash attaches exactly at proxy position
|
||||
return Vec3.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvulnerableTo(
|
||||
net.minecraft.world.damagesource.DamageSource source
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(
|
||||
net.minecraft.world.damagesource.DamageSource source,
|
||||
float amount
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target entity this proxy follows.
|
||||
*/
|
||||
public LivingEntity getTarget() {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user