Further work on spell traits and clean up how projectile spells are handled

This commit is contained in:
Sollace 2021-11-08 14:25:59 +02:00
parent 33476f2ed8
commit 2890bc3e07
22 changed files with 204 additions and 150 deletions

View file

@ -8,8 +8,8 @@ import com.google.common.collect.Streams;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
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.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.AmuletItem; import com.minelittlepony.unicopia.item.AmuletItem;
import com.minelittlepony.unicopia.item.GemstoneItem; import com.minelittlepony.unicopia.item.GemstoneItem;
@ -109,13 +109,13 @@ public class UnicornCastingAbility implements Ability<Hit> {
} }
} }
} else { } else {
TypedActionResult<SpellType<?>> newSpell = getNewSpell(player); TypedActionResult<CustomisedSpellType<?>> newSpell = getNewSpell(player);
if (newSpell.getResult() != ActionResult.FAIL) { if (newSpell.getResult() != ActionResult.FAIL) {
SpellType<?> spell = newSpell.getValue(); CustomisedSpellType<?> spell = newSpell.getValue();
player.subtractEnergyCost(spell.isEmpty() ? 2 : 4); player.subtractEnergyCost(spell.isEmpty() ? 2 : 4);
spell.apply(player, SpellTraits.EMPTY); spell.apply(player);
} }
} }
} }
@ -135,13 +135,13 @@ public class UnicornCastingAbility implements Ability<Hit> {
return TypedActionResult.pass(stack); return TypedActionResult.pass(stack);
} }
private TypedActionResult<SpellType<?>> getNewSpell(Pony player) { private TypedActionResult<CustomisedSpellType<?>> getNewSpell(Pony player) {
final SpellType<?> current = player.getSpellSlot().get(true).map(Spell::getType).orElse(SpellType.empty()); final SpellType<?> current = player.getSpellSlot().get(true).map(Spell::getType).orElse(SpellType.empty());
return Streams.stream(player.getMaster().getItemsHand()) return Streams.stream(player.getMaster().getItemsHand())
.filter(GemstoneItem::isEnchanted) .filter(GemstoneItem::isEnchanted)
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), current, null)) .map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), current, null))
.findFirst() .findFirst()
.orElse(TypedActionResult.<SpellType<?>>pass(current == SpellType.EMPTY_KEY ? SpellType.SHIELD : SpellType.EMPTY_KEY)); .orElse(TypedActionResult.<CustomisedSpellType<?>>pass((current == SpellType.EMPTY_KEY ? SpellType.SHIELD : SpellType.EMPTY_KEY).withTraits()));
} }
@Override @Override

View file

@ -5,9 +5,8 @@ import org.jetbrains.annotations.Nullable;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileCapable; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
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.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.GemstoneItem; import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
@ -63,27 +62,27 @@ public class UnicornProjectileAbility implements Ability<Hit> {
@Override @Override
public void apply(Pony player, Hit data) { public void apply(Pony player, Hit data) {
TypedActionResult<SpellType<?>> thrown = getNewSpell(player); TypedActionResult<CustomisedSpellType<?>> thrown = getNewSpell(player);
if (thrown.getResult() != ActionResult.FAIL) { if (thrown.getResult() != ActionResult.FAIL) {
@Nullable @Nullable
SpellType<?> spell = thrown.getValue(); CustomisedSpellType<?> spell = thrown.getValue();
if (spell == null) { if (spell == null) {
spell = SpellType.VORTEX; spell = SpellType.VORTEX.withTraits();
} }
player.subtractEnergyCost(getCostEstimate(player)); player.subtractEnergyCost(getCostEstimate(player));
((ProjectileCapable)spell.create(SpellTraits.EMPTY)).toss(player); spell.create().toThrowable().throwProjectile(player);
} }
} }
private TypedActionResult<SpellType<?>> getNewSpell(Pony player) { private TypedActionResult<CustomisedSpellType<?>> getNewSpell(Pony player) {
return Streams.stream(player.getMaster().getItemsHand()) return Streams.stream(player.getMaster().getItemsHand())
.filter(GemstoneItem::isEnchanted) .filter(GemstoneItem::isEnchanted)
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), null, null)) .map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), null, null))
.findFirst() .findFirst()
.orElse(TypedActionResult.<SpellType<?>>pass(null)); .orElse(TypedActionResult.<CustomisedSpellType<?>>pass(null));
} }
@Override @Override

View file

