package com.tiedup.remake.entities; import com.tiedup.remake.core.SettingsAccessor; 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 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 = SettingsAccessor.getRopeArrowBindChance(); } // Roll for bind chance int roll = this.random.nextInt(100) + 1; if (roll <= bindChance) { // Success! Bind the target ItemStack ropeItem = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack( new net.minecraft.resources.ResourceLocation("tiedup", "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()); } }