Move the unicorn shoot ability to the primary slot (second page) and add a separate ability specifically for cancelling spells

This commit is contained in:
Sollace 2021-12-31 15:40:08 +02:00
parent 1b17ab24c9
commit 9723bfd104
8 changed files with 109 additions and 11 deletions

View file

@ -24,6 +24,7 @@ public interface EquinePredicates {
Predicate<Entity> PLAYER_CAN_USE_EARTH = IS_PLAYER.and(raceMatches(Race::canUseEarth));
Predicate<Entity> IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || PLAYER_UNICORN.test(e));
Predicate<Entity> IS_PLACED_SPELL = e -> e instanceof Caster && !e.isRemoved();
Predicate<LivingEntity> HAS_WANT_IT_NEED_IT = e -> EnchantmentHelper.getEquipmentLevel(UEnchantments.WANT_IT_NEED_IT, e) > 0;

View file

@ -15,9 +15,10 @@ public interface Abilities {
// unicorn / alicorn
Ability<?> CAST = register(new UnicornCastingAbility(), "cast", AbilitySlot.PRIMARY);
Ability<?> SHOOT = register(new UnicornProjectileAbility(), "shoot", AbilitySlot.PRIMARY);
Ability<?> TELEPORT = register(new UnicornTeleportAbility(), "teleport", AbilitySlot.SECONDARY);
Ability<?> GROUP_TELEPORT = register(new UnicornGroupTeleportAbility(), "teleport_group", AbilitySlot.SECONDARY);
Ability<?> SHOOT = register(new UnicornProjectileAbility(), "shoot", AbilitySlot.TERTIARY);
Ability<?> DISPELL = register(new UnicornDispellAbility(), "dispell", AbilitySlot.TERTIARY);
// earth / alicorn
Ability<?> KICK = register(new EarthPonyKickAbility(), "kick", AbilitySlot.PRIMARY);

View file

@ -15,7 +15,6 @@ import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.predicate.entity.EntityPredicates;

View file

@ -0,0 +1,91 @@
package com.minelittlepony.unicopia.ability;
import java.util.Optional;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import com.minelittlepony.unicopia.util.VecHelper;
/**
* Dispells an active spell
*/
public class UnicornDispellAbility implements Ability<Pos> {
@Override
public int getWarmupTime(Pony player) {
return 4;
}
@Override
public int getCooldownTime(Pony player) {
return 0;
}
@Override
public boolean canUse(Race race) {
return race.canCast();
}
@Override
public Pos.Serializer<Pos> getSerializer() {
return Pos.SERIALIZER;
}
@Override
public boolean onQuickAction(Pony player, ActivationType type) {
if (type.getTapCount() > 1) {
player.getSpellSlot().clear();
return true;
}
if (type == ActivationType.TAP) {
// TODO: gui to remove spells
}
return false;
}
@Override
public double getCostEstimate(Pony player) {
return getTarget(player)
.filter(caster -> caster.getMaster() != player.getMaster())
.isPresent() ? 10 : 0;
}
@Override
public Pos tryActivate(Pony player) {
return getTarget(player).map(Caster::getOrigin).map(Pos::new).orElse(null);
}
@Override
public void apply(Pony player, Pos data) {
Caster.stream(VecHelper.findInRange(player.getEntity(), player.getWorld(), data.vec(), 2, EquinePredicates.IS_PLACED_SPELL).stream()).forEach(target -> {
target.getSpellSlot().clear();
});
}
private Optional<Caster<?>> getTarget(Pony player) {
int maxDistance = player.getMaster().isCreative() ? 1000 : 100;
return RayTraceHelper.doTrace(player.getMaster(), maxDistance, 1,
EquinePredicates.IS_PLACED_SPELL)
.getEntity()
.flatMap(Caster::of);
}
@Override
public void preApply(Pony player, AbilitySlot slot) {
player.getMagicalReserves().getExhaustion().multiply(3.3F);
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}
@Override
public void postApply(Pony player, AbilitySlot slot) {
}
}

View file

@ -1,13 +1,10 @@
package com.minelittlepony.unicopia.ability.data;
import com.google.gson.annotations.Expose;
import net.minecraft.network.PacketByteBuf;
public class Numeric extends Hit {
public static final Serializer<Numeric> SERIALIZER = Numeric::new;
@Expose
public int type;
Numeric(PacketByteBuf buf) {

View file

@ -4,6 +4,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public class Pos extends Hit {
@ -42,6 +43,10 @@ public class Pos extends Hit {
return new BlockPos(x, y, z);
}
public Vec3d vec() {
return new Vec3d(x, y, z);
}
public double distanceTo(Caster<?> caster) {
return Math.sqrt(caster.getEntity().squaredDistanceTo(x, y, z));
}

View file

@ -93,10 +93,7 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
}
static Stream<Caster<?>> stream(Stream<Entity> entities) {
return entities
.map(Caster::of)
.filter(Optional::isPresent)
.map(Optional::get);
return entities.map(Caster::of).flatMap(Optional::stream);
}
/**

View file

@ -44,6 +44,13 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity>, Lig
getDataTracker().set(SPELL, Optional.ofNullable(spell).map(Spell::getUuid));
SpellContainer.Delegate.super.put(spell);
}
@Override
public void clear() {
getDataTracker().get(SPELL).ifPresent(id -> {
delegate().removeIf(spell -> spell.getUuid().equals(id), true);
});
}
};
private final EntityReference<LivingEntity> owner = new EntityReference<>();
@ -96,11 +103,11 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity>, Lig
emitter.tick();
if (!dataTracker.get(SPELL).filter(spellId -> {
if (dataTracker.get(SPELL).filter(spellId -> {
return getSpellSlot().forEach(spell -> {
return spell.getUuid().equals(spellId) ? Operation.ofBoolean(spell.tick(this, Situation.GROUND_ENTITY)) : Operation.SKIP;
}, true);
}).isPresent()) {
}).isEmpty()) {
discard();
}
}