@ -2,11 +2,11 @@ package com.minelittlepony.unicopia.ability.magic;
import java.util.function.Predicate; import java.util.function.Predicate;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileCapable; import com.minelittlepony.unicopia.ability.magic.spell.ProjectileSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.Spell;
public interface SpellPredicate<T extends Spell> extends Predicate<Spell> { public interface SpellPredicate<T extends Spell> extends Predicate<Spell> {
SpellPredicate<ProjectileCapable> IS_THROWN = s -> s instanceof ProjectileCapable; SpellPredicate<ProjectileSpell> IS_THROWN = s -> s instanceof ProjectileSpell;
SpellPredicate<Suppressable> IS_SUPPRESSABLE = s -> s instanceof Suppressable; SpellPredicate<Suppressable> IS_SUPPRESSABLE = s -> s instanceof Suppressable;
default boolean isOn(Caster<?> caster) { default boolean isOn(Caster<?> caster) {

View file

@ -10,13 +10,14 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
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.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
public abstract class AbstractDelegatingSpell implements Spell, ProjectileCapable { public abstract class AbstractDelegatingSpell implements ProjectileSpell {
private boolean isDirty; private boolean isDirty;
@ -76,27 +77,30 @@ public abstract class AbstractDelegatingSpell implements Spell, ProjectileCapabl
getDelegates().forEach(spell -> spell.onDestroyed(caster)); getDelegates().forEach(spell -> spell.onDestroyed(caster));
} }
@Override
public boolean tick(Caster<?> source, Situation situation) {
return execute(getDelegates().stream(), spell -> spell.tick(source, situation));
}
@Override @Override
public void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) { public void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {
getDelegates().stream().filter(a -> a instanceof ProjectileCapable).forEach(a -> { getDelegates().stream().filter(a -> a instanceof ProjectileDelegate).forEach(a -> {
((ProjectileCapable)a).onImpact(projectile, pos, state); ((ProjectileDelegate)a).onImpact(projectile, pos, state);
}); });
} }
@Override @Override
public void onImpact(MagicProjectileEntity projectile, Entity entity) { public void onImpact(MagicProjectileEntity projectile, Entity entity) {
getDelegates().stream().filter(a -> a instanceof ProjectileCapable).forEach(a -> { getDelegates().stream().filter(a -> a instanceof ProjectileDelegate).forEach(a -> {
((ProjectileCapable)a).onImpact(projectile, entity); ((ProjectileDelegate)a).onImpact(projectile, entity);
}); });
} }
@Override @Override
public boolean tick(Caster<?> source, Situation situation) { public void configureProjectile(MagicProjectileEntity projectile, Caster<?> caster) {
return execute(getDelegates().stream(), spell -> spell.tick(source, situation)); getDelegates().stream().filter(a -> a instanceof ProjectileSpell).forEach(a -> {
} ((ProjectileSpell)a).configureProjectile(projectile, caster);
});
private boolean execute(Stream<Spell> spells, Function<Spell, Boolean> action) {
return spells.reduce(false, (u, a) -> action.apply(a), (a, b) -> a || b);
} }
@Override @Override
@ -111,4 +115,8 @@ public abstract class AbstractDelegatingSpell implements Spell, ProjectileCapabl
uuid = compound.getUuid("uuid"); uuid = compound.getUuid("uuid");
} }
} }
private static boolean execute(Stream<Spell> spells, Function<Spell, Boolean> action) {
return spells.reduce(false, (u, a) -> action.apply(a), (a, b) -> a || b);
}
} }

View file

@ -26,8 +26,8 @@ public class CompoundSpell extends AbstractDelegatingSpell {
@Override @Override
public Spell combineWith(Spell other) { public Spell combineWith(Spell other) {
if (other instanceof CompoundSpell) { if (other instanceof AbstractDelegatingSpell) {
spells.addAll(((CompoundSpell)other).spells); spells.addAll(((AbstractDelegatingSpell)other).getDelegates());
} else { } else {
spells.add(other); spells.add(other);
} }

View file

@ -86,7 +86,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell {
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), source.getOriginVector(), Vec3d.ZERO); spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> { }).ifPresent(p -> {
p.attach(source); p.attach(source);
p.setAttribute(1, getType().getColor()); p.setAttribute(1, spell.getType().getColor());
}); });
} }

View file

