Added spell traits and trait groupings, and rewrite spell casting to make combining spells and catering to different situations easier

This commit is contained in:
Sollace 2021-11-05 15:18:32 +02:00
parent 27f143982e
commit 33476f2ed8
58 changed files with 852 additions and 386 deletions

View file

@ -5,7 +5,7 @@ import java.util.function.Predicate;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments;

View file

@ -11,6 +11,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.minelittlepony.unicopia.ability.data.tree.TreeTypeLoader;
import com.minelittlepony.unicopia.ability.magic.spell.trait.TraitLoader;
import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.command.Commands;
@ -54,6 +55,7 @@ public class Unicopia implements ModInitializer {
});
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(TreeTypeLoader.INSTANCE);
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(UEnchantments.POISONED_JOKE);
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(TraitLoader.INSTANCE);
UBlocks.bootstrap();
UItems.bootstrap();

View file

@ -8,7 +8,7 @@ import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.MagicalDamageSource;

View file

@ -5,7 +5,8 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.UParticles;
@ -59,7 +60,7 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
player.getEntityWorld().playSound(null, player.getBlockPos(), SoundEvents.ENTITY_PARROT_IMITATE_RAVAGER, SoundCategory.PLAYERS, 1.4F, 0.4F);
iplayer.getSpellSlot().get(SpellType.DISGUISE, true)
.orElseGet(() -> SpellType.DISGUISE.apply(iplayer))
.orElseGet(() -> SpellType.DISGUISE.apply(iplayer, SpellTraits.EMPTY))
.setDisguise(looked);
if (!player.isCreative()) {

View file

@ -4,7 +4,8 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.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.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
@ -67,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.setSpell(SpellType.JOUSTING.create());
player.setSpell(SpellType.RAINBOOM.create(SpellTraits.EMPTY));
}
}

View file

@ -7,8 +7,9 @@ import org.jetbrains.annotations.Nullable;
import com.google.common.collect.Streams;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
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.item.AmuletItem;
import com.minelittlepony.unicopia.item.GemstoneItem;
@ -114,7 +115,7 @@ public class UnicornCastingAbility implements Ability<Hit> {
SpellType<?> spell = newSpell.getValue();
player.subtractEnergyCost(spell.isEmpty() ? 2 : 4);
spell.apply(player);
spell.apply(player, SpellTraits.EMPTY);
}
}
}
@ -138,7 +139,7 @@ public class UnicornCastingAbility implements Ability<Hit> {
final SpellType<?> current = player.getSpellSlot().get(true).map(Spell::getType).orElse(SpellType.empty());
return Streams.stream(player.getMaster().getItemsHand())
.filter(GemstoneItem::isEnchanted)
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), current, SpellType::mayAttach))
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), current, null))
.findFirst()
.orElse(TypedActionResult.<SpellType<?>>pass(current == SpellType.EMPTY_KEY ? SpellType.SHIELD : SpellType.EMPTY_KEY));
}

View file

@ -5,8 +5,9 @@ import org.jetbrains.annotations.Nullable;
import com.google.common.collect.Streams;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileCapable;
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.item.GemstoneItem;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
@ -73,14 +74,14 @@ public class UnicornProjectileAbility implements Ability<Hit> {
}
player.subtractEnergyCost(getCostEstimate(player));
((Thrown)spell.create()).toss(player);
((ProjectileCapable)spell.create(SpellTraits.EMPTY)).toss(player);
}
}
private TypedActionResult<SpellType<?>> getNewSpell(Pony player) {
return Streams.stream(player.getMaster().getItemsHand())
.filter(GemstoneItem::isEnchanted)
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), null, SpellType::mayThrow))
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), null, null))
.findFirst()
.orElse(TypedActionResult.<SpellType<?>>pass(null));
}

View file

@ -1,14 +0,0 @@
package com.minelittlepony.unicopia.ability.magic;
/**
* A magic effect that does something when attached to an entity.
*/
public interface Attached extends Spell {
/**
* Called every tick when attached to a living entity.
*
* @param source The entity we are currently attached to.
* @return true to keep alive
*/
boolean onBodyTick(Caster<?> source);
}

View file

@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.particle.ParticleSource;

View file

@ -1,52 +0,0 @@
package com.minelittlepony.unicopia.ability.magic;
import java.util.UUID;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.util.NbtSerialisable;
/**
* Interface for a magic spells
*/
public interface Spell extends NbtSerialisable, Affine {
/**
* Returns the registered type of this spell.
*/
SpellType<?> getType();
/**
* The unique id of this particular spell instance.
*/
UUID getUuid();
/**
* Sets this effect as dead.
*/
void setDead();
/**
* Returns true if this spell is dead, and must be cleaned up.
*/
boolean isDead();
/**
* Returns true if this effect has changes that need to be sent to the client.
*/
boolean isDirty();
/**
* Applies this spell to the supplied caster.
*/
boolean apply(Caster<?> caster);
/**
* Marks this effect as dirty.
*/
void setDirty();
/**
* Called when a gem is destroyed.
*/
void onDestroyed(Caster<?> caster);
}

View file

