Clean up the abilities code

This commit is contained in:
Sollace 2023-08-15 23:18:41 +01:00
parent 3ed4ec746c
commit 3b16930e3b
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
24 changed files with 292 additions and 355 deletions

View file

@ -2,15 +2,13 @@ package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
import net.minecraft.util.Util;
public interface Ability<T extends Hit> {
/**
@ -28,10 +26,42 @@ public interface Ability<T extends Hit> {
*/
int getCooldownTime(Pony player);
/**
* The icon representing this ability on the UI and HUD.
*/
default Identifier getIcon(Pony player) {
return getId().withPath(p -> "textures/gui/ability/" + p + ".png");
}
default int getColor(Pony player) {
return -1;
}
/**
* The display name for this ability.
*/
default Text getName(Pony player) {
return Text.translatable(getTranslationKey());
}
default String getTranslationKey() {
return Util.createTranslationKey("ability", getId());
}
default Identifier getId() {
return Abilities.REGISTRY.getId(this);
}
default boolean activateOnEarlyRelease() {
return false;
}
/**
* Checks if the given race is permitted to use this ability
* @param playerSpecies The player's species
*/
boolean canUse(Race playerSpecies);
/**
* Called when an ability is about to be triggered. This event occurs on both the client and server so check {@code Pony#isClient} if you need to know which one you're on.
* <p>
@ -40,86 +70,32 @@ public interface Ability<T extends Hit> {
* @return True if the event has been handled.
*/
default boolean onQuickAction(Pony player, ActivationType type, Optional<T> data) {
return onQuickAction(player, type);
}
@Deprecated
default boolean onQuickAction(Pony player, ActivationType type) {
return false;
}
/**
* Called on the client to get any data required for the quick action.
*
* @param player The player
* @param type The type of quick event being triggered
* @return The data to pass on to the quick event handler
*/
default Optional<T> prepareQuickAction(Pony player, ActivationType type) {
return Optional.empty();
}
/**
* Called to check preconditions for activating the ability.
*
* @param w The world
* @param player The player
* @return True to allow activation
* Gets the serializer to use for reading data over the network.
*/
default boolean canActivate(World w, Pony player) {
return true;
}
/**
* Checks if the given race is permitted to use this ability
* @param playerSpecies The player's species
*/
boolean canUse(Race playerSpecies);
@Deprecated
@Nullable
T tryActivate(Pony player);
/**
* Called on the client to activate the ability.
*
* @param player The player activating the ability
* @return Data to be sent, or null if activation failed
*/
default Optional<T> prepare(Pony player) {
return Optional.ofNullable(tryActivate(player));
}
Hit.Serializer<T> getSerializer();
/**
* The icon representing this ability on the UI and HUD.
*/
default Identifier getIcon(Pony player) {
Identifier id = Abilities.REGISTRY.getId(this);
return new Identifier(id.getNamespace(), "textures/gui/ability/" + id.getPath() + ".png");
}
default Text getName(Pony player) {
return getName();
}
/**
* The display name for this ability.
*/
default Text getName() {
return Text.translatable(getTranslationKey());
}
default String getTranslationKey() {
Identifier id = Abilities.REGISTRY.getId(this);
return "ability." + id.getNamespace() + "." + id.getPath().replace('/', '.');
}
/**
* Server-side counterpart to canActivate.
* Called on the client to get any data required to activate the ability.
*
* Called before applying to determine whether to cancel the command or not.
* @param player The player activating the ability
* @return Data to be sent, or Empty if activation failed
*/
default boolean canApply(Pony player, T data) {
return true;
}
Optional<T> prepare(Pony player);
/**
* Called to actually apply the ability.
@ -127,18 +103,19 @@ public interface Ability<T extends Hit> {
*
* @param player The player that triggered the ability
* @param data Data previously sent from the client
* @return True if the ability succeeded. Returning false will cause an ability reset message to be sent to the client.
*/
void apply(Pony player, T data);
boolean apply(Pony player, T data);
/**
* Called every tick until the warmup timer runs out.
* @param player The current player
*/
void preApply(Pony player, AbilitySlot slot);
void warmUp(Pony player, AbilitySlot slot);
/**
* Called every tick until the cooldown timer runs out.
* @param player The current player
*/
void postApply(Pony player, AbilitySlot slot);
void coolDown(Pony player, AbilitySlot slot);
}

View file