@ -1,88 +0,0 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
/**
* Magic effects that can be thrown.
*/
public interface ProjectileCapable extends Spell, ProjectileDelegate {
@Override
default void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {
if (!projectile.isClient()) {
tick(projectile, Situation.PROJECTILE);
}
}
@Override
default void onImpact(MagicProjectileEntity projectile, Entity entity) {
}
/**
* The amount of damage to be dealt when the projectile collides with an entity.
*/
default int getThrowDamage(Caster<?> stack) {
return 0;
}
/**
* The sound made when thrown.
*/
default SoundEvent getThrowSound(Caster<?> caster) {
return SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT;
}
/**
* Gets the appearance to be used when projecting this spell.
*/
default ItemStack getCastAppearance(Caster<?> caster) {
return GemstoneItem.enchanted(UItems.GEMSTONE.getDefaultStack(), getType());
}
/**
* Projects this spell.
*
* Returns the resulting projectile entity for customization (or null if on the client).
*/
@Nullable
default MagicProjectileEntity toss(Caster<?> caster) {
World world = caster.getWorld();
LivingEntity entity = caster.getMaster();
world.playSound(null, entity.getX(), entity.getY(), entity.getZ(), getThrowSound(caster), SoundCategory.NEUTRAL, 0.7F, 0.4F / (world.random.nextFloat() * 0.4F + 0.8F));
if (!caster.isClient()) {
MagicProjectileEntity projectile = new MagicProjectileEntity(world, entity);
projectile.setItem(getCastAppearance(caster));
projectile.setThrowDamage(getThrowDamage(caster));
projectile.setSpell(this);
projectile.setHydrophobic();
projectile.setProperties(entity, entity.getPitch(), entity.getYaw(), 0, 1.5F, 1);
world.spawnEntity(projectile);
return projectile;
}
return null;
}
}

View file

@ -0,0 +1,23 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
/**
* Magic effects that can be thrown.
*/
public interface ProjectileSpell extends Spell, ProjectileDelegate {
@Override
default void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {
if (!projectile.isClient()) {
tick(projectile, Situation.PROJECTILE);
}
}
default void configureProjectile(MagicProjectileEntity projectile, Caster<?> caster) { }
}

View file

@ -75,4 +75,12 @@ public interface Spell extends NbtSerialisable, Affine {
default PlaceableSpell toPlaceable() { default PlaceableSpell toPlaceable() {
return SpellType.PLACED_SPELL.create(SpellTraits.EMPTY).setSpell(this); return SpellType.PLACED_SPELL.create(SpellTraits.EMPTY).setSpell(this);
} }
/**
* Converts this spell into a throwable spell.
* @return
*/
default ThrowableSpell toThrowable() {
return SpellType.THROWN_SPELL.create(SpellTraits.EMPTY).setSpell(this);
}
} }

View file

@ -0,0 +1,69 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.world.World;
public class ThrowableSpell extends AbstractDelegatingSpell {
private Spell spell;
public ThrowableSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
public ThrowableSpell setSpell(Spell spell) {
this.spell = spell;
return this;
}
@Override
protected Collection<Spell> getDelegates() {
return List.of(spell);
}
/**
* Projects this spell.
*
* Returns the resulting projectile entity for customization (or null if on the client).
*/
public Optional<MagicProjectileEntity> throwProjectile(Caster<?> caster) {
World world = caster.getWorld();
LivingEntity entity = caster.getMaster();
world.playSound(null, entity.getX(), entity.getY(), entity.getZ(), SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.NEUTRAL, 0.7F, 0.4F / (world.random.nextFloat() * 0.4F + 0.8F));
if (!caster.isClient()) {
MagicProjectileEntity projectile = new MagicProjectileEntity(world, entity);
projectile.setItem(GemstoneItem.enchanted(UItems.GEMSTONE.getDefaultStack(), spell.getType()));
projectile.setSpell(this);
projectile.setProperties(entity, entity.getPitch(), entity.getYaw(), 0, 1.5F, 1);
projectile.setHydrophobic();
configureProjectile(projectile, caster);
world.spawnEntity(projectile);
return Optional.of(projectile);
}
return Optional.empty();
}
@Override
public ThrowableSpell toThrowable() {
return this;
}
}

View file

