Add arcane protection spell

This commit is contained in:
Sollace 2022-09-14 12:17:46 +02:00
parent d38005c5bf
commit 3225c7924f
7 changed files with 132 additions and 10 deletions

View file

@ -52,6 +52,9 @@ public class UnicornCastingAbility implements Ability<Hit> {
@Override @Override
@Nullable @Nullable
public Hit tryActivate(Pony player) { public Hit tryActivate(Pony player) {
if (!player.canCast()) {
return null;
}
return Hit.of(player.getMagicalReserves().getMana().get() >= getCostEstimate(player)); return Hit.of(player.getMagicalReserves().getMana().get() >= getCostEstimate(player));
} }
@ -77,6 +80,10 @@ public class UnicornCastingAbility implements Ability<Hit> {
@Override @Override
public void apply(Pony player, Hit data) { public void apply(Pony player, Hit data) {
if (!player.canCast()) {
return;
}
TypedActionResult<ItemStack> amulet = getAmulet(player); TypedActionResult<ItemStack> amulet = getAmulet(player);
if (amulet.getResult().isAccepted()) { if (amulet.getResult().isAccepted()) {

View file

@ -66,6 +66,11 @@ public class UnicornTeleportAbility implements Ability<Pos> {
@Override @Override
public Pos tryActivate(Pony player) { public Pos tryActivate(Pony player) {
if (!player.canCast()) {
return null;
}
int maxDistance = player.getMaster().isCreative() ? 1000 : 100; int maxDistance = player.getMaster().isCreative() ? 1000 : 100;
HitResult ray = RayTraceHelper.doTrace(player.getMaster(), maxDistance, 1, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR).getResult(); HitResult ray = RayTraceHelper.doTrace(player.getMaster(), maxDistance, 1, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR).getResult();
@ -130,6 +135,10 @@ public class UnicornTeleportAbility implements Ability<Pos> {
protected void teleport(Pony teleporter, Caster<?> teleportee, Pos destination) { protected void teleport(Pony teleporter, Caster<?> teleportee, Pos destination) {
if (!teleporter.canCast()) {
return;
}
LivingEntity player = teleportee.getMaster(); LivingEntity player = teleportee.getMaster();
if (player == null) { if (player == null) {

View file

@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.Physics; import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.PonyContainer; import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.particle.ParticleSource; import com.minelittlepony.unicopia.particle.ParticleSource;
@ -18,6 +19,7 @@ import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvent;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules; import net.minecraft.world.GameRules;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -61,6 +63,11 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
} }
default boolean canModifyAt(BlockPos pos) { default boolean canModifyAt(BlockPos pos) {
if (!canCastAt(Vec3d.ofCenter(pos))) {
return false;
}
if (getMaster() instanceof PlayerEntity) { if (getMaster() instanceof PlayerEntity) {
return getReferenceWorld().canPlayerModifyAt((PlayerEntity)getMaster(), pos); return getReferenceWorld().canPlayerModifyAt((PlayerEntity)getMaster(), pos);
} }
@ -94,6 +101,18 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
return findAllEntitiesInRange(radius, null); return findAllEntitiesInRange(radius, null);
} }
default boolean canCast() {
return canCastAt(getOriginVector());
}
default boolean canCastAt(Vec3d pos) {
return findAllSpellsInRange(500, SpellType.ARCANE_PROTECTION::isOn).noneMatch(caster -> caster
.getSpellSlot().get(SpellType.ARCANE_PROTECTION, false)
.filter(spell -> spell.blocksMagicFor(caster, this, pos))
.isPresent()
);
}
static Stream<Caster<?>> stream(Stream<Entity> entities) { static Stream<Caster<?>> stream(Stream<Entity> entities) {
return entities.map(Caster::of).flatMap(Optional::stream); return entities.map(Caster::of).flatMap(Optional::stream);
} }

View file

@ -0,0 +1,75 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.UEntities;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public class AreaProtectionSpell extends AbstractAreaEffectSpell {
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.FOCUS, 50)
.with(Trait.STRENGTH, 30)
.build();
protected AreaProtectionSpell(CustomisedSpellType<?> type) {
super(type);
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
if (situation == Situation.PROJECTILE || situation == Situation.BODY) {
return false;
}
float radius = (float)getDrawDropOffRange(source);
if (source.isClient()) {
Vec3d origin = source.getOriginVector();
source.spawnParticles(origin, new Sphere(true, radius), (int)(radius * 6), pos -> {
if (!source.getReferenceWorld().isAir(new BlockPos(pos))) {
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
}
});
}
source.findAllSpellsInRange(radius, e -> isValidTarget(source, e)).filter(caster -> !caster.hasCommonOwner(source)).forEach(caster -> {
caster.getEntity().kill();
});
return !isDead();
}
/**
* Calculates the maximum radius of the shield. aka The area of effect.
*/
public double getDrawDropOffRange(Caster<?> source) {
float multiplier = source instanceof Pony pony && pony.getMaster().isSneaking() ? 1 : 2;
float min = 4 + getTraits().get(Trait.POWER);
double range = (min + (source.getLevel().getScaled(4) * 2)) / multiplier;
if (source instanceof Pony && range > 2) {
range = Math.sqrt(range);
}
return range;
}
public boolean blocksMagicFor(Caster<?> source, Caster<?> other, Vec3d position) {
return !FriendshipBraceletItem.isComrade(other, other.getEntity())
&& source.getOriginVector().distanceTo(position) <= getDrawDropOffRange(source);
}
protected boolean isValidTarget(Caster<?> source, Entity entity) {
return entity.getType() == UEntities.MAGIC_BEAM;
}
}

View file

@ -50,13 +50,14 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
public static final SpellType<FireSpell> FLAME = register("flame", Affinity.GOOD, 0xFFBB99, true, FireSpell.DEFAULT_TRAITS, FireSpell::new); public static final SpellType<FireSpell> FLAME = register("flame", Affinity.GOOD, 0xFFBB99, true, FireSpell.DEFAULT_TRAITS, FireSpell::new);
public static final SpellType<InfernoSpell> INFERNAL = register("infernal", Affinity.BAD, 0xFFAA00, true, InfernoSpell.DEFAULT_TRAITS, InfernoSpell::new); public static final SpellType<InfernoSpell> INFERNAL = register("infernal", Affinity.BAD, 0xFFAA00, true, InfernoSpell.DEFAULT_TRAITS, InfernoSpell::new);
public static final SpellType<ShieldSpell> SHIELD = register("shield", Affinity.NEUTRAL, 0x66CDAA, true, ShieldSpell.DEFAULT_TRAITS, ShieldSpell::new); public static final SpellType<ShieldSpell> SHIELD = register("shield", Affinity.NEUTRAL, 0x66CDAA, true, ShieldSpell.DEFAULT_TRAITS, ShieldSpell::new);
public static final SpellType<AreaProtectionSpell> ARCANE_PROTECTION = register("arcane_protection", Affinity.BAD, 0x99CDAA, true, AreaProtectionSpell.DEFAULT_TRAITS, AreaProtectionSpell::new);
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", Affinity.NEUTRAL, 0xFFEA88, true, AttractiveSpell.DEFAULT_TRAITS, AttractiveSpell::new); public static final SpellType<AttractiveSpell> VORTEX = register("vortex", Affinity.NEUTRAL, 0xFFEA88, true, AttractiveSpell.DEFAULT_TRAITS, AttractiveSpell::new);
public static final SpellType<DarkVortexSpell> DARK_VORTEX = register("dark_vortex", Affinity.BAD, 0xA33333, true, DarkVortexSpell.DEFAULT_TRAITS, DarkVortexSpell::new); public static final SpellType<DarkVortexSpell> DARK_VORTEX = register("dark_vortex", Affinity.BAD, 0xA33333, true, DarkVortexSpell.DEFAULT_TRAITS, DarkVortexSpell::new);
public static final SpellType<NecromancySpell> NECROMANCY = register("necromancy", Affinity.BAD, 0xFA3A3A, true, NecromancySpell::new); public static final SpellType<NecromancySpell> NECROMANCY = register("necromancy", Affinity.BAD, 0xFA3A3A, true, SpellTraits.EMPTY, NecromancySpell::new);
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", Affinity.NEUTRAL, 0xFFA3AA, true, SiphoningSpell::new); public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", Affinity.NEUTRAL, 0xFFA3AA, true, SpellTraits.EMPTY, SiphoningSpell::new);
public static final SpellType<DisperseIllusionSpell> REVEALING = register("reveal", Affinity.GOOD, 0xFFFFAF, true, DisperseIllusionSpell::new); public static final SpellType<DisperseIllusionSpell> REVEALING = register("reveal", Affinity.GOOD, 0xFFFFAF, true, SpellTraits.EMPTY, DisperseIllusionSpell::new);
public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", Affinity.GOOD, 0x3A59FF, true, AwkwardSpell::new); public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", Affinity.GOOD, 0x3A59FF, true, SpellTraits.EMPTY, AwkwardSpell::new);
public static final SpellType<TransformationSpell> TRANSFORMATION = register("transformation", Affinity.GOOD, 0x19E48E, true, TransformationSpell::new); public static final SpellType<TransformationSpell> TRANSFORMATION = register("transformation", Affinity.GOOD, 0x19E48E, true, SpellTraits.EMPTY, TransformationSpell::new);
public static final SpellType<FeatherFallSpell> FEATHER_FALL = register("feather_fall", Affinity.GOOD, 0x00EEFF, true, FeatherFallSpell.DEFAULT_TRAITS, FeatherFallSpell::new); public static final SpellType<FeatherFallSpell> FEATHER_FALL = register("feather_fall", Affinity.GOOD, 0x00EEFF, true, FeatherFallSpell.DEFAULT_TRAITS, FeatherFallSpell::new);
public static final SpellType<CatapultSpell> CATAPULT = register("catapult", Affinity.GOOD, 0x22FF00, true, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new); public static final SpellType<CatapultSpell> CATAPULT = register("catapult", Affinity.GOOD, 0x22FF00, true, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new);
public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0xFF8811, true, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new); public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0xFF8811, true, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new);
@ -159,11 +160,6 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
return "SpellType[" + getTranslationKey() + "]"; return "SpellType[" + getTranslationKey() + "]";
} }
@Deprecated(forRemoval = true)
public static <T extends Spell> SpellType<T> register(String name, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
return register(name, affinity, color, obtainable, SpellTraits.EMPTY, factory);
}
public static <T extends Spell> SpellType<T> register(String name, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory<T> factory) { public static <T extends Spell> SpellType<T> register(String name, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory<T> factory) {
return register(Unicopia.id(name), affinity, color, obtainable, traits, factory); return register(Unicopia.id(name), affinity, color, obtainable, traits, factory);
} }

View file

@ -146,6 +146,8 @@
"spell.unicopia.fire_bolt.lore": "Produces several burning projectiles", "spell.unicopia.fire_bolt.lore": "Produces several burning projectiles",
"spell.unicopia.shield": "Protection", "spell.unicopia.shield": "Protection",
"spell.unicopia.shield.lore": "Casts a protective shield around the user", "spell.unicopia.shield.lore": "Casts a protective shield around the user",
"spell.unicopia.arcane_protection": "Arcane Protections",
"spell.unicopia.arcane_protection.lore": "Creates a protective shroud over an area in which no other spells can be cast",
"spell.unicopia.vortex": "Arcane Attraction", "spell.unicopia.vortex": "Arcane Attraction",
"spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets", "spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets",
"spell.unicopia.dark_vortex": "Dark Vortex", "spell.unicopia.dark_vortex": "Dark Vortex",

View file

@ -0,0 +1,14 @@
{
"type": "unicopia:spellbook/crafting",
"material": { "item": "unicopia:gemstone", "spell": "unicopia:none" },
"traits": {
"strength": 10, "knowledge": 18
},
"ingredients": [
{ "item": "unicopia:gemstone", "spell": "unicopia:shield" }
],
"result": {
"item": "unicopia:gemstone",
"spell": "unicopia:arcane_protection"
}
}