Implement traits to modify spell behaviours

This commit is contained in:
Sollace 2021-11-13 19:31:45 +02:00
parent 78a33deb72
commit 2f1d473262
8 changed files with 51 additions and 23 deletions

View file

@ -20,5 +20,5 @@ public interface Suppressable extends Spell {
/** /**
* Event triggered when this effect is suppressed. * Event triggered when this effect is suppressed.
*/ */
void onSuppressed(Caster<?> otherSource); void onSuppressed(Caster<?> otherSource, float suppressTime);
} }

View file

@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.ability.magic.Suppressable;
import com.minelittlepony.unicopia.ability.magic.spell.effect.AbstractSpell; 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.effect.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.behaviour.EntityBehaviour; import com.minelittlepony.unicopia.entity.behaviour.EntityBehaviour;
import com.minelittlepony.unicopia.entity.behaviour.Disguise; import com.minelittlepony.unicopia.entity.behaviour.Disguise;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
@ -59,8 +60,9 @@ public class DisguiseSpell extends AbstractSpell implements Suppressable, Flight
} }
@Override @Override
public void onSuppressed(Caster<?> otherSource) { public void onSuppressed(Caster<?> otherSource, float time) {
suppressionCounter = 100; time /= getTraits().getOrDefault(Trait.STRENGTH, 1);
suppressionCounter = (int)(100 * time);
setDirty(); setDirty();
} }

View file

