Some more tweaks and adjustments:

- Adding traits to some existing spells
- Removed curses (and their respective duplicate spells)
- Spells can now control how they are applied and some spells will now correctly apply as area effects
This commit is contained in:
Sollace 2021-12-23 19:46:25 +02:00
parent b11fabdecb
commit 996181c097
20 changed files with 100 additions and 83 deletions

View file

@ -68,7 +68,7 @@ public class PegasusRainboomAbility implements Ability<Hit> {
if (player.getPhysics().isFlying() && !player.getSpellSlot().isPresent()) {
player.getMagicalReserves().getMana().multiply(0.1F);
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
player.getSpellSlot().put(SpellType.RAINBOOM.create(SpellTraits.EMPTY));
SpellType.RAINBOOM.withTraits().apply(player);
}
}

View file

@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellTyp
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.AmuletItem;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.particle.ParticleTypes;
@ -116,7 +117,10 @@ public class UnicornCastingAbility implements Ability<Hit> {
boolean remove = player.getSpellSlot().removeIf(spell, true);
player.subtractEnergyCost(remove ? 2 : 4);
if (!remove) {
spell.apply(player);
if (spell.apply(player) != null) {
player.spawnParticles(ParticleTypes.LARGE_SMOKE, 6);
player.playSound(SoundEvents.ENTITY_ITEM_BREAK, 1, 0.5F);
}
}
}
}
@ -147,13 +151,8 @@ public class UnicornCastingAbility implements Ability<Hit> {
float i = player.getAbilities().getStat(slot).getFillProgress();
Random rng = player.getWorld().random;
player.getWorld().addParticle(i > 0.5F ? ParticleTypes.LARGE_SMOKE : ParticleTypes.CLOUD, eyes.x, eyes.y, eyes.z,
(rng.nextGaussian() - 0.5) / 10,
(rng.nextGaussian() - 0.5) / 10,
(rng.nextGaussian() - 0.5) / 10
);
player.getWorld().playSound(player.getEntity().getX(), player.getEntity().getY(), player.getEntity().getZ(), SoundEvents.ENTITY_GUARDIAN_ATTACK, SoundCategory.PLAYERS, 1, i / 20, true);
player.addParticle(i > 0.5F ? ParticleTypes.LARGE_SMOKE : ParticleTypes.CLOUD, eyes, VecHelper.supply(() -> (rng.nextGaussian() - 0.5) / 10));
player.playSound(SoundEvents.ENTITY_GUARDIAN_ATTACK, 1, i / 20);
} else {
player.spawnParticles(MagicParticleEffect.UNICORN, 5);
}

View file

@ -0,0 +1,19 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.AbstractSpell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
public abstract class AbstractAreaEffectSpell extends AbstractSpell {
protected AbstractAreaEffectSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean apply(Caster<?> source) {
return toPlaceable().apply(source);
}
}

View file

