diff --git a/src/main/java/com/minelittlepony/unicopia/UPOIs.java b/src/main/java/com/minelittlepony/unicopia/UPOIs.java new file mode 100644 index 00000000..3a7b07e4 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/UPOIs.java @@ -0,0 +1,16 @@ +package com.minelittlepony.unicopia; + +import net.fabricmc.fabric.api.object.builder.v1.world.poi.PointOfInterestHelper; +import net.minecraft.block.AbstractChestBlock; +import net.minecraft.block.Block; +import net.minecraft.registry.Registries; +import net.minecraft.world.poi.PointOfInterestType; + +public interface UPOIs { + PointOfInterestType CHESTS = PointOfInterestHelper.register(Unicopia.id("chests"), 1, 64, Registries.BLOCK.getEntrySet().stream() + .map(entry -> entry.getValue()) + .filter(b -> b instanceof AbstractChestBlock) + .toArray(Block[]::new)); + + static void bootstrap() { } +} diff --git a/src/main/java/com/minelittlepony/unicopia/USounds.java b/src/main/java/com/minelittlepony/unicopia/USounds.java index ec580e36..23980e16 100644 --- a/src/main/java/com/minelittlepony/unicopia/USounds.java +++ b/src/main/java/com/minelittlepony/unicopia/USounds.java @@ -29,6 +29,7 @@ public interface USounds { SoundEvent ENTITY_PLAYER_UNICORN_TELEPORT = register("entity.player.unicorn.teleport"); SoundEvent ENTITY_PLAYER_KIRIN_RAGE = ENTITY_POLAR_BEAR_WARNING; SoundEvent ENTITY_PLAYER_KIRIN_RAGE_LOOP = register("entity.player.kirin.rage.loop"); + SoundEvent ENTITY_PLAYER_SEAPONY_SONAR = register("entity.player.seapony.sonar", 64); SoundEvent ENTITY_PLAYER_EARS_RINGING = register("entity.player.ears_ring"); SoundEvent ENTITY_PLAYER_HEARTBEAT = register("entity.player.heartbeat"); @@ -148,6 +149,11 @@ public interface USounds { return Registry.register(Registries.SOUND_EVENT, id, SoundEvent.of(id)); } + static SoundEvent register(String name, float range) { + Identifier id = Unicopia.id(name); + return Registry.register(Registries.SOUND_EVENT, id, SoundEvent.of(id, range)); + } + static void bootstrap() {} static final class Vanilla extends SoundEvents {} diff --git a/src/main/java/com/minelittlepony/unicopia/Unicopia.java b/src/main/java/com/minelittlepony/unicopia/Unicopia.java index 28922b79..4b1f6f3a 100644 --- a/src/main/java/com/minelittlepony/unicopia/Unicopia.java +++ b/src/main/java/com/minelittlepony/unicopia/Unicopia.java @@ -91,6 +91,7 @@ public class Unicopia implements ModInitializer { UGameEvents.bootstrap(); UBlocks.bootstrap(); + UPOIs.bootstrap(); UItems.bootstrap(); UPotions.bootstrap(); UParticles.bootstrap(); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java b/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java index dbf06852..39b66b8f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/Abilities.java @@ -67,6 +67,9 @@ public interface Abilities { Ability NIRIK_BLAST = register(new NirikBlastAbility(), "nirik_blast", AbilitySlot.SECONDARY); Ability KIRIN_CAST = register(new KirinCastingAbility(), "kirin_cast", AbilitySlot.SECONDARY); + // seapony + Ability SONAR_PULSE = register(new SeaponySonarPulseAbility(), "sonar_pulse", AbilitySlot.SECONDARY); + static > T register(T power, String name, AbilitySlot slot) { Identifier id = Unicopia.id(name); BY_SLOT.computeIfAbsent(slot, s -> new LinkedHashSet<>()).add(power); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/SeaponySonarPulseAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/SeaponySonarPulseAbility.java new file mode 100644 index 00000000..c4df03a4 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/SeaponySonarPulseAbility.java @@ -0,0 +1,112 @@ +package com.minelittlepony.unicopia.ability; + +import java.util.Comparator; +import java.util.Optional; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.AwaitTickQueue; +import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.UPOIs; +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.ability.data.Hit; +import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.particle.UParticles; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityGroup; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.registry.tag.FluidTags; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.sound.SoundCategory; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.event.GameEvent; +import net.minecraft.world.poi.PointOfInterestStorage.OccupationStatus; + +public class SeaponySonarPulseAbility implements Ability { + + @Override + public int getWarmupTime(Pony player) { + return 10; + } + + @Override + public int getCooldownTime(Pony player) { + return 100; + } + + @Override + public boolean canUse(Race race) { + return race == Race.SEAPONY; + } + + @Nullable + @Override + public Optional prepare(Pony player) { + return Hit.INSTANCE; + } + + @Override + public Hit.Serializer getSerializer() { + return Hit.SERIALIZER; + } + + @Override + public double getCostEstimate(Pony player) { + return 5; + } + + @Override + public boolean apply(Pony player, Hit data) { + player.setAnimation(Animation.ARMS_UP, Animation.Recipient.ANYONE); + + for (Entity target : player.findAllEntitiesInRange(64, e -> { + return (e instanceof LivingEntity && (e instanceof HostileEntity || ((LivingEntity)e).getGroup() == EntityGroup.AQUATIC)) && e.isSubmergedInWater(); + }).sorted(Comparator.comparing(e -> e.distanceTo(player.asEntity()))).toList()) { + Vec3d offset = target.getPos().subtract(player.getOriginVector()); + float distance = target.distanceTo(player.asEntity()); + if (distance < 4) { + float scale = 1 - (distance/10F); + ((LivingEntity)target).takeKnockback(0.7 * scale, -offset.x, -offset.z); + target.damage(target.getDamageSources().sonicBoom(player.asEntity()), 10 * scale); + } else { + emitPing(player, target.getPos(), 10, 1, 1.3F); + } + } + player.subtractEnergyCost(5); + + if (player.asWorld() instanceof ServerWorld sw) { + sw.getPointOfInterestStorage().getNearestPosition( + type -> type.value() == UPOIs.CHESTS, + pos -> player.asWorld().getFluidState(pos).isIn(FluidTags.WATER), player.getOrigin(), 64, OccupationStatus.ANY) + .ifPresent(chestPos -> { + emitPing(player, chestPos.toCenterPos(), 20, 0.5F, 2F); + }); + } + + player.playSound(USounds.ENTITY_PLAYER_SEAPONY_SONAR, 1); + player.spawnParticles(UParticles.SHOCKWAVE, 1); + player.asEntity().emitGameEvent(GameEvent.INSTRUMENT_PLAY); + + return true; + } + + private void emitPing(Pony player, Vec3d pos, int delay, float volume, float pitch) { + AwaitTickQueue.scheduleTask(player.asWorld(), w -> { + ParticleUtils.spawnParticle(w, UParticles.SHOCKWAVE, pos, Vec3d.ZERO); + float loudness = Math.max(0, 1.4F - (float)Math.log10(player.getOriginVector().distanceTo(pos))); + w.playSound(null, pos.x, pos.y, pos.z, USounds.ENTITY_PLAYER_SEAPONY_SONAR, SoundCategory.AMBIENT, volume * loudness, pitch); + w.emitGameEvent(player.asEntity(), GameEvent.INSTRUMENT_PLAY, pos); + }, delay + (int)player.getOriginVector().distanceTo(pos)); + } + + @Override + public void warmUp(Pony player, AbilitySlot slot) { + player.getMagicalReserves().getExertion().addPercent(6); + } + + @Override + public void coolDown(Pony player, AbilitySlot slot) { + } +} diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index c3ea7de1..77647277 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -497,6 +497,7 @@ "ability.unicopia.peck.block.unfased": "The block is unfazed by your threats", "ability.unicopia.dash": "Flying Dash", "ability.unicopia.change_form": "Change Form", + "ability.unicopia.sonar_pulse": "Sonar Pulse", "gui.unicopia.trait.label": "Element of %s", "gui.unicopia.trait.group": "\n %s", @@ -959,8 +960,10 @@ "unicopia.subtitle.wind_rush": "Wind gusts", "unicopia.subtitle.insects": "Insects Scurrying", "unicopia.subtitle.changeling_buzz": "Drone buzzing", - "unicopia.subtitle.batpony_eeee": "Batpony screeches", + "unicopia.subtitle.batpony_eeee": "Batpony Screeches", + "unicopia.subtitle.sonar": "Sonar Pulses", "unicopia.subtitle.changeling.transform": "Changeling transforms", + "unicopia.subtitle.screech": "Hippogriff Screeches", "unicopia.subtitle.pegasus.molt": "Pegasus loses feather", "unicopia.subtitle.unicorn.teleport": "Magic pops", "unicopia.subtitle.player.wololo": "Wololo!", diff --git a/src/main/resources/assets/unicopia/sounds.json b/src/main/resources/assets/unicopia/sounds.json index 67885c5c..f914ffea 100644 --- a/src/main/resources/assets/unicopia/sounds.json +++ b/src/main/resources/assets/unicopia/sounds.json @@ -33,7 +33,15 @@ "category": "player", "subtitle": "unicopia.subtitle.screech", "sounds": [ - "unicopia:screech/screech0" + "unicopia:screech/screech1", + "unicopia:screech/screech2" + ] + }, + "entity.player.seapony.sonar": { + "category": "player", + "subtitle": "unicopia.subtitle.sonar", + "sounds": [ + "unicopia:sonar/sonar0" ] }, "item.magic.aura": { diff --git a/src/main/resources/assets/unicopia/sounds/screech/screech1.ogg b/src/main/resources/assets/unicopia/sounds/screech/screech1.ogg new file mode 100644 index 00000000..d7d9d38b Binary files /dev/null and b/src/main/resources/assets/unicopia/sounds/screech/screech1.ogg differ diff --git a/src/main/resources/assets/unicopia/sounds/screech/screech2.ogg b/src/main/resources/assets/unicopia/sounds/screech/screech2.ogg new file mode 100644 index 00000000..b60cb9bd Binary files /dev/null and b/src/main/resources/assets/unicopia/sounds/screech/screech2.ogg differ diff --git a/src/main/resources/assets/unicopia/sounds/sonar/sonar0.ogg b/src/main/resources/assets/unicopia/sounds/sonar/sonar0.ogg new file mode 100644 index 00000000..7b61c0b3 Binary files /dev/null and b/src/main/resources/assets/unicopia/sounds/sonar/sonar0.ogg differ diff --git a/src/main/resources/assets/unicopia/textures/gui/ability/sonar_pulse.png b/src/main/resources/assets/unicopia/textures/gui/ability/sonar_pulse.png new file mode 100644 index 00000000..fa9f923a Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/ability/sonar_pulse.png differ