package eu.abdelbaki.cmmodular; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.RangedAttribute; import net.minecraft.world.entity.player.Player; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.EntityAttributeModificationEvent; import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent; import net.neoforged.neoforge.event.tick.PlayerTickEvent; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; /** * A sea serpent fang in hand is worth more in water than out of it: harder * hits and a faster swing while the bearer is in it. * *
The mining half of the same idea needs nothing here - vanilla models * underwater digging as an attribute of its own, * {@code minecraft:player.submerged_mining_speed}, and the material simply * adds to it. Damage and attack speed have no such switch, so this follows the * pattern {@link FireImmunity} and {@link WaterBreathing} already set: an * attribute of this mod's own that the material grants like any other, and a * listener here that watches for it. * *
Damage is answered on the blow, which needs no state. Attack speed cannot
* be - it is read off the attribute map whenever the game wants to know how
* fast the bearer swings - so a modifier goes on and comes off as the bearer
* enters and leaves the water. It is transient and keyed by one id, so a
* player who never touches a fang never has one and a player who does never
* collects two.
*/
public final class WaterCombat {
private static final DeferredRegister Only players, because attack speed is only ever read for a player.
* The equipment is only consulted while the bearer is in water, so this
* costs nothing on dry land.
*/
private static void onPlayerTick(PlayerTickEvent.Post event) {
Player player = event.getEntity();
AttributeInstance speed = player.getAttribute(Attributes.ATTACK_SPEED);
if (speed == null) {
return;
}
double bonus = player.isInWater() ? boost(player) : 0.0D;
AttributeModifier applied = speed.getModifier(SWING_ID);
if (bonus <= 0.0D) {
if (applied != null) {
speed.removeModifier(SWING_ID);
}
return;
}
if (applied != null && applied.amount() == bonus) {
return;
}
// Weapon swapped for one with more fang in it, or the map was rebuilt
// out from under the modifier - either way, put the right one back.
speed.removeModifier(SWING_ID);
speed.addTransientModifier(new AttributeModifier(SWING_ID, bonus,
AttributeModifier.Operation.ADD_MULTIPLIED_BASE));
}
/** What the held modules are worth, clamped to what the attribute allows. */
private static double boost(LivingEntity entity) {
return Math.min(EquipmentAttributes.value(entity, WATER_COMBAT), MAX);
}
}