@ -5,7 +5,6 @@ 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.ProjectileCapable;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
@ -17,7 +16,7 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
public class AwkwardSpell extends AbstractSpell implements ProjectileCapable { public class AwkwardSpell extends AbstractSpell {
protected AwkwardSpell(SpellType<?> type, SpellTraits traits) { protected AwkwardSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits); super(type, traits);

View file

@ -0,0 +1,31 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
public class CustomisedSpellType<T extends Spell> {
private final SpellType<T> type;
private final SpellTraits traits;
public CustomisedSpellType(SpellType<T> type, SpellTraits traits) {
this.type = type;
this.traits = traits;
}
public boolean isEmpty() {
return type.isEmpty();
}
public T create() {
return type.create(traits);
}
@Nullable
public T apply(Caster<?> caster) {
return type.apply(caster, traits);
}
}

View file

@ -3,7 +3,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
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.ProjectileCapable; 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.block.state.StateMaps; import com.minelittlepony.unicopia.block.state.StateMaps;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
@ -35,7 +35,7 @@ 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 ProjectileCapable { public class FireSpell extends AbstractSpell implements ProjectileSpell {
private static final Shape EFFECT_RANGE = new Sphere(false, 4); private static final Shape EFFECT_RANGE = new Sphere(false, 4);

View file

@ -2,7 +2,6 @@ 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.ProjectileCapable;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
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;
@ -27,7 +26,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
public class IceSpell extends AbstractSpell implements ProjectileCapable { public class IceSpell extends AbstractSpell {
private final int rad = 3; private final int rad = 3;
private final Shape effect_range = new Sphere(false, rad); private final Shape effect_range = new Sphere(false, rad);

View file

@ -3,7 +3,6 @@ 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.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.ProjectileCapable;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
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.Shape;
@ -16,7 +15,7 @@ import net.minecraft.util.math.Vec3d;
/** /**
* A spell for revealing changelings. * A spell for revealing changelings.
*/ */
public class RevealingSpell extends AbstractSpell implements ProjectileCapable { public class RevealingSpell extends AbstractSpell {
private static final Shape AREA = new Sphere(false, 15); private static final Shape AREA = new Sphere(false, 15);
protected RevealingSpell(SpellType<?> type, SpellTraits traits) { protected RevealingSpell(SpellType<?> type, SpellTraits traits) {

View file

@ -1,7 +1,5 @@
package com.minelittlepony.unicopia.ability.magic.spell.effect; package com.minelittlepony.unicopia.ability.magic.spell.effect;
import org.jetbrains.annotations.Nullable;
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;
@ -42,14 +40,7 @@ public class ScorchSpell extends FireSpell {
} }
@Override @Override
@Nullable public void configureProjectile(MagicProjectileEntity projectile, Caster<?> caster) {
public MagicProjectileEntity toss(Caster<?> caster) {
MagicProjectileEntity projectile = super.toss(caster);
if (projectile != null) {
projectile.setNoGravity(true); projectile.setNoGravity(true);
} }
return projectile;
}
} }

View file

@ -10,7 +10,7 @@ import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
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.ProjectileCapable; 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.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
@ -35,7 +35,7 @@ import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public class ShieldSpell extends AbstractSpell implements ProjectileCapable { public class ShieldSpell extends AbstractSpell implements ProjectileSpell {
private final ParticleHandle particlEffect = new ParticleHandle(); private final ParticleHandle particlEffect = new ParticleHandle();

View file

@ -17,6 +17,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.JoustingSpell; import com.minelittlepony.unicopia.ability.magic.spell.JoustingSpell;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.ThrowableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.util.Registries; import com.minelittlepony.unicopia.util.Registries;
@ -37,6 +38,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
public static final SpellType<CompoundSpell> COMPOUND_SPELL = register("compound", Affinity.NEUTRAL, 0, false, CompoundSpell::new); public static final SpellType<CompoundSpell> COMPOUND_SPELL = register("compound", Affinity.NEUTRAL, 0, false, CompoundSpell::new);
public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, PlaceableSpell::new); public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, PlaceableSpell::new);
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", Affinity.NEUTRAL, 0, false, ThrowableSpell::new);
public static final SpellType<DisguiseSpell> DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, DisguiseSpell::new); public static final SpellType<DisguiseSpell> DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, DisguiseSpell::new);
public static final SpellType<JoustingSpell> RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, JoustingSpell::new); public static final SpellType<JoustingSpell> RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, JoustingSpell::new);
@ -66,12 +68,15 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
@Nullable @Nullable
private String translationKey; private String translationKey;
private final CustomisedSpellType<T> traited;
private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, Factory<T> factory) { private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
this.id = id; this.id = id;
this.affinity = affinity; this.affinity = affinity;
this.color = color; this.color = color;
this.obtainable = obtainable; this.obtainable = obtainable;
this.factory = factory; this.factory = factory;
traited = new CustomisedSpellType<>(this, SpellTraits.EMPTY);
} }
public boolean isObtainable() { public boolean isObtainable() {
@ -105,6 +110,14 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
return new TranslatableText(getTranslationKey()); return new TranslatableText(getTranslationKey());
} }
public CustomisedSpellType<T> withTraits() {
return traited;
}
public CustomisedSpellType<T> withTraits(SpellTraits traits) {
return traits.isEmpty() ? withTraits() : new CustomisedSpellType<>(this, traits);
}
@Nullable @Nullable
public T create(SpellTraits traits) { public T create(SpellTraits traits) {
try { try {

View file

@ -9,7 +9,7 @@ import java.util.Set;
import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.UTags;
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.ProjectileCapable; 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.entity.UEntities; import com.minelittlepony.unicopia.entity.UEntities;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
@ -22,7 +22,7 @@ import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Util; import net.minecraft.util.Util;
public class TransformationSpell extends AbstractSpell implements ProjectileCapable { public class TransformationSpell extends AbstractSpell implements ProjectileSpell {
protected TransformationSpell(SpellType<?> type, SpellTraits traits) { protected TransformationSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits); super(type, traits);

View file

@ -43,19 +43,19 @@ public final class SpellTraits {
return nbt; return nbt;
} }
public Optional<SpellTraits> of(Collection<ItemStack> stacks) { public static Optional<SpellTraits> of(Collection<ItemStack> stacks) {
return fromEntries(stacks.stream().flatMap(a -> of(a).entries().stream())); return fromEntries(stacks.stream().flatMap(a -> of(a).entries().stream()));
} }
public SpellTraits of(ItemStack stack) { public static SpellTraits of(ItemStack stack) {
return getEmbeddedTraits(stack).orElseGet(() -> of(stack.getItem())); return getEmbeddedTraits(stack).orElseGet(() -> of(stack.getItem()));
} }
public SpellTraits of(Item item) { public static SpellTraits of(Item item) {
return TraitLoader.INSTANCE.values.getOrDefault(Registry.ITEM.getId(item), null); return TraitLoader.INSTANCE.values.getOrDefault(Registry.ITEM.getId(item), null);
} }
public SpellTraits of(Block block) { public static SpellTraits of(Block block) {
return of(block.asItem()); return of(block.asItem());
} }

View file

@ -9,7 +9,9 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
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 net.minecraft.client.item.TooltipContext; import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
@ -79,19 +81,20 @@ public class GemstoneItem extends Item {
return super.getName(); return super.getName();
} }
public static TypedActionResult<SpellType<?>> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable SpellType<?> exclude, @Nullable Predicate<SpellType<?>> test) { public static TypedActionResult<CustomisedSpellType<?>> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable SpellType<?> exclude, @Nullable Predicate<SpellType<?>> test) {
if (!isEnchanted(stack)) { if (!isEnchanted(stack)) {
return TypedActionResult.pass(null); return TypedActionResult.pass(null);
} }
SpellType<Spell> key = getSpellKey(stack); SpellType<Spell> key = getSpellKey(stack);
SpellTraits traits = SpellTraits.of(stack);
if (Objects.equals(key, exclude)) { if (Objects.equals(key, exclude)) {
return TypedActionResult.fail(null); return TypedActionResult.fail(null);
} }
if (key == SpellType.EMPTY_KEY || (test == null || !test.test(key))) { if (key.isEmpty() || (test == null || !test.test(key))) {
return TypedActionResult.fail(null); return TypedActionResult.fail(null);
} }
@ -105,7 +108,7 @@ public class GemstoneItem extends Item {
} }
} }
return TypedActionResult.consume(key); return TypedActionResult.consume(key.withTraits(traits));
} }
public static boolean isEnchanted(ItemStack stack) { public static boolean isEnchanted(ItemStack stack) {

View file

@ -8,10 +8,10 @@ public interface ProjectileDelegate {
/** /**
* Called once the projectile lands either hitting the ground or an entity. * Called once the projectile lands either hitting the ground or an entity.
*/ */
void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state); default void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {}
/** /**
* Called once the projectile lands either hitting the ground or an entity. * Called once the projectile lands either hitting the ground or an entity.
*/ */
void onImpact(MagicProjectileEntity projectile, Entity entity); default void onImpact(MagicProjectileEntity projectile, Entity entity) {}
} }