@ -4,7 +4,7 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.spell.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
public interface SpellContainer {
SpellContainer EMPTY = new SpellContainer() {

View file

@ -0,0 +1,15 @@
package com.minelittlepony.unicopia.ability.magic;
import java.util.function.Predicate;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileCapable;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
public interface SpellPredicate<T extends Spell> extends Predicate<Spell> {
SpellPredicate<ProjectileCapable> IS_THROWN = s -> s instanceof ProjectileCapable;
SpellPredicate<Suppressable> IS_SUPPRESSABLE = s -> s instanceof Suppressable;
default boolean isOn(Caster<?> caster) {
return caster.getSpellSlot().get(this, false).isPresent();
}
}

View file

@ -1,5 +1,7 @@
package com.minelittlepony.unicopia.ability.magic;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
/**
* Magic effects that can be suppressed by other nearby effects.
*/

View file

@ -0,0 +1,114 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.Collection;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Stream;
import com.minelittlepony.unicopia.Affinity;
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.projectile.MagicProjectileEntity;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.BlockPos;
public abstract class AbstractDelegatingSpell implements Spell, ProjectileCapable {
private boolean isDirty;
private UUID uuid = UUID.randomUUID();
private final SpellType<?> type;
public AbstractDelegatingSpell(SpellType<?> type, SpellTraits traits) {
this.type = type;
}
protected abstract Collection<Spell> getDelegates();
@Override
public Affinity getAffinity() {
return Affinity.NEUTRAL;
}
@Override
public SpellType<?> getType() {
return type;
}
@Override
public UUID getUuid() {
return uuid;
}
@Override
public void setDead() {
getDelegates().forEach(Spell::setDead);
}
@Override
public boolean isDead() {
return getDelegates().isEmpty() || getDelegates().stream().allMatch(Spell::isDead);
}
@Override
public boolean isDirty() {
return isDirty || getDelegates().stream().anyMatch(Spell::isDirty);
}
@Override
public boolean apply(Caster<?> caster) {
caster.setSpell(this);
return true;
}
@Override
public void setDirty() {
isDirty = true;
}
@Override
public void onDestroyed(Caster<?> caster) {
getDelegates().forEach(spell -> spell.onDestroyed(caster));
}
@Override
public void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {
getDelegates().stream().filter(a -> a instanceof ProjectileCapable).forEach(a -> {
((ProjectileCapable)a).onImpact(projectile, pos, state);
});
}
@Override
public void onImpact(MagicProjectileEntity projectile, Entity entity) {
getDelegates().stream().filter(a -> a instanceof ProjectileCapable).forEach(a -> {
((ProjectileCapable)a).onImpact(projectile, entity);
});
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
return execute(getDelegates().stream(), spell -> spell.tick(source, situation));
}
private boolean execute(Stream<Spell> spells, Function<Spell, Boolean> action) {
return spells.reduce(false, (u, a) -> action.apply(a), (a, b) -> a || b);
}
@Override
public void toNBT(NbtCompound compound) {
compound.putUuid("uuid", uuid);
}
@Override
public void fromNBT(NbtCompound compound) {
isDirty = false;
if (compound.contains("uuid")) {
uuid = compound.getUuid("uuid");
}
}
}

View file

@ -1,96 +0,0 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.entity.CastSpellEntity;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.UEntities;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.UParticles;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec3d;
public abstract class AbstractPlacedSpell extends AbstractSpell implements Attached {
@Nullable
private Identifier dimension;
private final ParticleHandle particlEffect = new ParticleHandle();
private final EntityReference<CastSpellEntity> castEntity = new EntityReference<>();
protected AbstractPlacedSpell(SpellType<?> type) {
super(type);
}
@Override
public void setDead() {
super.setDead();
particlEffect.destroy();
}
@Override
public boolean onBodyTick(Caster<?> source) {
if (!source.isClient()) {
if (dimension == null) {
dimension = source.getWorld().getRegistryKey().getValue();
setDirty();
} else if (!source.getWorld().getRegistryKey().getValue().equals(dimension)) {
return false;
}
if (!castEntity.isPresent(source.getWorld())) {
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getWorld());
Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector());
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
entity.setSpell(this);
entity.setMaster(source.getMaster());
entity.world.spawnEntity(entity);
castEntity.set(entity);
setDirty();
}
}
return !isDead();
}
public boolean onGroundTick(Caster<?> source) {
particlEffect.ifAbsent(source, spawner -> {
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> {
p.attach(source);
p.setAttribute(1, getType().getColor());
});
return true;
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
if (dimension != null) {
compound.putString("dimension", dimension.toString());
}
compound.put("castEntity", castEntity.toNBT());
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
if (compound.contains("dimension")) {
dimension = new Identifier(compound.getString("dimension"));
}
if (compound.contains("castEntity")) {
castEntity.fromNBT(compound.getCompound("castEntity"));
}
}
}

View file

@ -0,0 +1,57 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
public class CompoundSpell extends AbstractDelegatingSpell {
private final List<Spell> spells = new ArrayList<>();
public CompoundSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
protected Collection<Spell> getDelegates() {
return spells;
}
@Override
public Spell combineWith(Spell other) {
if (other instanceof CompoundSpell) {
spells.addAll(((CompoundSpell)other).spells);
} else {
spells.add(other);
}
return this;
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
NbtList spells = new NbtList();
this.spells.forEach(spell -> {
spells.add(spell.toNBT());
});
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
spells.clear();
if (compound.contains("spells", NbtElement.LIST_TYPE)) {
spells.addAll(compound.getList("spells", NbtElement.COMPOUND_TYPE).stream()
.map(el -> SpellType.fromNBT((NbtCompound)el))
.filter(Objects::nonNull)
.toList());
}
}
}

View file

@ -6,10 +6,11 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.Suppressable;
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;
import com.minelittlepony.unicopia.entity.behaviour.EntityBehaviour;
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -27,14 +28,14 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.nbt.NbtCompound;
public class DisguiseSpell extends AbstractSpell implements Attached, Suppressable, FlightType.Provider, PlayerDimensions.Provider, ProjectileImpactListener {
public class DisguiseSpell extends AbstractSpell implements Suppressable, FlightType.Provider, PlayerDimensions.Provider, ProjectileImpactListener {
private final Disguise disguise = new Disguise();
private int suppressionCounter;
protected DisguiseSpell(SpellType<?> type) {
super(type);
public DisguiseSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
@ -83,8 +84,11 @@ public class DisguiseSpell extends AbstractSpell implements Attached, Suppressab
}
@Override
public boolean onBodyTick(Caster<?> source) {
return update(source, true);
public boolean tick(Caster<?> source, Situation situation) {
if (situation == Situation.BODY) {
return update(source, true);
}
return false;
}
@SuppressWarnings("unchecked")

View file

@ -1,8 +1,10 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.magic.Attached;
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;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
@ -20,7 +22,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules;
public class JoustingSpell extends AbstractSpell implements Attached {
public class JoustingSpell extends AbstractSpell {
private final int rad = 5;
private final Shape effect_range = new Sphere(false, rad);
@ -29,8 +31,8 @@ public class JoustingSpell extends AbstractSpell implements Attached {
private int age;
protected JoustingSpell(SpellType<?> type) {
super(type);
public JoustingSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
@ -40,7 +42,12 @@ public class JoustingSpell extends AbstractSpell implements Attached {
}
@Override
public boolean onBodyTick(Caster<?> source) {
public boolean tick(Caster<?> source, Situation situation) {
if (situation != Situation.BODY) {
return false;
}
if (source.isClient()) {
particlEffect.ifAbsent(source, spawner -> {
spawner.addParticle(UParticles.RAINBOOM_TRAIL, source.getOriginVector(), Vec3d.ZERO);

View file

@ -0,0 +1,120 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.Collection;
import java.util.List;
import org.jetbrains.annotations.Nullable;
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.entity.CastSpellEntity;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.entity.UEntities;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.UParticles;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Vec3d;
// TODO: We'll need a recipe to make a spell placeable
/**
* A spell that can be attached to a specific location in the world.
* <p>
* The spell's effects are still powered by the casting player, so if the player dies or leaves the area, their
* spell loses effect until they return.
*/
public class PlaceableSpell extends AbstractDelegatingSpell {
@Nullable
private Identifier dimension;
private final ParticleHandle particlEffect = new ParticleHandle();
private final EntityReference<CastSpellEntity> castEntity = new EntityReference<>();
private Spell spell;
public PlaceableSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
public PlaceableSpell setSpell(Spell spell) {
this.spell = spell;
return this;
}
@Override
protected Collection<Spell> getDelegates() {
return List.of(spell);
}
@Override
public void setDead() {
super.setDead();
particlEffect.destroy();
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
if (situation == Situation.BODY) {
if (!source.isClient()) {
if (dimension == null) {
dimension = source.getWorld().getRegistryKey().getValue();
setDirty();
} else if (!source.getWorld().getRegistryKey().getValue().equals(dimension)) {
return false;
}
if (!castEntity.isPresent(source.getWorld())) {
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.getWorld());
Vec3d pos = castEntity.getPosition().orElse(source.getOriginVector());
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
entity.setSpell(this);
entity.setMaster(source.getMaster());
entity.world.spawnEntity(entity);
castEntity.set(entity);
setDirty();
}
}
return super.tick(source, Situation.GROUND);
} else if (situation == Situation.GROUND_ENTITY) {
particlEffect.ifAbsent(source, spawner -> {
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> {
p.attach(source);
p.setAttribute(1, getType().getColor());
});
}
return !isDead();
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
if (dimension != null) {
compound.putString("dimension", dimension.toString());
}
compound.put("castEntity", castEntity.toNBT());
}
@Override
public void fromNBT(NbtCompound compound) {
super.toNBT();
if (compound.contains("dimension")) {
dimension = new Identifier(compound.getString("dimension"));
}
if (compound.contains("castEntity")) {
castEntity.fromNBT(compound.getCompound("castEntity"));
}
}
@Override
public PlaceableSpell toPlaceable() {
return this;
}
}

View file

@ -1,7 +1,8 @@
package com.minelittlepony.unicopia.ability.magic;
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;
@ -20,20 +21,12 @@ import net.minecraft.world.World;
/**
* Magic effects that can be thrown.
*/
public interface Thrown extends Spell, ProjectileDelegate {
/**
* Called every tick when attached to an entity.
* Called on both sides.
*
* @param source The entity we are currently attached to.
*/
boolean onThrownTick(MagicProjectileEntity projectile);
public interface ProjectileCapable extends Spell, ProjectileDelegate {
@Override
default void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {
if (!projectile.isClient()) {
onThrownTick(projectile);
tick(projectile, Situation.PROJECTILE);
}
}

View file

@ -0,0 +1,8 @@
package com.minelittlepony.unicopia.ability.magic.spell;
public enum Situation {
BODY,
PROJECTILE,
GROUND,
GROUND_ENTITY
}

View file

@ -0,0 +1,78 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.UUID;
import com.minelittlepony.unicopia.ability.magic.Affine;
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.util.NbtSerialisable;
/**
* Interface for a magic spells
*/
public interface Spell extends NbtSerialisable, Affine {
/**
* Returns the registered type of this spell.
*/
SpellType<?> getType();
/**
* The unique id of this particular spell instance.
*/
UUID getUuid();
/**
* Sets this effect as dead.
*/
void setDead();
/**
* Returns true if this spell is dead, and must be cleaned up.
*/
boolean isDead();
/**
* Returns true if this effect has changes that need to be sent to the client.
*/
boolean isDirty();
/**
* Applies this spell to the supplied caster.
*/
boolean apply(Caster<?> caster);
/**
* Called to generate this spell's effects.
* @param caster The caster currently fueling this spell
* @param situation The situation in which the spell is being applied.
*/
boolean tick(Caster<?> caster, Situation situation);
/**
* Marks this effect as dirty.
*/
void setDirty();
/**
* Called when a gem is destroyed.
*/
void onDestroyed(Caster<?> caster);
/**
* Used by crafting to combine two spells into one.
*
* Returns a compound spell representing the union of this and the other spell.
*/
default Spell combineWith(Spell other) {
return SpellType.COMPOUND_SPELL.create(SpellTraits.EMPTY).combineWith(this).combineWith(other);
}
/**
* Converts this spell into a placeable spell.
*/
default PlaceableSpell toPlaceable() {
return SpellType.PLACED_SPELL.create(SpellTraits.EMPTY).setSpell(this);
}
}

View file

@ -1,19 +0,0 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.function.Predicate;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.Suppressable;
import com.minelittlepony.unicopia.ability.magic.Thrown;
public interface SpellPredicate<T extends Spell> extends Predicate<Spell> {
SpellPredicate<Thrown> IS_THROWN = s -> s instanceof Thrown;
SpellPredicate<Attached> IS_ATTACHED = s -> s instanceof Attached;
SpellPredicate<Suppressable> IS_SUPPRESSABLE = s -> s instanceof Suppressable;
default boolean isOn(Caster<?> caster) {
return caster.getSpellSlot().get(this, false).isPresent();
}
}

View file

@ -1,10 +1,11 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.UUID;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import net.minecraft.nbt.NbtCompound;
@ -15,11 +16,13 @@ public abstract class AbstractSpell implements Spell {
private final SpellType<?> type;
private UUID uuid;
private SpellTraits traits;
protected AbstractSpell(SpellType<?> type) {
private UUID uuid = UUID.randomUUID();
protected AbstractSpell(SpellType<?> type, SpellTraits traits) {
this.type = type;
uuid = UUID.randomUUID();
this.traits = traits;
}
@Override
@ -32,6 +35,10 @@ public abstract class AbstractSpell implements Spell {
return type;
}
protected SpellTraits getTraits() {
return traits == null ? SpellTraits.EMPTY : traits;
}
@Override
public void setDead() {
isDead = true;
@ -59,7 +66,7 @@ public abstract class AbstractSpell implements Spell {
}
@Override
public boolean apply(Caster<?> caster) {
public final boolean apply(Caster<?> caster) {
caster.setSpell(this);
return true;
}
@ -72,6 +79,7 @@ public abstract class AbstractSpell implements Spell {
public void toNBT(NbtCompound compound) {
compound.putBoolean("dead", isDead);
compound.putUuid("uuid", uuid);
compound.put("traits", getTraits().toNbt());
}
@Override
@ -81,5 +89,8 @@ public abstract class AbstractSpell implements Spell {
uuid = compound.getUuid("uuid");
}
isDead = compound.getBoolean("dead");
if (compound.contains("traits")) {
traits = SpellTraits.readNbt(compound.getCompound("traits")).orElse(SpellTraits.EMPTY);
}
}
}

View file

@ -1,10 +1,10 @@
package com.minelittlepony.unicopia.ability.magic.spell;
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.Thrown;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
@ -20,13 +20,13 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class AttractiveSpell extends ShieldSpell implements Thrown {
public class AttractiveSpell extends ShieldSpell {
@Nullable
private BlockPos homingPos;
protected AttractiveSpell(SpellType<?> type) {
super(type);
protected AttractiveSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override

View file

@ -1,10 +1,12 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.ArrayList;
import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.ability.magic.Caster;
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.util.shape.Sphere;
import net.minecraft.particle.ParticleEffect;
@ -15,14 +17,14 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
public class AwkwardSpell extends AbstractSpell implements Thrown {
public class AwkwardSpell extends AbstractSpell implements ProjectileCapable {
protected AwkwardSpell(SpellType<?> type) {
super(type);
protected AwkwardSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean onThrownTick(MagicProjectileEntity source) {
public boolean tick(Caster<?> source, Situation situation) {
if (source.isClient()) {
source.spawnParticles(new Sphere(false, (1 + source.getLevel().get()) * 8), 10, pos -> {

View file

@ -1,9 +1,10 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Thrown;
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.block.state.StateMaps;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
@ -34,12 +35,12 @@ 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 Thrown, Attached {
public class FireSpell extends AbstractSpell implements ProjectileCapable {
private static final Shape EFFECT_RANGE = new Sphere(false, 4);
protected FireSpell(SpellType<?> type) {
super(type);
protected FireSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
@ -50,12 +51,7 @@ public class FireSpell extends AbstractSpell implements Thrown, Attached {
}
@Override
public boolean onThrownTick(MagicProjectileEntity projectile) {
return onBodyTick(projectile);
}
@Override
public boolean onBodyTick(Caster<?> source) {
public boolean tick(Caster<?> source, Situation situation) {
if (source.isClient()) {
generateParticles(source);
}

View file

@ -1,11 +1,11 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Thrown;
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.block.state.StateMaps;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.PosHelper;
import com.minelittlepony.unicopia.util.VecHelper;
@ -27,22 +27,17 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class IceSpell extends AbstractSpell implements Thrown, Attached {
public class IceSpell extends AbstractSpell implements ProjectileCapable {
private final int rad = 3;
private final Shape effect_range = new Sphere(false, rad);
protected IceSpell(SpellType<?> type) {
super(type);
protected IceSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean onThrownTick(MagicProjectileEntity projectile) {
return onBodyTick(projectile);
}
@Override
public boolean onBodyTick(Caster<?> source) {
public boolean tick(Caster<?> source, Situation situation) {
LivingEntity owner = source.getMaster();
PosHelper.getAllInRegionMutable(source.getOrigin(), effect_range)

View file

@ -1,6 +1,8 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.block.state.BlockStateConverter;
import com.minelittlepony.unicopia.block.state.StateMaps;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
@ -16,12 +18,12 @@ import net.minecraft.world.World;
public class InfernoSpell extends FireSpell {
protected InfernoSpell(SpellType<?> type) {
super(type);
protected InfernoSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean onBodyTick(Caster<?> source) {
public boolean tick(Caster<?> source, Situation situation) {
if (source.isClient()) {
generateParticles(source);
}

View file

@ -1,9 +1,11 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.ArrayList;
import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.entity.EntityReference;
import com.minelittlepony.unicopia.util.Weighted;
import com.minelittlepony.unicopia.util.shape.Shape;
@ -20,7 +22,7 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.Difficulty;
import net.minecraft.world.WorldEvents;
public class NecromancySpell extends AbstractPlacedSpell {
public class NecromancySpell extends AbstractSpell {
private final Weighted<EntityType<? extends LivingEntity>> spawnPool = new Weighted<EntityType<? extends LivingEntity>>()
.put(7, EntityType.ZOMBIE)
@ -31,14 +33,12 @@ public class NecromancySpell extends AbstractPlacedSpell {
private final List<EntityReference<LivingEntity>> summonedEntities = new ArrayList<>();
protected NecromancySpell(SpellType<?> type) {
super(type);
protected NecromancySpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean onGroundTick(Caster<?> source) {
super.onGroundTick(source);
public boolean tick(Caster<?> source, Situation situation) {
int radius = (source.getLevel().get() + 1) * 4;
if (source.isClient()) {

View file

@ -1,10 +1,11 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
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.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.util.shape.Shape;
import com.minelittlepony.unicopia.util.shape.Sphere;
@ -15,21 +16,15 @@ import net.minecraft.util.math.Vec3d;
/**
* A spell for revealing changelings.
*/
public class RevealingSpell extends AbstractSpell implements Attached, Thrown {
public class RevealingSpell extends AbstractSpell implements ProjectileCapable {
private static final Shape AREA = new Sphere(false, 15);
protected RevealingSpell(SpellType<?> type) {
super(type);
protected RevealingSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean onThrownTick(MagicProjectileEntity projectile) {
return onBodyTick(projectile);
}
@Override
public boolean onBodyTick(Caster<?> source) {
public boolean tick(Caster<?> source, Situation situation) {
if (source.isClient()) {
MagicParticleEffect effect = new MagicParticleEffect(getType().getColor());

View file

@ -1,8 +1,10 @@
package com.minelittlepony.unicopia.ability.magic.spell;
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.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.block.state.StateMaps;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
@ -15,13 +17,12 @@ import net.minecraft.util.math.Vec3d;
public class ScorchSpell extends FireSpell {
protected ScorchSpell(SpellType<?> type) {
super(type);
protected ScorchSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean onBodyTick(Caster<?> source) {
public boolean tick(Caster<?> source, Situation situation) {
BlockPos pos = PosHelper.findSolidGroundAt(source.getWorld(), source.getOrigin(), source.getPhysics().getGravitySignum());
if (StateMaps.FIRE_AFFECTED.convert(source.getWorld(), pos)) {

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import java.util.Map;
@ -8,16 +8,16 @@ import java.util.stream.Collectors;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Thrown;
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.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.SphereParticleEffect;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileUtil;
import com.minelittlepony.unicopia.util.shape.Sphere;
@ -35,14 +35,14 @@ import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d;
public class ShieldSpell extends AbstractSpell implements Attached, Thrown {
public class ShieldSpell extends AbstractSpell implements ProjectileCapable {
private final ParticleHandle particlEffect = new ParticleHandle();
private final Map<UUID, Target> targets = new TreeMap<>();
protected ShieldSpell(SpellType<?> type) {
super(type);
protected ShieldSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
@ -68,19 +68,14 @@ public class ShieldSpell extends AbstractSpell implements Attached, Thrown {
}
@Override
public boolean onThrownTick(MagicProjectileEntity source) {
public boolean tick(Caster<?> source, Situation situation) {
if (source.isClient()) {
generateParticles(source);
}
applyEntities(source);
return true;
}
@Override
public boolean onBodyTick(Caster<?> source) {
if (source.isClient()) {
generateParticles(source);
if (situation == Situation.PROJECTILE) {
applyEntities(source);
return true;
}
long costMultiplier = applyEntities(source);
@ -216,8 +211,7 @@ public class ShieldSpell extends AbstractSpell implements Attached, Thrown {
int cooldown = 20;
Target(UUID id) {
}
Target(UUID id) { }
boolean tick() {
return --cooldown < 0;

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.List;
import java.util.stream.Collectors;
@ -6,6 +6,8 @@ import java.util.stream.Stream;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils;
@ -26,17 +28,16 @@ import net.minecraft.util.math.Vec3d;
/**
* A spell that pulls health from other entities and delivers it to the caster.
*/
public class SiphoningSpell extends AbstractPlacedSpell {
public class SiphoningSpell extends AbstractSpell {
private int ticksUpset;
protected SiphoningSpell(SpellType<?> type) {
super(type);
protected SiphoningSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean onGroundTick(Caster<?> source) {
super.onGroundTick(source);
public boolean tick(Caster<?> source, Situation situation) {
if (ticksUpset > 0) {
ticksUpset--;

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.EnumMap;
import java.util.HashSet;
@ -11,7 +11,13 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.CompoundSpell;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.JoustingSpell;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.util.Registries;
import net.minecraft.nbt.NbtCompound;
@ -24,11 +30,17 @@ 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, t -> null);
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, (t, c) -> null);
private static final Registry<SpellType<?>> REGISTRY = Registries.createSimple(new Identifier("unicopia", "spells"));
private static final Map<Affinity, Set<SpellType<?>>> BY_AFFINITY = new EnumMap<>(Affinity.class);
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<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<IceSpell> FROST = register("frost", Affinity.GOOD, 0xBDBDF9, true, IceSpell::new);
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);
@ -40,9 +52,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
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<DisguiseSpell> DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, DisguiseSpell::new);
public static final SpellType<RevealingSpell> REVEALING = register("reveal", Affinity.GOOD, 0x5CE81F, true, RevealingSpell::new);
public static final SpellType<JoustingSpell> JOUSTING = register("joust", Affinity.GOOD, 0xBDBDF9, false, JoustingSpell::new);
public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", Affinity.GOOD, 0xE1239C, true, AwkwardSpell::new);
public static final SpellType<TransformationSpell> TRANSFORMATION = register("transformation", Affinity.NEUTRAL, 0x3A59AA, true, TransformationSpell::new);
@ -53,9 +63,6 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
private final Factory<T> factory;
private final boolean thrown;
private final boolean attached;
@Nullable
private String translationKey;
@ -65,24 +72,12 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
this.color = color;
this.obtainable = obtainable;
this.factory = factory;
Spell inst = create();
thrown = SpellPredicate.IS_THROWN.test(inst);
attached = SpellPredicate.IS_ATTACHED.test(inst);
}
public boolean isObtainable() {
return obtainable;
}
public boolean mayThrow() {
return thrown;
}
public boolean mayAttach() {
return attached;
}
public Identifier getId() {
return id;
}
@ -111,9 +106,9 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
}
@Nullable
public T create() {
public T create(SpellTraits traits) {
try {
return factory.create(this);
return factory.create(this, traits);
} catch (Exception e) {
e.printStackTrace();
}
@ -122,13 +117,13 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
}
@Nullable
public T apply(Caster<?> caster) {
public T apply(Caster<?> caster, SpellTraits traits) {
if (isEmpty()) {
caster.setSpell(null);
return null;
}
T spell = create();
T spell = create(traits);
if (spell.apply(caster)) {
return spell;
}
@ -177,7 +172,7 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
@Nullable
public static Spell fromNBT(@Nullable NbtCompound compound) {
if (compound != null && compound.contains("effect_id")) {
Spell effect = getKey(new Identifier(compound.getString("effect_id"))).create();
Spell effect = getKey(new Identifier(compound.getString("effect_id"))).create(SpellTraits.EMPTY);
if (effect != null) {
effect.fromNBT(compound);
@ -198,6 +193,6 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
}
public interface Factory<T extends Spell> {
T create(SpellType<T> type);
T create(SpellType<T> type, SpellTraits traits);
}
}

View file

@ -1,4 +1,4 @@
package com.minelittlepony.unicopia.ability.magic.spell;
package com.minelittlepony.unicopia.ability.magic.spell.effect;
import java.util.ArrayList;
import java.util.HashSet;
@ -7,7 +7,10 @@ import java.util.Random;
import java.util.Set;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.ability.magic.Caster;
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.entity.UEntities;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
@ -19,15 +22,15 @@ import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Util;
public class TransformationSpell extends AbstractSpell implements Thrown {
public class TransformationSpell extends AbstractSpell implements ProjectileCapable {
protected TransformationSpell(SpellType<?> type) {
super(type);
protected TransformationSpell(SpellType<?> type, SpellTraits traits) {
super(type, traits);
}
@Override
public boolean onThrownTick(MagicProjectileEntity source) {
return true;
public boolean tick(Caster<?> caster, Situation situation) {
return situation == Situation.PROJECTILE;
}
@Override

View file

@ -0,0 +1,96 @@
package com.minelittlepony.unicopia.ability.magic.spell.trait;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.util.registry.Registry;
public final class SpellTraits {
public static final SpellTraits EMPTY = new SpellTraits(Map.of());
private final Map<Trait, Float> traits;
SpellTraits(Map<Trait, Float> traits) {
this.traits = traits;
}
public boolean isEmpty() {
return traits.isEmpty();
}
public Set<Map.Entry<Trait, Float>> entries() {
return traits.entrySet();
}
public float getAmount(Trait trait) {
return traits.getOrDefault(trait, 0F);
}
public NbtCompound toNbt() {
NbtCompound nbt = new NbtCompound();
traits.forEach((key, value) -> nbt.putFloat(key.name(), value));
return nbt;
}
public Optional<SpellTraits> of(Collection<ItemStack> stacks) {
return fromEntries(stacks.stream().flatMap(a -> of(a).entries().stream()));
}
public SpellTraits of(ItemStack stack) {
return getEmbeddedTraits(stack).orElseGet(() -> of(stack.getItem()));
}
public SpellTraits of(Item item) {
return TraitLoader.INSTANCE.values.getOrDefault(Registry.ITEM.getId(item), null);
}
public SpellTraits of(Block block) {
return of(block.asItem());
}
private static Optional<SpellTraits> getEmbeddedTraits(ItemStack stack) {
if (!(stack.hasTag() && stack.getTag().contains("spell_traits", NbtElement.COMPOUND_TYPE))) {
return Optional.empty();
}
return readNbt(stack.getTag().getCompound("spell_traits"));
}
public static Optional<SpellTraits> readNbt(NbtCompound traits) {
return fromEntries(streamFromNbt(traits));
}
public static Stream<Map.Entry<Trait, Float>> streamFromNbt(NbtCompound traits) {
return traits.getKeys().stream().map(key -> {
Trait trait = Trait.REGISTRY.get(key.toUpperCase());
if (trait == null && !traits.contains(key, NbtElement.NUMBER_TYPE)) {
return null;
}
return Map.entry(trait, traits.getFloat(key));
});
}
public static Optional<SpellTraits> fromEntries(Stream<Map.Entry<Trait, Float>> entries) {
var result = collect(entries);
if (result.isEmpty()) {
return Optional.empty();
}
return Optional.of(new SpellTraits(result));
}
static Map<Trait, Float> collect(Stream<Map.Entry<Trait, Float>> entries) {
return entries.filter(Objects::nonNull)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a + b, () -> new EnumMap<>(Trait.class)));
}
}

View file

@ -0,0 +1,43 @@
package com.minelittlepony.unicopia.ability.magic.spell.trait;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.minecraft.util.Identifier;
public enum Trait {
LIFE(TraitGroup.NATURE),
ENERGY(TraitGroup.NATURE),
REBIRTH(TraitGroup.NATURE),
GROWTH(TraitGroup.NATURE),
WATER(TraitGroup.ELEMENTAL),
EARTH(TraitGroup.ELEMENTAL),
FIRE(TraitGroup.ELEMENTAL),
AIR(TraitGroup.ELEMENTAL),
CORRUPTION(TraitGroup.DARKNESS),
DEATH(TraitGroup.DARKNESS),
FAMINE(TraitGroup.DARKNESS),
PESTILENCE(TraitGroup.DARKNESS);
public static final Map<String, Trait> REGISTRY = Arrays.stream(values()).collect(Collectors.toMap(Trait::name, Function.identity()));
private final Identifier id;
private final TraitGroup group;
Trait(TraitGroup group) {
this.id = new Identifier("unicopia", "spell/trait/" + name().toLowerCase());
this.group = group;
}
public Identifier getId() {
return id;
}
public TraitGroup getGroup() {
return group;
}
}

View file

@ -0,0 +1,18 @@
package com.minelittlepony.unicopia.ability.magic.spell.trait;
public enum TraitGroup {
NATURE(-0.2F),
DARKNESS(0.4F),
ELEMENTAL(-0.02F);
private final float corruption;
TraitGroup(float corruption) {
this.corruption = corruption;
}
// TODO: implement corruption mechanics
public float getCorruption() {
return corruption;
}
}

View file

@ -0,0 +1,90 @@
package com.minelittlepony.unicopia.ability.magic.spell.trait;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.reflect.TypeToken;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.util.Resources;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.minecraft.resource.Resource;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.SinglePreparationResourceReloader;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.profiler.Profiler;
public class TraitLoader extends SinglePreparationResourceReloader<Map<Identifier, SpellTraits>> implements IdentifiableResourceReloadListener {
private static final Identifier ID = new Identifier("unicopia", "data/traits");
private static final TypeToken<Map<String, String>> TYPE = new TypeToken<>() {};
public static final TraitLoader INSTANCE = new TraitLoader();
Map<Identifier, SpellTraits> values = new HashMap<>();
@Override
public Identifier getFabricId() {
return ID;
}
@Override
protected Map<Identifier, SpellTraits> prepare(ResourceManager manager, Profiler profiler) {
profiler.startTick();
Map<Identifier, SpellTraits> prepared = new HashMap<>();
for (String namespace : manager.getAllNamespaces()) {
profiler.push(namespace);
try {
for (Resource resource : manager.getAllResources(new Identifier(namespace, "items/traits"))) {
profiler.push(resource.getResourcePackName());
try (InputStreamReader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
Map<String, String> data = JsonHelper.deserialize(Resources.GSON, reader, TYPE);
data.forEach((name, set) -> {
try {
Identifier id = new Identifier(name);
SpellTraits.fromEntries(Arrays.stream(set.split(" ")).map(a -> a.split(":")).map(pair -> {
Trait key = Trait.REGISTRY.get(pair[0].toUpperCase());
if (key == null) {
Unicopia.LOGGER.warn("Failed to load trait entry for item {} in {}. {} is not a valid trait", id, resource.getResourcePackName(), pair[0]);
return null;
}
try {
return Map.entry(key, Float.parseFloat(pair[1]));
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
Unicopia.LOGGER.warn("Failed to load trait entry for item {} in {}. {} is not a valid weighting", id, resource.getResourcePackName(), Arrays.toString(pair));
return null;
}
})).ifPresent(value -> prepared.put(id, value));
} catch (InvalidIdentifierException e) {
Unicopia.LOGGER.warn("Failed to load traits for item {} in {}.", name, resource.getResourcePackName(), e);
}
});
} finally {
profiler.pop();
}
}
} catch (IOException e) {
} finally {
profiler.pop();
}
}
return prepared;
}
@Override
protected void apply(Map<Identifier, SpellTraits> prepared, ResourceManager manager, Profiler profiler) {
values = prepared;
}
}

View file

@ -10,7 +10,7 @@ import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ability.AbilitySlot;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.KeyBindingsHandler;
import com.minelittlepony.unicopia.client.sound.LoopingSoundInstance;
import com.minelittlepony.unicopia.entity.behaviour.Disguise;

View file

@ -3,7 +3,7 @@ package com.minelittlepony.unicopia.client.render;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.ItemImpl;
import com.minelittlepony.unicopia.entity.Living;

View file

@ -3,8 +3,9 @@ package com.minelittlepony.unicopia.command;
import java.util.function.Function;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
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.mojang.authlib.GameProfile;
import com.mojang.brigadier.CommandDispatcher;
@ -83,7 +84,7 @@ public class DisguiseCommand {
Pony iplayer = Pony.of(player);
iplayer.getSpellSlot().get(SpellType.DISGUISE, true)
.orElseGet(() -> SpellType.DISGUISE.apply(iplayer))
.orElseGet(() -> SpellType.DISGUISE.apply(iplayer, SpellTraits.EMPTY))
.setDisguise(entity);
if (source.getEntity() == player) {

View file

@ -6,10 +6,9 @@ import java.util.UUID;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractPlacedSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
@ -32,8 +31,6 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
private static final TrackedData<Optional<UUID>> SPELL = DataTracker.registerData(CastSpellEntity.class, TrackedDataHandlerRegistry.OPTIONAL_UUID);
private static final SpellPredicate<AbstractPlacedSpell> SPELL_TYPE = s -> s instanceof AbstractPlacedSpell;
private static final LevelStore LEVELS = Levelled.fixed(0);
private final EntityPhysics<CastSpellEntity> physics = new EntityPhysics<>(this, GRAVITY);
@ -96,7 +93,7 @@ public class CastSpellEntity extends Entity implements Caster<LivingEntity> {
if (!Caster.of(master).filter(c -> {
UUID spellId = dataTracker.get(SPELL).orElse(null);
if (!c.getSpellSlot().get(SPELL_TYPE, true).filter(s -> s.getUuid().equals(spellId) && s.onGroundTick(this)).isPresent()) {
if (!c.getSpellSlot().get(true).filter(s -> s.getUuid().equals(spellId) && s.tick(this, Situation.GROUND_ENTITY)).isPresent()) {
c.setSpell(null);
return false;

View file

@ -4,7 +4,7 @@ import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.ai.BreakHeartGoal;
import com.minelittlepony.unicopia.entity.ai.DynamicTargetGoal;
import com.minelittlepony.unicopia.entity.ai.WantItTakeItGoal;

View file

@ -7,8 +7,8 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.spell.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.EffectSync;
import com.minelittlepony.unicopia.projectile.ProjectileImpactListener;
@ -85,8 +85,8 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
@Override
public void tick() {
getSpellSlot().get(SpellPredicate.IS_ATTACHED, true).ifPresent(effect -> {
if (!effect.onBodyTick(this)) {
getSpellSlot().get(true).ifPresent(effect -> {
if (!effect.tick(this, Situation.BODY)) {
setSpell(null);
}
});

View file

@ -5,7 +5,7 @@ import java.util.Random;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.item.UItems;
import net.fabricmc.fabric.api.object.builder.v1.trade.TradeOfferHelper;

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.entity.behaviour;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import net.minecraft.entity.passive.AxolotlEntity;
import net.minecraft.util.math.Vec3f;

View file

@ -15,7 +15,7 @@ import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.player.PlayerAttributes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.projectile.ProjectileUtil;

View file

@ -4,7 +4,7 @@ import java.util.Optional;
import com.minelittlepony.common.util.animation.MotionCompositor;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import net.minecraft.util.math.Vec3d;

View file

@ -4,7 +4,7 @@ import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.EntityPhysics;
@ -94,7 +94,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
@Override
public boolean isRainbooming() {
return pony.getSpellSlot().get(SpellType.JOUSTING, true).isPresent();
return pony.getSpellSlot().get(SpellType.RAINBOOM, true).isPresent();
}
@Override

View file

@ -16,8 +16,8 @@ import com.minelittlepony.unicopia.WorldTribeManager;
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ability.EarthPonyStompAbility;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.advancement.UCriteria;
import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.entity.Living;

View file

@ -8,8 +8,8 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.player.PlayerEntity;
@ -79,7 +79,7 @@ public class GemstoneItem extends Item {
return super.getName();
}
public static TypedActionResult<SpellType<?>> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable SpellType<?> exclude, Predicate<SpellType<?>> test) {
public static TypedActionResult<SpellType<?>> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable SpellType<?> exclude, @Nullable Predicate<SpellType<?>> test) {
if (!isEnchanted(stack)) {
return TypedActionResult.pass(null);
@ -91,7 +91,7 @@ public class GemstoneItem extends Item {
return TypedActionResult.fail(null);
}
if (key == SpellType.EMPTY_KEY || !test.test(key)) {
if (key == SpellType.EMPTY_KEY || (test == null || !test.test(key))) {
return TypedActionResult.fail(null);
}

View file

@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.entity.behaviour.Disguise;

View file

@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.player.Pony;

View file

@ -6,10 +6,10 @@ import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.spell.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.nbt.NbtCompound;

View file

@ -5,7 +5,7 @@ import java.util.UUID;
import java.util.function.Consumer;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;

View file

@ -6,10 +6,11 @@ import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.spell.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.UEntities;
@ -162,7 +163,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Caster<Li
lastBlockPos = getBlockPos();
}
if (!getSpellSlot().get(SpellPredicate.IS_THROWN, true).filter(spell -> spell.onThrownTick(this)).isPresent()) {
if (!getSpellSlot().get(true).filter(spell -> spell.tick(this, Situation.PROJECTILE)).isPresent()) {
discard();
}
}