@ -37,21 +37,10 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
Stat stat = getStat(slot);
if (stat.canSwitchStates()) {
if (pressType == ActivationType.NONE || stat.getAbility(page).filter(ability -> !triggerQuickAction(ability, pressType)).isEmpty()) {
stat.setActiveAbility(null);
}
stat.clear(pressType, page);
}
}
private <T extends Hit> boolean triggerQuickAction(Ability<T> ability, ActivationType pressType) {
Optional<T> data = ability.prepareQuickAction(player, pressType);
if (ability.onQuickAction(player, pressType, data)) {
Channel.CLIENT_PLAYER_ABILITY.sendToServer(new MsgPlayerAbility<>(ability, data, pressType));
return true;
}
return false;
}
public Optional<Ability<?>> activate(AbilitySlot slot, long page) {
Stat stat = getStat(slot);
if (stat.canSwitchStates()) {
@ -182,42 +171,42 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
}
public void tick() {
getActiveAbility().ifPresent(this::activate);
getActiveAbility().ifPresent(ability -> {
if (warmup > 0) {
warmup--;
ability.warmUp(player, slot);
return;
}
if (cooldown > 0 && cooldown-- > 0) {
ability.coolDown(player, slot);
if (cooldown <= 0) {
setActiveAbility(null);
}
return;
}
tryFire(ability);
});
}
private <T extends Hit> void activate(Ability<T> ability) {
if (warmup > 0) {
warmup--;
ability.preApply(player, slot);
return;
}
if (cooldown > 0 && cooldown-- > 0) {
ability.postApply(player, slot);
if (cooldown <= 0) {
setActiveAbility(null);
}
return;
}
private <T extends Hit> void tryFire(Ability<T> ability) {
if (triggered) {
return;
}
if (ability.canActivate(player.asWorld(), player)) {
triggered = true;
setCooldown(ability.getCooldownTime(player));
triggered = true;
setCooldown(ability.getCooldownTime(player));
if (player.isClientPlayer()) {
Optional<T> data = ability.prepare(player);
if (player.isClientPlayer()) {
Optional<T> data = ability.prepare(player);
if (data.isPresent()) {
Channel.CLIENT_PLAYER_ABILITY.sendToServer(new MsgPlayerAbility<>(ability, data, ActivationType.NONE));
} else {
player.asEntity().playSound(USounds.GUI_ABILITY_FAIL, 1, 1);
setCooldown(0);
}
if (data.isPresent()) {
Channel.CLIENT_PLAYER_ABILITY.sendToServer(new MsgPlayerAbility<>(ability, data, ActivationType.NONE));
} else {
player.asEntity().playSound(USounds.GUI_ABILITY_FAIL, 1, 1);
setCooldown(0);
}
}
@ -235,6 +224,26 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable {
return Optional.ofNullable(found.get((int)Math.min(found.size() - 1, page)));
}
public void clear(ActivationType pressType, long page) {
if (pressType == ActivationType.NONE
|| getAbility(page).filter(ability -> !triggerQuickAction(ability, pressType)).isEmpty()) {
if (warmup > 0) {
getActiveAbility().filter(Ability::activateOnEarlyRelease).ifPresentOrElse(this::tryFire, () -> setActiveAbility(null));
} else {
setActiveAbility(null);
}
}
}
private <T extends Hit> boolean triggerQuickAction(Ability<T> ability, ActivationType pressType) {
Optional<T> data = ability.prepareQuickAction(player, pressType);
if (ability.onQuickAction(player, pressType, data)) {
Channel.CLIENT_PLAYER_ABILITY.sendToServer(new MsgPlayerAbility<>(ability, data, pressType));
return true;
}
return false;
}
public long getMaxPage() {
return Abilities.BY_SLOT_AND_COMPOSITE_RACE.apply(slot, player.getCompositeRace()).size();
}

View file

@ -45,7 +45,7 @@ abstract class AbstractSpellCastingAbility implements Ability<Hit> {
gemSpell.getValue().type().getName().copy().formatted(gemSpell.getValue().type().getAffinity().getColor())
);
}
return getName();
return Ability.super.getName(player);
}
@Override
@ -64,7 +64,7 @@ abstract class AbstractSpellCastingAbility implements Ability<Hit> {
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
}

View file

@ -1,10 +1,12 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Numeric;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
@ -23,7 +25,7 @@ import net.minecraft.util.math.random.Random;
* A magic casting ability for unicorns.
* (only shields for now)
*/
public class BatEeeeAbility implements Ability<Hit> {
public class BatEeeeAbility implements Ability<Numeric> {
@Override
public int getWarmupTime(Pony player) {
@ -46,17 +48,17 @@ public class BatEeeeAbility implements Ability<Hit> {
}
@Override
public Hit tryActivate(Pony player) {
return Hit.INSTANCE;
public Optional<Numeric> prepare(Pony player) {
return Numeric.of(1);
}
@Override
public Hit.Serializer<Hit> getSerializer() {
return Hit.SERIALIZER;
public Numeric.Serializer<Numeric> getSerializer() {
return Numeric.SERIALIZER;
}
@Override
public void apply(Pony player, Hit data) {
public boolean apply(Pony player, Numeric data) {
Random rng = player.asWorld().random;
int count = 1 + rng.nextInt(10);
@ -105,14 +107,16 @@ public class BatEeeeAbility implements Ability<Hit> {
if (total >= 20) {
UCriteria.SCREECH_TWENTY_MOBS.trigger(player.asEntity());
}
return true;
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
for (int i = 0; i < 20; i++) {
player.addParticle(ParticleTypes.BUBBLE_POP, player.getPhysics().getHeadPosition().toCenterPos(), VecHelper.supply(() -> player.asWorld().getRandom().nextGaussian() - 0.5));
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Multi;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -34,17 +36,16 @@ public class BatPonyHangAbility implements Ability<Multi> {
}
@Override
public Multi tryActivate(Pony player) {
public Optional<Multi> prepare(Pony player) {
if (player.isHanging()) {
return new Multi(BlockPos.ZERO, 0);
return Optional.of(new Multi(BlockPos.ZERO, 0));
}
return TraceHelper.findBlock(player.asEntity(), 5, 1)
.map(BlockPos::down)
.filter(player::canHangAt)
.map(pos -> new Multi(pos, 1))
.orElse(null);
.map(pos -> new Multi(pos, 1));
}
@Override
@ -53,22 +54,24 @@ public class BatPonyHangAbility implements Ability<Multi> {
}
@Override
public void apply(Pony player, Multi data) {
if (data.hitType == 0 && player.isHanging()) {
public boolean apply(Pony player, Multi data) {
if (data.hitType() == 0 && player.isHanging()) {
player.stopHanging();
return;
return true;
}
if (data.hitType == 1 && player.canHangAt(data.pos())) {
player.startHanging(data.pos());
if (data.hitType() == 1 && player.canHangAt(data.pos().pos())) {
player.startHanging(data.pos().pos());
}
return true;
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
}
}

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.StreamSupport;
@ -41,7 +42,7 @@ public class CarryAbility implements Ability<Hit> {
}
@Override
public Hit tryActivate(Pony player) {
public Optional<Hit> prepare(Pony player) {
return Hit.INSTANCE;
}
@ -57,7 +58,7 @@ public class CarryAbility implements Ability<Hit> {
}
@Override
public boolean onQuickAction(Pony player, ActivationType type) {
public boolean onQuickAction(Pony player, ActivationType type, Optional<Hit> data) {
if (type == ActivationType.TAP && player.getPhysics().isFlying()) {
player.getPhysics().dashForward((float)player.asWorld().random.nextTriangular(1, 0.3F));
@ -68,7 +69,7 @@ public class CarryAbility implements Ability<Hit> {
}
@Override
public void apply(Pony iplayer, Hit data) {
public boolean apply(Pony iplayer, Hit data) {
PlayerEntity player = iplayer.asEntity();
LivingEntity rider = findRider(player, iplayer.asWorld());
@ -90,14 +91,15 @@ public class CarryAbility implements Ability<Hit> {
}
Living.transmitPassengers(player);
return true;
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
}
public interface IPickupImmuned {

View file

@ -1,6 +1,8 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
@ -25,19 +27,16 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
@Nullable
@Override
public Hit tryActivate(Pony player) {
if (player.asEntity().isCreative() || player.getMagicalReserves().getMana().getPercentFill() >= 0.9F) {
return Hit.INSTANCE;
}
return null;
public Optional<Hit> prepare(Pony player) {
return Hit.of(player.asEntity().isCreative() || player.getMagicalReserves().getMana().getPercentFill() >= 0.9F);
}
@Override
public void apply(Pony iplayer, Hit data) {
public boolean apply(Pony iplayer, Hit data) {
PlayerEntity player = iplayer.asEntity();
if (!player.isCreative() && iplayer.getMagicalReserves().getMana().getPercentFill() < 0.9F) {
return;
if (prepare(iplayer).isEmpty()) {
return false;
}
Trace trace = Trace.create(player, 10, 1, EquinePredicates.VALID_FOR_DISGUISE);
@ -61,16 +60,17 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
player.calculateDimensions();
iplayer.setDirty();
return true;
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getEnergy().add(20);
player.spawnParticles(UParticles.CHANGELING_MAGIC, 5);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getEnergy().set(0);
player.spawnParticles(UParticles.CHANGELING_MAGIC, 5);
}

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
@ -51,14 +52,8 @@ public class ChangelingFeedAbility implements Ability<Hit> {
@Nullable
@Override
public Hit tryActivate(Pony player) {
if (canFeed(player)) {
if (!getTargets(player).isEmpty()) {
return Hit.INSTANCE;
}
}
return null;
public Optional<Hit> prepare(Pony player) {
return Hit.of(canFeed(player) && !getTargets(player).isEmpty());
}
private boolean canFeed(Pony player) {
@ -97,9 +92,9 @@ public class ChangelingFeedAbility implements Ability<Hit> {
}
@Override
public void apply(Pony iplayer, Hit data) {
public boolean apply(Pony iplayer, Hit data) {
if (!canFeed(iplayer)) {
return;
return false;
}
PlayerEntity player = iplayer.asEntity();
@ -131,6 +126,8 @@ public class ChangelingFeedAbility implements Ability<Hit> {
} else {
iplayer.playSound(SoundEvents.ENTITY_GENERIC_DRINK, 0.1F, iplayer.getRandomPitch());
}
return true;
}
public float drainFrom(Pony changeling, LivingEntity living) {
@ -167,12 +164,12 @@ public class ChangelingFeedAbility implements Ability<Hit> {
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().add(6);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
if (player.asWorld().random.nextInt(10) == 0) {
player.spawnParticles(ParticleTypes.HEART, 1);
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Pos;
@ -35,8 +37,8 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
}
@Override
public Pos tryActivate(Pony player) {
return TraceHelper.findBlock(player.asEntity(), 3, 1).map(Pos::new).orElse(null);
public Optional<Pos> prepare(Pony player) {
return TraceHelper.findBlock(player.asEntity(), 3, 1).map(Pos::new);
}
@Override
@ -50,7 +52,7 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
}
@Override
public void apply(Pony player, Pos data) {
public boolean apply(Pony player, Pos data) {
int count = 0;
for (BlockPos pos : BlockPos.iterate(
@ -62,6 +64,7 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
if (count > 0) {
player.subtractEnergyCost(count / 5D);
}
return true;
}
protected int applySingle(World w, BlockState state, BlockPos pos) {
@ -77,7 +80,7 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().add(30);
if (player.asWorld().isClient()) {
@ -86,7 +89,7 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
}
}

View file

@ -113,11 +113,11 @@ public class EarthPonyKickAbility implements Ability<Pos> {
@Nullable
@Override
public Pos tryActivate(Pony player) {
public Optional<Pos> prepare(Pony player) {
return TraceHelper.findBlock(player.asEntity(), 6 * getKickDirection(player), 1)
.filter(pos -> TreeType.at(pos, player.asWorld()) != TreeType.NONE)
.map(Pos::new)
.orElseGet(() -> getDefaultKickLocation(player));
.or(() -> Optional.of(getDefaultKickLocation(player)));
}
private int getKickDirection(Pony player) {
@ -133,33 +133,26 @@ public class EarthPonyKickAbility implements Ability<Pos> {
return new Pos(BlockPos.ofFloored(player.getOriginVector().add(kickVector)));
}
@Override
public boolean canApply(Pony player, Pos data) {
BlockPos pos = data.pos();
TreeType tree = TreeType.at(pos, player.asWorld());
return tree == TreeType.NONE || tree.findBase(player.asWorld(), pos)
.map(base -> tree.countBlocks(player.asWorld(), pos) > 0)
.orElse(false);
}
@Override
public Hit.Serializer<Pos> getSerializer() {
return Pos.SERIALIZER;
}
@Override
public void apply(Pony iplayer, Pos data) {
public boolean apply(Pony iplayer, Pos data) {
BlockPos pos = data.pos();
TreeType tree = TreeType.at(pos, iplayer.asWorld());
if (tree == TreeType.NONE || tree.findBase(iplayer.asWorld(), pos)
.map(base -> tree.countBlocks(iplayer.asWorld(), pos) > 0)
.orElse(false)) {
return false;
}
iplayer.setAnimation(Animation.KICK, Animation.Recipient.ANYONE);
iplayer.subtractEnergyCost(tree == TreeType.NONE ? 1 : 3);
if (tree == TreeType.NONE) {
return;
}
ParticleUtils.spawnParticle(iplayer.asWorld(), UParticles.GROUND_POUND, data.vec(), Vec3d.ZERO);
PlayerEntity player = iplayer.asEntity();
@ -184,15 +177,17 @@ public class EarthPonyKickAbility implements Ability<Pos> {
iplayer.subtractEnergyCost(cost / 7F);
}
}
return true;
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().add(40);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
}
private int dropApples(PlayerEntity player, BlockPos pos) {

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
@ -66,7 +68,7 @@ public class EarthPonyStompAbility implements Ability<Hit> {
@Nullable
@Override
public Hit tryActivate(Pony player) {
public Optional<Hit> prepare(Pony player) {
if (!player.asEntity().isOnGround()
&& player.asEntity().getVelocity().y * player.getPhysics().getGravitySignum() < 0
&& !player.asEntity().getAbilities().flying) {
@ -74,7 +76,7 @@ public class EarthPonyStompAbility implements Ability<Hit> {
return Hit.INSTANCE;
}
return null;
return Optional.empty();
}
@Override
@ -92,7 +94,7 @@ public class EarthPonyStompAbility implements Ability<Hit> {
}
@Override
public void apply(Pony iplayer, Hit data) {
public boolean apply(Pony iplayer, Hit data) {
PlayerEntity player = iplayer.asEntity();
iplayer.setAnimation(Animation.STOMP, Animation.Recipient.ANYONE, 10);
@ -152,6 +154,7 @@ public class EarthPonyStompAbility implements Ability<Hit> {
iplayer.subtractEnergyCost(rad);
});
return true;
}
public static void spawnEffectAround(Entity source, BlockPos center, double radius, double range) {
@ -200,11 +203,11 @@ public class EarthPonyStompAbility implements Ability<Hit> {
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().add(40);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
}
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
@ -38,13 +40,8 @@ public class PegasusCaptureStormAbility implements Ability<Hit> {
@Nullable
@Override
public Hit tryActivate(Pony player) {
if (!player.asEntity().isCreative() && player.getMagicalReserves().getMana().getPercentFill() < 0.2F) {
return null;
}
return Hit.INSTANCE;
public Optional<Hit> prepare(Pony player) {
return Hit.of(player.asEntity().isCreative() || player.getMagicalReserves().getMana().getPercentFill() >= 0.2F);
}
@Override
@ -58,7 +55,7 @@ public class PegasusCaptureStormAbility implements Ability<Hit> {
}
@Override
public void apply(Pony player, Hit data) {
public boolean apply(Pony player, Hit data) {
World w = player.asWorld();
ItemStack stack = player.asEntity().getStackInHand(Hand.MAIN_HAND);
@ -106,6 +103,7 @@ public class PegasusCaptureStormAbility implements Ability<Hit> {
}
}
return true;
}
private void tell(Pony player, String translation) {
@ -113,12 +111,12 @@ public class PegasusCaptureStormAbility implements Ability<Hit> {
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().add(6);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
@ -29,8 +31,8 @@ public class PegasusFlightToggleAbility implements Ability<Hit> {
@Nullable
@Override
public Hit tryActivate(Pony player) {
return player.asEntity().isCreative() || player.getPhysics().getFlightType().isGrounded() ? null : Hit.INSTANCE;
public Optional<Hit> prepare(Pony player) {
return Hit.of(!player.asEntity().isCreative() && !player.getPhysics().getFlightType().isGrounded());
}
@Override
@ -53,9 +55,9 @@ public class PegasusFlightToggleAbility implements Ability<Hit> {
}
@Override
public void apply(Pony player, Hit data) {
if (tryActivate(player) == null) {
return;
public boolean apply(Pony player, Hit data) {
if (prepare(player).isEmpty()) {
return false;
}
player.subtractEnergyCost(1);
@ -69,14 +71,15 @@ public class PegasusFlightToggleAbility implements Ability<Hit> {
}
player.setDirty();
player.setAnimation(Animation.SPREAD_WINGS, Animation.Recipient.ANYONE);
return true;
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().add(6);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
}
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
@ -12,7 +14,6 @@ import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.UParticles;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
/**
* Pegasus ability to perform rainbooms
@ -29,11 +30,6 @@ public class PegasusRainboomAbility implements Ability<Hit> {
return 60;
}
@Override
public boolean canActivate(World w, Pony player) {
return player.canUseSuperMove();
}
@Override
public boolean canUse(Race race) {
return race.canInteractWithClouds();
@ -41,17 +37,8 @@ public class PegasusRainboomAbility implements Ability<Hit> {
@Nullable
@Override
public Hit tryActivate(Pony player) {
if (!player.asEntity().isCreative() && !player.canUseSuperMove()) {
return null;
}
if (player.getPhysics().isFlying() && !SpellType.RAINBOOM.isOn(player)) {
return Hit.INSTANCE;
}
return null;
public Optional<Hit> prepare(Pony player) {
return Hit.of(player.canUseSuperMove() && player.getPhysics().isFlying() && !SpellType.RAINBOOM.isOn(player));
}
@Override
@ -65,7 +52,7 @@ public class PegasusRainboomAbility implements Ability<Hit> {
}
@Override
public boolean onQuickAction(Pony player, ActivationType type) {
public boolean onQuickAction(Pony player, ActivationType type, Optional<Hit> data) {
if (type == ActivationType.TAP && player.getPhysics().isFlying() && player.getMagicalReserves().getMana().get() > 40) {
player.getPhysics().dashForward((float)player.asWorld().random.nextTriangular(2.5F, 0.3F));
@ -78,25 +65,26 @@ public class PegasusRainboomAbility implements Ability<Hit> {
}
@Override
public void apply(Pony player, Hit data) {
public boolean apply(Pony player, Hit data) {
if (tryActivate(player) == null) {
return;
if (prepare(player).isEmpty()) {
return false;
}
if (player.consumeSuperMove()) {
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
SpellType.RAINBOOM.withTraits().apply(player, CastingMethod.INNATE);
}
return true;
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().add(6);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
}

View file

@ -1,6 +1,6 @@
package com.minelittlepony.unicopia.ability;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.ability.data.Hit;
@ -42,12 +42,8 @@ public class UnicornCastingAbility extends AbstractSpellCastingAbility {
}
@Override
@Nullable
public Hit tryActivate(Pony player) {
if (!player.canCast()) {
return null;
}
return Hit.of(player.getMagicalReserves().getMana().get() >= getCostEstimate(player));
public Optional<Hit> prepare(Pony player) {
return Hit.of(player.canCast() && player.getMagicalReserves().getMana().get() >= getCostEstimate(player));
}
@Override
@ -76,9 +72,9 @@ public class UnicornCastingAbility extends AbstractSpellCastingAbility {
}
@Override
public void apply(Pony player, Hit data) {
public boolean apply(Pony player, Hit data) {
if (!player.canCast()) {
return;
return false;
}
TypedActionResult<ItemStack> amulet = getAmulet(player);
@ -123,6 +119,8 @@ public class UnicornCastingAbility extends AbstractSpellCastingAbility {
}
}
}
return true;
}
private TypedActionResult<ItemStack> getAmulet(Pony player) {
@ -141,7 +139,7 @@ public class UnicornCastingAbility extends AbstractSpellCastingAbility {
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExhaustion().multiply(3.3F);
if (getAmulet(player).getResult() == ActionResult.CONSUME) {

View file

@ -54,7 +54,7 @@ public class UnicornDispellAbility implements Ability<Pos> {
}
@Override
public boolean onQuickAction(Pony player, ActivationType type) {
public boolean onQuickAction(Pony player, ActivationType type, Optional<Pos> data) {
if (player.getSpecies() != Race.CHANGELING) {
if (type.getTapCount() > 1) {
@ -84,16 +84,17 @@ public class UnicornDispellAbility implements Ability<Pos> {
}
@Override
public Pos tryActivate(Pony player) {
return getTarget(player).map(Caster::getOrigin).map(Pos::new).orElse(null);
public Optional<Pos> prepare(Pony player) {
return getTarget(player).map(Caster::getOrigin).map(Pos::new);
}
@Override
public void apply(Pony player, Pos data) {
public boolean apply(Pony player, Pos data) {
player.setAnimation(Animation.WOLOLO, Animation.Recipient.ANYONE);
Caster.stream(VecHelper.findInRange(player.asEntity(), player.asWorld(), data.vec(), 3, EquinePredicates.IS_PLACED_SPELL).stream()).forEach(target -> {
target.getSpellSlot().clear();
});
return true;
}
private Optional<Caster<?>> getTarget(Pony player) {
@ -102,12 +103,12 @@ public class UnicornDispellAbility implements Ability<Pos> {
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExhaustion().multiply(3.3F);
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
}
}

View file

@ -23,9 +23,9 @@ public class UnicornGroupTeleportAbility extends UnicornTeleportAbility {
}
@Override
public void apply(Pony player, Pos data) {
public boolean apply(Pony player, Pos data) {
getComrades(player).forEach(teleportee -> teleport(player, teleportee, data));
super.apply(player, data);
return super.apply(player, data);
}
private Stream<Caster<?>> getComrades(Pony player) {

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.HomingSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
@ -25,7 +27,7 @@ public class UnicornProjectileAbility extends AbstractSpellCastingAbility {
}
@Override
public Hit tryActivate(Pony player) {
public Optional<Hit> prepare(Pony player) {
return Hit.of(player.getCharms().getSpellInHand(false).getResult() != ActionResult.FAIL);
}
@ -35,7 +37,7 @@ public class UnicornProjectileAbility extends AbstractSpellCastingAbility {
}
@Override
public boolean onQuickAction(Pony player, ActivationType type) {
public boolean onQuickAction(Pony player, ActivationType type, Optional<Hit> data) {
if (type == ActivationType.DOUBLE_TAP) {
if (!player.isClient()) {
TypedActionResult<CustomisedSpellType<?>> thrown = player.getCharms().getSpellInHand(true);
@ -54,7 +56,7 @@ public class UnicornProjectileAbility extends AbstractSpellCastingAbility {
}
@Override
public void apply(Pony player, Hit data) {
public boolean apply(Pony player, Hit data) {
TypedActionResult<CustomisedSpellType<?>> thrown = player.getCharms().getSpellInHand(true);
if (thrown.getResult() != ActionResult.FAIL) {
@ -69,11 +71,15 @@ public class UnicornProjectileAbility extends AbstractSpellCastingAbility {
TraceHelper.findEntity(player.asEntity(), 600, 1).filter(((HomingSpell)spell)::setTarget).ifPresent(projectile::setHomingTarget);
}
});
return true;
}
return false;
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExhaustion().multiply(3.3F);
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
@ -32,8 +34,7 @@ public class UnicornTeleportAbility implements Ability<Pos> {
@Override
public Identifier getIcon(Pony player) {
Identifier id = Abilities.REGISTRY.getId(this);
return new Identifier(id.getNamespace(), "textures/gui/ability/" + id.getPath() + (player.asEntity().isSneaking() ? "_far" : "_near") + ".png");
return getId().withPath(p -> "textures/gui/ability/" + p + (player.asEntity().isSneaking() ? "_far" : "_near") + ".png");
}
@Override
@ -41,7 +42,7 @@ public class UnicornTeleportAbility implements Ability<Pos> {
if (player.asEntity().isSneaking()) {
return Text.translatable(getTranslationKey() + ".far");
}
return getName();
return Ability.super.getName(player);
}
@Override
@ -61,12 +62,7 @@ public class UnicornTeleportAbility implements Ability<Pos> {
@Override
public double getCostEstimate(Pony player) {
Pos pos = tryActivate(player);
if (pos == null) {
return 0;
}
return pos.distanceTo(player) / 10;
return prepare(player).map(pos -> pos.distanceTo(player) / 10).orElse(0D);
}
@Override
@ -75,10 +71,10 @@ public class UnicornTeleportAbility implements Ability<Pos> {
}
@Override
public Pos tryActivate(Pony player) {
public Optional<Pos> prepare(Pony player) {
if (!player.canCast()) {
return null;
return Optional.empty();
}
int maxDistance = player.asEntity().isCreative() ? 1000 : 100;
@ -121,7 +117,7 @@ public class UnicornTeleportAbility implements Ability<Pos> {
}
return new Pos(pos);
}).orElse(null);
});
}
@Override
@ -130,20 +126,20 @@ public class UnicornTeleportAbility implements Ability<Pos> {
}
@Override
public void apply(Pony iplayer, Pos data) {
teleport(iplayer, iplayer, data);
public boolean apply(Pony iplayer, Pos data) {
return teleport(iplayer, iplayer, data);
}
protected void teleport(Pony teleporter, Caster<?> teleportee, Pos destination) {
protected boolean teleport(Pony teleporter, Caster<?> teleportee, Pos destination) {
if (!teleporter.canCast()) {
return;
return false;
}
Entity participant = teleportee.asEntity();
if (participant == null) {
return;
return false;
}
teleportee.asWorld().playSound(null, teleportee.getOrigin(), USounds.ENTITY_PLAYER_UNICORN_TELEPORT, SoundCategory.PLAYERS, 1, 1);
@ -177,6 +173,8 @@ public class UnicornTeleportAbility implements Ability<Pos> {
participant.fallDistance /= distance;
participant.getWorld().playSound(null, destination.pos(), USounds.ENTITY_PLAYER_UNICORN_TELEPORT, SoundCategory.PLAYERS, 1, 1);
return true;
}
private boolean enterable(World w, BlockPos pos) {
@ -200,13 +198,13 @@ public class UnicornTeleportAbility implements Ability<Pos> {
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
public void warmUp(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExertion().add(30);
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
public void coolDown(Pony player, AbilitySlot slot) {
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
}

View file

@ -1,25 +1,18 @@
package com.minelittlepony.unicopia.ability.data;
import java.util.Optional;
import net.minecraft.network.PacketByteBuf;
public class Hit {
public interface Hit {
Optional<Hit> INSTANCE = Optional.of(new Hit() {});
Serializer<Hit> SERIALIZER = new Serializer<>(buf -> INSTANCE.get(), (buf, t) -> {});
public static final Hit INSTANCE = new Hit();
public static final Serializer<Hit> SERIALIZER = buf -> INSTANCE;
public static Hit of(boolean value) {
return value ? INSTANCE : null;
static Optional<Hit> of(boolean value) {
return value ? INSTANCE : Optional.empty();
}
protected Hit() {
}
public void toBuffer(PacketByteBuf buf) {
}
public interface Serializer<T extends Hit> {
T fromBuffer(PacketByteBuf buf);
public record Serializer<T extends Hit> (
PacketByteBuf.PacketReader<T> read,
PacketByteBuf.PacketWriter<T> write) {
}
}

View file

@ -1,26 +1,16 @@
package com.minelittlepony.unicopia.ability.data;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.math.Vec3i;
public class Multi extends Pos {
public static final Serializer<Multi> SERIALIZER = Multi::new;
public final int hitType;
Multi(PacketByteBuf buf) {
super(buf);
hitType = buf.readInt();
}
@Override
public void toBuffer(PacketByteBuf buf) {
super.toBuffer(buf);
buf.writeInt(hitType);
}
public record Multi (Pos pos, int hitType) implements Hit {
public static final Serializer<Multi> SERIALIZER = new Serializer<>(
buf -> new Multi(Pos.SERIALIZER.read().apply(buf), buf.readInt()),
(buf, t) -> {
Pos.SERIALIZER.write().accept(buf, t.pos());
buf.writeInt(t.hitType());
});
public Multi(Vec3i pos, int hit) {
super(pos.getX(), pos.getY(), pos.getZ());
hitType = hit;
this(new Pos(pos), hit);
}
}

View file

@ -1,22 +1,13 @@
package com.minelittlepony.unicopia.ability.data;
import net.minecraft.network.PacketByteBuf;
import java.util.Optional;
public class Numeric extends Hit {
public static final Serializer<Numeric> SERIALIZER = Numeric::new;
public record Numeric (int type) implements Hit {
public static final Serializer<Numeric> SERIALIZER = new Serializer<>(
buf -> new Numeric(buf.readInt()),
(buf, t) -> buf.writeInt(t.type()));
public int type;
Numeric(PacketByteBuf buf) {
type = buf.readInt();
}
@Override
public void toBuffer(PacketByteBuf buf) {
buf.writeInt(type);
}
public Numeric(int t) {
type = t;
public static Optional<Numeric> of(int type) {
return Optional.of(new Numeric(type));
}
}

View file

@ -2,40 +2,19 @@ package com.minelittlepony.unicopia.ability.data;
import com.minelittlepony.unicopia.ability.magic.Caster;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.math.*;
public class Pos extends Hit {
public record Pos (int x, int y, int z) implements Hit {
public static final Serializer<Pos> SERIALIZER = new Serializer<>(
buf -> new Pos(buf.readInt(), buf.readInt(), buf.readInt()),
(buf, t) -> {
buf.writeInt(t.x());
buf.writeInt(t.y());
buf.writeInt(t.z());
});
public static final Serializer<Pos> SERIALIZER = Pos::new;
public final int x;
public final int y;
public final int z;
Pos(PacketByteBuf buf) {
x = buf.readInt();
y = buf.readInt();
z = buf.readInt();
}
@Override
public void toBuffer(PacketByteBuf buf) {
buf.writeInt(x);
buf.writeInt(y);
buf.writeInt(z);
}
public Pos(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public Pos(BlockPos pos) {
x = pos.getX();
y = pos.getY();
z = pos.getZ();
public Pos(Vec3i pos) {
this(pos.getX(), pos.getY(), pos.getZ());
}
public BlockPos pos() {

View file

@ -26,7 +26,7 @@ public record MsgPlayerAbility<T extends Hit> (
Ability<T> power = (Ability<T>) Abilities.REGISTRY.get(buffer.readIdentifier());
return new MsgPlayerAbility<>(
power,
buffer.readOptional(power.getSerializer()::fromBuffer),
buffer.readOptional(power.getSerializer().read()),
ActivationType.of(buffer.readInt())
);
}
@ -34,7 +34,7 @@ public record MsgPlayerAbility<T extends Hit> (
@Override
public void toBuffer(PacketByteBuf buffer) {
buffer.writeIdentifier(Abilities.REGISTRY.getId(power));
buffer.writeOptional(data, (buf, t) -> t.toBuffer(buf));
buffer.writeOptional(data, power.getSerializer().write());
buffer.writeInt(type.ordinal());
}
@ -48,10 +48,9 @@ public record MsgPlayerAbility<T extends Hit> (
if (type != ActivationType.NONE) {
power.onQuickAction(player, type, data);
} else {
data.filter(data -> power.canApply(player, data)).ifPresentOrElse(
data -> power.apply(player, data),
() -> Channel.CANCEL_PLAYER_ABILITY.sendToPlayer(new MsgCancelPlayerAbility(), sender)
);
if (data.filter(data -> power.apply(player, data)).isEmpty()) {
Channel.CANCEL_PLAYER_ABILITY.sendToPlayer(new MsgCancelPlayerAbility(), sender);
}
}
}
}