Strip all Phase references, TODO/FUTURE roadmap notes, and internal planning comments from the codebase. Run Prettier for consistent formatting across all Java files.
139 lines
4.6 KiB
Java
139 lines
4.6 KiB
Java
package com.tiedup.remake.entities;
|
|
|
|
import com.tiedup.remake.core.ModConfig;
|
|
import com.tiedup.remake.core.TiedUpMod;
|
|
import com.tiedup.remake.items.ModItems;
|
|
import com.tiedup.remake.state.IBondageState;
|
|
import com.tiedup.remake.util.KidnappedHelper;
|
|
import com.tiedup.remake.v2.BodyRegionV2;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.projectile.AbstractArrow;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.phys.EntityHitResult;
|
|
|
|
/**
|
|
* Rope Arrow Entity - Arrow that binds targets on hit.
|
|
*
|
|
*
|
|
* Behavior:
|
|
* - When hitting an entity, has a chance to bind them
|
|
* - Default: 50% chance
|
|
* - From Archer Kidnapper: 10% base + 10% per previous hit on same target
|
|
* - Uses rope restraint on target
|
|
* - Target must be IBondageState (Player, Damsel, Kidnapper)
|
|
*/
|
|
public class EntityRopeArrow extends AbstractArrow {
|
|
|
|
public EntityRopeArrow(
|
|
EntityType<? extends EntityRopeArrow> type,
|
|
Level level
|
|
) {
|
|
super(type, level);
|
|
}
|
|
|
|
public EntityRopeArrow(Level level, LivingEntity shooter) {
|
|
super(ModEntities.ROPE_ARROW.get(), shooter, level);
|
|
}
|
|
|
|
public EntityRopeArrow(Level level, double x, double y, double z) {
|
|
super(ModEntities.ROPE_ARROW.get(), x, y, z, level);
|
|
}
|
|
|
|
/**
|
|
* Called when the arrow hits an entity.
|
|
* Attempts to bind the target with a chance based on shooter type.
|
|
*/
|
|
@Override
|
|
protected void onHitEntity(EntityHitResult result) {
|
|
// Don't call super - we don't want normal arrow damage
|
|
if (this.level().isClientSide) {
|
|
return;
|
|
}
|
|
|
|
if (result.getEntity() instanceof LivingEntity target) {
|
|
// Check if target can be kidnapped
|
|
IBondageState targetState = KidnappedHelper.getKidnappedState(
|
|
target
|
|
);
|
|
if (targetState == null) {
|
|
TiedUpMod.LOGGER.debug(
|
|
"[EntityRopeArrow] Target {} cannot be bound (not IBondageState)",
|
|
target.getName().getString()
|
|
);
|
|
// Remove arrow
|
|
this.discard();
|
|
return;
|
|
}
|
|
|
|
// Already tied?
|
|
if (targetState.isTiedUp()) {
|
|
TiedUpMod.LOGGER.debug(
|
|
"[EntityRopeArrow] Target {} is already tied",
|
|
target.getName().getString()
|
|
);
|
|
this.discard();
|
|
return;
|
|
}
|
|
|
|
// Determine bind chance based on shooter type
|
|
int bindChance;
|
|
EntityKidnapperArcher archerShooter = null;
|
|
|
|
if (this.getOwner() instanceof EntityKidnapperArcher archer) {
|
|
// Archer kidnapper: cumulative chance system
|
|
archerShooter = archer;
|
|
bindChance = archer.getBindChanceForTarget(target.getUUID());
|
|
} else {
|
|
// Other shooters: default 50% chance
|
|
bindChance = ModConfig.SERVER.ropeArrowBindChance.get();
|
|
}
|
|
|
|
// Roll for bind chance
|
|
int roll = this.random.nextInt(100) + 1;
|
|
if (roll <= bindChance) {
|
|
// Success! Bind the target
|
|
ItemStack ropeItem = new ItemStack(
|
|
ModItems.getBind(
|
|
com.tiedup.remake.items.base.BindVariant.ROPES
|
|
)
|
|
);
|
|
targetState.equip(BodyRegionV2.ARMS, ropeItem);
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[EntityRopeArrow] Successfully bound {} (roll: {} <= {}%)",
|
|
target.getName().getString(),
|
|
roll,
|
|
bindChance
|
|
);
|
|
|
|
// Clear hit counts for this target if shot by archer
|
|
if (archerShooter != null) {
|
|
archerShooter.clearHitsForTarget(target.getUUID());
|
|
}
|
|
} else {
|
|
TiedUpMod.LOGGER.debug(
|
|
"[EntityRopeArrow] Failed to bind {} (roll: {} > {}%)",
|
|
target.getName().getString(),
|
|
roll,
|
|
bindChance
|
|
);
|
|
|
|
// Record hit for archer's cumulative chance
|
|
if (archerShooter != null) {
|
|
archerShooter.recordHitOnTarget(target.getUUID());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove the arrow after hit
|
|
this.discard();
|
|
}
|
|
|
|
@Override
|
|
protected ItemStack getPickupItem() {
|
|
return new ItemStack(ModItems.ROPE_ARROW.get());
|
|
}
|
|
}
|