@ -5,13 +5,13 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileSpell; 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.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.block.state.StateMaps; import com.minelittlepony.unicopia.block.state.StateMaps;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.PosHelper;
import com.minelittlepony.unicopia.util.VecHelper; import com.minelittlepony.unicopia.util.VecHelper;
import com.minelittlepony.unicopia.util.shape.Shape;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.block.Block; import net.minecraft.block.Block;
@ -36,9 +36,6 @@ import net.minecraft.world.explosion.Explosion.DestructionType;
* Simple fire spell that triggers an effect when used on a block. * Simple fire spell that triggers an effect when used on a block.
*/ */
public class FireSpell extends AbstractSpell implements ProjectileSpell { public class FireSpell extends AbstractSpell implements ProjectileSpell {
private static final Shape EFFECT_RANGE = new Sphere(false, 4);
protected FireSpell(SpellType<?> type, SpellTraits traits) { protected FireSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits); super(type, traits);
} }
@ -56,14 +53,14 @@ public class FireSpell extends AbstractSpell implements ProjectileSpell {
generateParticles(source); generateParticles(source);
} }
return PosHelper.getAllInRegionMutable(source.getOrigin(), EFFECT_RANGE).reduce(false, return PosHelper.getAllInRegionMutable(source.getOrigin(), new Sphere(false, Math.max(0, 4 + getTraits().get(Trait.POWER)))).reduce(false,
(r, i) -> applyBlocks(source.getWorld(), i), (r, i) -> applyBlocks(source.getWorld(), i),
(a, b) -> a || b) (a, b) -> a || b)
|| applyEntities(null, source.getWorld(), source.getOriginVector()); || applyEntities(null, source.getWorld(), source.getOriginVector());
} }
protected void generateParticles(Caster<?> source) { protected void generateParticles(Caster<?> source) {
source.spawnParticles(EFFECT_RANGE, (1 + source.getLevel().get()) * 6, pos -> { source.spawnParticles(new Sphere(false, Math.max(0, 4 + getTraits().get(Trait.POWER))), (1 + source.getLevel().get()) * 6, pos -> {
source.addParticle(ParticleTypes.LARGE_SMOKE, pos, Vec3d.ZERO); source.addParticle(ParticleTypes.LARGE_SMOKE, pos, Vec3d.ZERO);
}); });
} }
@ -111,7 +108,7 @@ public class FireSpell extends AbstractSpell implements ProjectileSpell {
} }
protected boolean applyEntities(Entity owner, World world, Vec3d pos) { protected boolean applyEntities(Entity owner, World world, Vec3d pos) {
return !VecHelper.findInRange(owner, world, pos, 3, i -> applyEntitySingle(owner, world, i)).isEmpty(); return !VecHelper.findInRange(owner, world, pos, Math.max(0, 3 + getTraits().get(Trait.POWER)), i -> applyEntitySingle(owner, world, i)).isEmpty();
} }
protected boolean applyEntitySingle(Entity owner, World world, Entity e) { protected boolean applyEntitySingle(Entity owner, World world, Entity e) {

View file

@ -6,6 +6,7 @@ import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; 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.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.util.Weighted; import com.minelittlepony.unicopia.util.Weighted;
import com.minelittlepony.unicopia.util.shape.Shape; import com.minelittlepony.unicopia.util.shape.Shape;
@ -39,7 +40,11 @@ public class NecromancySpell extends AbstractSpell {
@Override @Override
public boolean tick(Caster<?> source, Situation situation) { public boolean tick(Caster<?> source, Situation situation) {
int radius = (source.getLevel().get() + 1) * 4; float radius = (source.getLevel().get() + 1) * 4 + getTraits().get(Trait.POWER);
if (radius <= 0) {
return false;
}
if (source.isClient()) { if (source.isClient()) {
source.spawnParticles(new Sphere(false, radius), 5, pos -> { source.spawnParticles(new Sphere(false, radius), 5, pos -> {
@ -56,7 +61,7 @@ public class NecromancySpell extends AbstractSpell {
summonedEntities.removeIf(ref -> !ref.isPresent(source.getWorld())); summonedEntities.removeIf(ref -> !ref.isPresent(source.getWorld()));
float additional = source.getWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty(); float additional = source.getWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty() + getTraits().get(Trait.CHAOS, 0, 10);
Shape affectRegion = new Sphere(false, radius); Shape affectRegion = new Sphere(false, radius);

View file

@ -4,8 +4,8 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; 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.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.shape.Shape;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundCategory;
@ -16,28 +16,33 @@ import net.minecraft.util.math.Vec3d;
* A spell for revealing changelings. * A spell for revealing changelings.
*/ */
public class RevealingSpell extends AbstractSpell { public class RevealingSpell extends AbstractSpell {
private static final Shape AREA = new Sphere(false, 15);
protected RevealingSpell(SpellType<?> type, SpellTraits traits) { protected RevealingSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits); super(type, traits);
} }
@Override @Override
public boolean tick(Caster<?> source, Situation situation) { public boolean tick(Caster<?> source, Situation situation) {
float range = Math.max(0, 15 + getTraits().get(Trait.POWER));
if (range == 0) {
return false;
}
if (source.isClient()) { if (source.isClient()) {
MagicParticleEffect effect = new MagicParticleEffect(getType().getColor()); MagicParticleEffect effect = new MagicParticleEffect(getType().getColor());
source.spawnParticles(AREA, 5, pos -> { source.spawnParticles(new Sphere(false, range), 5, pos -> {
source.addParticle(effect, pos, Vec3d.ZERO); source.addParticle(effect, pos, Vec3d.ZERO);
}); });
source.spawnParticles(effect, 5); source.spawnParticles(effect, 5);
} }
source.findAllSpellsInRange(15).forEach(e -> { source.findAllSpellsInRange(range).forEach(e -> {
e.getSpellSlot().get(SpellPredicate.CAN_SUPPRESS, false) e.getSpellSlot().get(SpellPredicate.CAN_SUPPRESS, false)
.filter(spell -> spell.isVulnerable(source, this)) .filter(spell -> spell.isVulnerable(source, this))
.ifPresent(spell -> { .ifPresent(spell -> {
spell.onSuppressed(source); spell.onSuppressed(source, 1 + getTraits().get(Trait.STRENGTH));
source.getWorld().playSound(null, e.getOrigin(), SoundEvents.ENTITY_ZOMBIE_VILLAGER_CURE, SoundCategory.PLAYERS, 0.2F, 0.5F); source.getWorld().playSound(null, e.getOrigin(), SoundEvents.ENTITY_ZOMBIE_VILLAGER_CURE, SoundCategory.PLAYERS, 0.2F, 0.5F);
}); });
}); });

View file

@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; 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.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.block.state.StateMaps; import com.minelittlepony.unicopia.block.state.StateMaps;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
@ -24,7 +25,7 @@ public class ScorchSpell extends FireSpell {
BlockPos pos = PosHelper.findSolidGroundAt(source.getWorld(), source.getOrigin(), source.getPhysics().getGravitySignum()); BlockPos pos = PosHelper.findSolidGroundAt(source.getWorld(), source.getOrigin(), source.getPhysics().getGravitySignum());
if (StateMaps.FIRE_AFFECTED.convert(source.getWorld(), pos)) { if (StateMaps.FIRE_AFFECTED.convert(source.getWorld(), pos)) {
source.spawnParticles(new Sphere(false, 1), 5, p -> { source.spawnParticles(new Sphere(false, Math.max(1, getTraits().get(Trait.POWER))), 5, p -> {
source.addParticle(ParticleTypes.SMOKE, PosHelper.offset(p, pos), Vec3d.ZERO); source.addParticle(ParticleTypes.SMOKE, PosHelper.offset(p, pos), Vec3d.ZERO);
}); });
} }

View file

@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileSpell; 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.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
@ -78,12 +79,18 @@ public class ShieldSpell extends AbstractSpell implements ProjectileSpell {
return true; return true;
} }
float knowledge = getTraits().get(Trait.KNOWLEDGE, -6, 6);
if (knowledge == 0) {
knowledge = 1;
}
long costMultiplier = applyEntities(source); long costMultiplier = applyEntities(source);
if (costMultiplier > 0) { if (costMultiplier > 0) {
double cost = 2 + source.getLevel().get(); double cost = 2 + source.getLevel().get();
cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F); cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F);
cost /= 2.725D; cost /= 2.725D;
cost /= knowledge;
if (!source.subtractEnergyCost(cost)) { if (!source.subtractEnergyCost(cost)) {
setDead(); setDead();
@ -98,7 +105,8 @@ public class ShieldSpell extends AbstractSpell implements ProjectileSpell {
*/ */
public double getDrawDropOffRange(Caster<?> source) { public double getDrawDropOffRange(Caster<?> source) {
float multiplier = source.getMaster().isSneaking() ? 1 : 2; float multiplier = source.getMaster().isSneaking() ? 1 : 2;
return (4 + (source.getLevel().get() * 2)) / multiplier; float min = 4 + getTraits().get(Trait.POWER);
return (min + (source.getLevel().get() * 2)) / multiplier;
} }
protected List<Entity> getTargets(Caster<?> source, double radius) { protected List<Entity> getTargets(Caster<?> source, double radius) {

View file

@ -27,6 +27,7 @@ import net.minecraft.nbt.NbtElement;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> { public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
@ -62,7 +63,7 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
public boolean includes(SpellTraits other) { public boolean includes(SpellTraits other) {
return other.stream().allMatch(pair -> { return other.stream().allMatch(pair -> {
return getAmount(pair.getKey()) >= pair.getValue(); return get(pair.getKey()) >= pair.getValue();
}); });
} }
@ -79,8 +80,17 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
return entries().stream(); return entries().stream();
} }
public float getAmount(Trait trait) { public float getOrDefault(Trait trait, float def) {
return traits.getOrDefault(trait, 0F); float i = traits.getOrDefault(trait, def);
return i == 0 ? def : i;
}
public float get(Trait trait) {
return getOrDefault(trait, 0F);
}
public float get(Trait trait, float min, float max) {
return MathHelper.clamp(get(trait), min, max);
} }
public void appendTooltip(List<Text> tooltip) { public void appendTooltip(List<Text> tooltip) {