@ -61,12 +61,6 @@ public abstract class AbstractDelegatingSpell implements ProjectileSpell {
return isDirty || getDelegates().stream().anyMatch(Spell::isDirty);
}
@Override
public boolean apply(Caster<?> caster) {
caster.getSpellSlot().put(this);
return true;
}
@Override
public void setDirty() {
isDirty = true;

View file

@ -41,7 +41,10 @@ public interface Spell extends NbtSerialisable, Affine {
/**
* Applies this spell to the supplied caster.
*/
boolean apply(Caster<?> caster);
default boolean apply(Caster<?> caster) {
caster.getSpellSlot().put(this);
return true;
}
/**
* Called to generate this spell's effects.

View file

@ -65,12 +65,6 @@ public abstract class AbstractSpell implements Spell {
return getType().getAffinity();
}
@Override
public final boolean apply(Caster<?> caster) {
caster.getSpellSlot().put(this);
return true;
}
@Override
public void onDestroyed(Caster<?> caster) {
}

View file

@ -1,10 +1,10 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
@ -14,16 +14,16 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class AttractiveSpell extends ShieldSpell {
@Nullable
private BlockPos homingPos;
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.FOCUS, 5)
.with(Trait.KNOWLEDGE, 1)
.with(Trait.STRENGTH, 50)
.with(Trait.AIR, 9)
.build();
protected AttractiveSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
@ -47,7 +47,7 @@ public class AttractiveSpell extends ShieldSpell {
@Override
protected List<Entity> getTargets(Caster<?> source, double radius) {
if (homingPos != null) {
if (getTraits().get(Trait.FOCUS) > 10) {
return VecHelper.findInRange(source.getEntity(), source.getWorld(), source.getOriginVector(), radius, i -> i instanceof ItemEntity);
}
@ -56,7 +56,6 @@ public class AttractiveSpell extends ShieldSpell {
@Override
protected void applyRadialEffect(Caster<?> source, Entity target, double distance, double radius) {
Vec3d pos = homingPos == null ? source.getOriginVector() : Vec3d.of(homingPos);
double force = 2.5F * distance;
@ -70,7 +69,7 @@ public class AttractiveSpell extends ShieldSpell {
source.getEntity().damage(MagicalDamageSource.create("vortex"), 4);
}
applyForce(pos, target, -force, 0);
applyForce(source.getOriginVector(), target, -force, 0);
float maxVel = !isFriendlyTogether(source) ? 1 : 1.6f;
@ -86,21 +85,4 @@ public class AttractiveSpell extends ShieldSpell {
target.setVelocity(x, y, z);
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
if (homingPos != null) {
compound.put("homingPos", NbtHelper.fromBlockPos(homingPos));
}
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
if (compound.contains("homingPos")) {
homingPos = NbtHelper.toBlockPos(compound.getCompound("homingPos"));
}
}
}

View file

@ -4,6 +4,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileSpell;
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.projectile.MagicProjectileEntity;
import net.minecraft.entity.Entity;
@ -11,6 +12,10 @@ import net.minecraft.item.Items;
import net.minecraft.sound.SoundEvents;
public class FireBoltSpell extends AbstractSpell implements ProjectileSpell {
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.FIRE, 90)
.with(Trait.AIR, 60)
.build();
protected FireBoltSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
@ -37,12 +42,12 @@ public class FireBoltSpell extends AbstractSpell implements ProjectileSpell {
@Override
public void configureProjectile(MagicProjectileEntity projectile, Caster<?> caster) {
projectile.setItem(Items.FIRE_CHARGE.getDefaultStack());
projectile.addThrowDamage(9);
projectile.addThrowDamage(getTraits().get(Trait.FIRE) / 10F);
projectile.setFireTicks(900000);
projectile.setVelocity(projectile.getVelocity().multiply(1.3));
projectile.setVelocity(projectile.getVelocity().multiply(1.3 + getTraits().get(Trait.STRENGTH)));
}
protected int getNumberOfBalls(Caster<?> caster) {
return 1 + caster.getWorld().random.nextInt(3);
return 1 + caster.getWorld().random.nextInt(3) + (int)getTraits().get(Trait.POWER) * 3;
}
}

View file

@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileSpell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
@ -35,7 +36,7 @@ import net.minecraft.world.explosion.Explosion.DestructionType;
/**
* Simple fire spell that triggers an effect when used on a block.
*/
public class FireSpell extends AbstractSpell implements ProjectileSpell {
public class FireSpell extends AbstractAreaEffectSpell implements ProjectileSpell {
protected FireSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}

View file

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
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;
@ -23,7 +24,10 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.Difficulty;
import net.minecraft.world.WorldEvents;
public class NecromancySpell extends AbstractSpell {
/**
* An area-effect spell that summons the undead.
*/
public class NecromancySpell extends AbstractAreaEffectSpell {
private final Weighted<EntityType<? extends LivingEntity>> spawnPool = new Weighted<EntityType<? extends LivingEntity>>()
.put(7, EntityType.ZOMBIE)

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
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;
@ -13,9 +14,9 @@ import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d;
/**
* A spell for revealing changelings.
* An area-effect spell that suppresses other forms of magic
*/
public class RevealingSpell extends AbstractSpell {
public class RevealingSpell extends AbstractAreaEffectSpell {
protected RevealingSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}

View file

@ -20,6 +20,11 @@ public class ScorchSpell extends FireSpell {
super(type, traits);
}
@Override
public boolean apply(Caster<?> source) {
return toPlaceable().apply(source);
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
BlockPos pos = PosHelper.findSolidGroundAt(source.getWorld(), source.getOrigin(), source.getPhysics().getGravitySignum());

View file

@ -6,6 +6,7 @@ import java.util.TreeMap;
import java.util.UUID;
import java.util.stream.Collectors;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Caster;
@ -38,7 +39,7 @@ import net.minecraft.util.math.Vec3d;
public class ShieldSpell extends AbstractSpell implements ProjectileSpell {
private final ParticleHandle particlEffect = new ParticleHandle();
protected final ParticleHandle particlEffect = new ParticleHandle();
private final Map<UUID, Target> targets = new TreeMap<>();
@ -52,6 +53,11 @@ public class ShieldSpell extends AbstractSpell implements ProjectileSpell {
particlEffect.destroy();
}
@Override
public Affinity getAffinity() {
return getTraits().get(Trait.DARKNESS) > 0 ? Affinity.BAD : Affinity.GOOD;
}
protected void generateParticles(Caster<?> source) {
float radius = (float)getDrawDropOffRange(source);

View file

@ -4,10 +4,13 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.Race;
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.player.Pony;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
@ -28,7 +31,7 @@ import net.minecraft.util.math.Vec3d;
/**
* A spell that pulls health from other entities and delivers it to the caster.
*/
public class SiphoningSpell extends AbstractSpell {
public class SiphoningSpell extends AbstractAreaEffectSpell {
private int ticksUpset;
@ -36,6 +39,11 @@ public class SiphoningSpell extends AbstractSpell {
super(type, traits);
}
@Override
public Affinity getAffinity() {
return getTraits().get(Trait.DARKNESS) > 0 ? Affinity.BAD : Affinity.GOOD;
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
@ -57,7 +65,6 @@ public class SiphoningSpell extends AbstractSpell {
}
});
} else {
if (source.getWorld().getTime() % 10 != 0) {
return true;
}

View file

@ -33,7 +33,6 @@ import net.minecraft.util.Util;
import net.minecraft.util.registry.Registry;
public final class SpellType<T extends Spell> implements Affine, SpellPredicate<T> {
public static final Identifier EMPTY_ID = new Identifier("unicopia", "null");
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, SpellTraits.EMPTY, (t, c) -> null);
@ -51,19 +50,16 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
public static final SpellType<ScorchSpell> SCORCH = register("scorch", Affinity.BAD, 0, true, ScorchSpell::new);
public static final SpellType<FireSpell> FLAME = register("flame", Affinity.GOOD, 0xFF5D00, true, FireSpell::new);
public static final SpellType<InfernoSpell> INFERNAL = register("infernal", Affinity.BAD, 0xF00F00, true, InfernoSpell::new);
public static final SpellType<ShieldSpell> SHIELD = register("shield", Affinity.GOOD, 0x66CDAA, true, ShieldSpell::new);
public static final SpellType<ShieldSpell> REPULSE = register("repulse", Affinity.BAD, 0x66CDAA, true, ShieldSpell::new);
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", Affinity.GOOD, 0x4CDEE7, true, AttractiveSpell::new);
public static final SpellType<AttractiveSpell> SUFFER = register("suffer", Affinity.BAD, 0x4CDEE7, true, AttractiveSpell::new);
public static final SpellType<ShieldSpell> SHIELD = register("shield", Affinity.NEUTRAL, 0x66CDAA, true, ShieldSpell::new);
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", Affinity.NEUTRAL, 0x4CDEE7, true, AttractiveSpell.DEFAULT_TRAITS, AttractiveSpell::new);
public static final SpellType<NecromancySpell> NECROMANCY = register("necromancy", Affinity.BAD, 0x8A3A3A, true, NecromancySpell::new);
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", Affinity.GOOD, 0xe308ab, true, SiphoningSpell::new);
public static final SpellType<SiphoningSpell> DRAINING = register("draining", Affinity.BAD, 0xe308ab, true, SiphoningSpell::new);
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", Affinity.NEUTRAL, 0xe308ab, true, SiphoningSpell::new);
public static final SpellType<RevealingSpell> REVEALING = register("reveal", Affinity.GOOD, 0x5CE81F, true, RevealingSpell::new);
public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", Affinity.GOOD, 0xE1239C, true, AwkwardSpell::new);
public static final SpellType<TransformationSpell> TRANSFORMATION = register("transformation", Affinity.GOOD, 0x3A59AA, true, 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<CatapultSpell> CATAPULT = register("catapult", Affinity.GOOD, 0x33FF00, true, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new);
public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0x888800, true, FireBoltSpell::new);
public static final SpellType<FireBoltSpell> FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0x888800, true, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new);
private final Identifier id;
private final Affinity affinity;
@ -121,7 +117,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
public String getTranslationKey() {
if (translationKey == null) {
translationKey = Util.createTranslationKey(getAffinity().getTranslationKey(), getId());
translationKey = Util.createTranslationKey("spell", getId());
}
return translationKey;
}

View file

@ -22,8 +22,10 @@ import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Util;
/**
* Transforms whatever entity it strikes into a random other entity.
*/
public class TransformationSpell extends AbstractSpell implements ProjectileSpell {
protected TransformationSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.util;
import java.util.List;
import java.util.function.DoubleSupplier;
import java.util.function.Predicate;
import org.jetbrains.annotations.Nullable;
@ -11,6 +12,10 @@ import net.minecraft.world.World;
public interface VecHelper {
static Vec3d supply(DoubleSupplier rng) {
return new Vec3d(rng.getAsDouble(), rng.getAsDouble(), rng.getAsDouble());
}
static Predicate<Entity> inRange(Vec3d center, double range) {
double rad = Math.pow(range, 2);
return e -> {

View file

@ -131,26 +131,20 @@
"spell.unicopia.frost": "Frost",
"spell.unicopia.frost.lore": "Chilling to the touch, this gem will freeze whatever it is used on",
"curse.unicopia.scorch": "Scorching",
"curse.unicopia.scorch.lore": "Warm to the touch, this gem will burn organic matter",
"spell.unicopia.scorch": "Scorching",
"spell.unicopia.scorch.lore": "Warm to the touch, this gem will burn organic matter",
"spell.unicopia.flame": "Flaming",
"spell.unicopia.flame.lore": "This gem burns hot with a magical flame",
"curse.unicopia.infernal": "Infernal",
"curse.unicopia.infernal.lore": "Conjuring the forces of the nether, will lay waste to everything it touches",
"spell.unicopia.infernal": "Infernal",
"spell.unicopia.infernal.lore": "Conjuring the forces of the nether, will lay waste to everything it touches",
"spell.unicopia.shield": "Protective",
"spell.unicopia.shield.lore": "Casts a protective shield around the user",
"curse.unicopia.repulse": "Repulsive",
"curse.unicopia.repulse.lore": "Casts a protective shield around the user",
"spell.unicopia.vortex": "Attractive",
"spell.unicopia.vortex.lore": "Creates a vortex that draws targets in",
"curse.unicopia.suffer": "Suffering",
"curse.unicopia.suffer.lore": "Creates a vortex that traps and harms targets",
"curse.unicopia.necromancy": "Resurrection",
"curse.unicopia.necromancy.lore": "Summons the undead from the afterlife",
"spell.unicopia.necromancy": "Resurrection",
"spell.unicopia.necromancy.lore": "Summons the undead from the afterlife",
"spell.unicopia.siphoning": "Siphoning",
"spell.unicopia.siphoning.lore": "Channels other creature's life force into the user",
"curse.unicopia.draining": "Siphoning",
"curse.unicopia.draining.lore": "Channels other creature's life force into the user",
"spell.unicopia.reveal": "Revealing",
"spell.unicopia.reveal.lore": "Negates shapeshifting magic",
"spell.unicopia.awkward": "Botched",