Some magic changes and added multi-gem interactions

This commit is contained in:
Sollace 2020-05-28 18:09:34 +02:00
parent 9a91586921
commit 8b3de321f0
19 changed files with 173 additions and 29 deletions

View file

@ -83,7 +83,7 @@ public class DisguiseCommand {
static int reveal(ServerCommandSource source, PlayerEntity player) {
Pony iplayer = Pony.of(player);
iplayer.getEffect(DisguiseSpell.class).ifPresent(disguise -> {
disguise.setDead();
disguise.onDestroyed(iplayer);
});
if (player.getEntityWorld().getGameRules().getBoolean(GameRules.SEND_COMMAND_FEEDBACK)) {

View file

@ -53,7 +53,7 @@ public class Creature implements Ponylike<LivingEntity>, Caster<LivingEntity> {
}
@Override
public <T extends MagicEffect> T getEffect(Class<T> type, boolean update) {
public <T> T getEffect(Class<T> type, boolean update) {
return effectDelegate.get(type, update);
}

View file

@ -4,6 +4,7 @@ import java.util.UUID;
import com.minelittlepony.unicopia.magic.Affinity;
import com.minelittlepony.unicopia.magic.Caster;
import com.minelittlepony.unicopia.magic.EtherialListener;
import com.minelittlepony.unicopia.magic.MagicEffect;
import com.minelittlepony.unicopia.magic.TossedMagicEffect;
import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
@ -34,6 +35,7 @@ import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
@ -52,6 +54,8 @@ public class ProjectileEntity extends ThrownItemEntity implements IMagicals, Adv
private UUID ownerUuid;
private BlockPos lastBlockPos;
public ProjectileEntity(EntityType<ProjectileEntity> type, World world) {
super(type, world);
}
@ -133,7 +137,7 @@ public class ProjectileEntity extends ThrownItemEntity implements IMagicals, Adv
}
@Override
public <T extends MagicEffect> T getEffect(Class<T> type, boolean update) {
public <T> T getEffect(Class<T> type, boolean update) {
return effectDelegate.get(type, update);
}
@ -178,6 +182,12 @@ public class ProjectileEntity extends ThrownItemEntity implements IMagicals, Adv
}
if (hasEffect()) {
if (lastBlockPos == null || !lastBlockPos.equals(getBlockPos())) {
notifyNearbySpells(getEffect(), lastBlockPos, 6, EtherialListener.REMOVED);
lastBlockPos = getBlockPos();
notifyNearbySpells(getEffect(), 6, EtherialListener.ADDED);
}
if (getEffect().isDead()) {
remove();
} else {

View file

@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.magic.Affinity;
import com.minelittlepony.unicopia.magic.Castable;
import com.minelittlepony.unicopia.magic.Caster;
import com.minelittlepony.unicopia.magic.EtherialListener;
import com.minelittlepony.unicopia.magic.MagicEffect;
import com.minelittlepony.unicopia.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.network.EffectSync;
@ -44,7 +45,7 @@ import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion.DestructionType;
public class SpellcastEntity extends MobEntityWithAi implements IMagicals, Caster<LivingEntity>, InAnimate, PickedItemSupplier {
public class SpellcastEntity extends MobEntityWithAi implements IMagicals, Caster<LivingEntity>, EtherialListener, InAnimate, PickedItemSupplier {
private static final TrackedData<Integer> LEVEL = DataTracker.registerData(SpellcastEntity.class, TrackedDataHandlerRegistry.INTEGER);
private static final TrackedData<Optional<UUID>> OWNER = DataTracker.registerData(SpellcastEntity.class, TrackedDataHandlerRegistry.OPTIONAL_UUID);
@ -110,7 +111,7 @@ public class SpellcastEntity extends MobEntityWithAi implements IMagicals, Caste
@Nullable
@Override
public <T extends MagicEffect> T getEffect(@Nullable Class<T> type, boolean update) {
public <T> T getEffect(@Nullable Class<T> type, boolean update) {
return effectDelegate.get(type, update);
}
@ -267,7 +268,7 @@ public class SpellcastEntity extends MobEntityWithAi implements IMagicals, Caste
@Override
public void remove() {
if (hasEffect()) {
getEffect().setDead();
getEffect().onDestroyed(this);
}
super.remove();
}
@ -362,4 +363,14 @@ public class SpellcastEntity extends MobEntityWithAi implements IMagicals, Caste
compound.put("effect", SpellRegistry.instance().serializeEffectToNBT(getEffect()));
}
}
@Override
public void onNearbySpellChange(Caster<?> source, MagicEffect effect, int newState) {
if (hasEffect()) {
EtherialListener listener = getEffect(EtherialListener.class, true);
if (listener != null) {
listener.onNearbySpellChange(source, effect, newState);
}
}
}
}

View file

@ -382,7 +382,7 @@ public class Pony implements Caster<PlayerEntity>, Ponylike<PlayerEntity>, Trans
@Nullable
@Override
public <T extends MagicEffect> T getEffect(@Nullable Class<T> type, boolean update) {
public <T> T getEffect(@Nullable Class<T> type, boolean update) {
return effectDelegate.get(type, update);
}

View file

@ -6,6 +6,7 @@ import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.entity.IMagicals;
import com.minelittlepony.unicopia.entity.Owned;
import com.minelittlepony.unicopia.particles.ParticleSource;
@ -39,7 +40,7 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
* Returns null if no such effect exists for this caster.
*/
@Nullable
<T extends MagicEffect> T getEffect(@Nullable Class<T> type, boolean update);
<T> T getEffect(@Nullable Class<T> type, boolean update);
/**
* Gets the active effect for this caster updating it if needed.
@ -134,4 +135,16 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
default Stream<Entity> findAllEntitiesInRange(double radius) {
return VecHelper.findAllEntitiesInRange(getEntity(), getWorld(), getOrigin(), radius);
}
default void notifyNearbySpells(MagicEffect sender, BlockPos origin, double radius, int newState) {
AwaitTickQueue.enqueueTask(w -> {
VecHelper.findAllEntitiesInRange(getEntity(), getWorld(), origin, radius).filter(i -> i instanceof EtherialListener).forEach(i -> {
((EtherialListener)i).onNearbySpellChange(this, sender, newState);
});
});
}
default void notifyNearbySpells(MagicEffect sender, double radius, int newState) {
notifyNearbySpells(sender, getOrigin(), radius, newState);
}
}

View file

@ -0,0 +1,18 @@
package com.minelittlepony.unicopia.magic;
/**
* A change listener for when a magic spell is either placed or destroyed in the world.
*/
public interface EtherialListener {
int REMOVED = 0x0;
int ADDED = 0x1;
/**
* Called when a nearby spell is added or removed.
*
* @param source The casting source of the sending spell
* @param effect The spell that dispatched the event
* @param state The new state
*/
void onNearbySpellChange(Caster<?> source, MagicEffect effect, int state);
}

View file

@ -65,6 +65,13 @@ public interface MagicEffect extends NbtSerialisable, Affine {
}
/**
* Called when a gem is destroyed.
*/
default void onDestroyed(Caster<?> caster) {
setDead();
}
default boolean handleProjectileImpact(ProjectileEntity projectile) {
return false;
}

View file

@ -3,7 +3,7 @@ package com.minelittlepony.unicopia.magic;
/**
* Magic effects that can be suppressed by other nearby effects.
*/
public interface SuppressableEffect extends MagicEffect {
public interface Suppressable {
/**
* Returns true if this spell is currently still suppressed.

View file

@ -7,14 +7,13 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.entity.SpellcastEntity;
import com.minelittlepony.unicopia.magic.Caster;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
public abstract class AbstractAttachableSpell extends AbstractSpell {
public abstract class AbstractLinkedSpell extends AbstractSpell {
protected boolean searching = true;

View file

@ -1,18 +1,33 @@
package com.minelittlepony.unicopia.magic.spell;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.magic.Affinity;
import com.minelittlepony.unicopia.magic.Caster;
import com.minelittlepony.unicopia.magic.EtherialListener;
import com.minelittlepony.unicopia.magic.MagicEffect;
import com.minelittlepony.unicopia.particles.MagicParticleEffect;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.VecHelper;
import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class AttractiveSpell extends ShieldSpell {
public class AttractiveSpell extends ShieldSpell implements EtherialListener {
@Nullable
private BlockPos homingPos;
@Override
public String getName() {
@ -44,9 +59,21 @@ public class AttractiveSpell extends ShieldSpell {
return 10 + (caster.getCurrentLevel() * 2);
}
@Override
protected List<Entity> getTargets(Caster<?> source, double radius) {
if (homingPos != null) {
return VecHelper.findAllEntitiesInRange(source.getEntity(), source.getWorld(), source.getOrigin(), radius)
.filter(i -> i instanceof ItemEntity)
.collect(Collectors.toList());
}
return super.getTargets(source, radius);
}
@Override
protected void applyRadialEffect(Caster<?> source, Entity target, double distance, double radius) {
Vec3d pos = source.getOriginVector();
Vec3d pos = homingPos == null ? source.getOriginVector() : new Vec3d(homingPos);
double force = 2.5F / distance;
@ -74,4 +101,37 @@ public class AttractiveSpell extends ShieldSpell {
target.setVelocity(x, y, z);
}
@Override
public void onNearbySpellChange(Caster<?> source, MagicEffect effect, int newState) {
if (effect instanceof ChargingSpell && !isDead()) {
if (newState == ADDED) {
if (homingPos == null) {
homingPos = source.getOrigin();
}
setDirty(true);
} else if (homingPos.equals(source.getOrigin())) {
setDead();
setDirty(true);
source.notifyNearbySpells(this, 5, REMOVED);
}
}
}
@Override
public void toNBT(CompoundTag compound) {
super.toNBT(compound);
if (homingPos != null) {
compound.put("homingPos", NbtSerialisable.writeBlockPos(homingPos));
}
}
@Override
public void fromNBT(CompoundTag compound) {
super.fromNBT(compound);
if (compound.contains("homingPos")) {
homingPos = NbtSerialisable.readBlockPos(compound.getCompound("homingPos"));
}
}
}

View file

@ -132,7 +132,7 @@ public class ChangelingTrapSpell extends AbstractSpell implements TossedMagicEff
if (caster.isLocal()) {
if (struggleCounter <= 0) {
setDead();
onDestroyed(caster);
setDirty(true);
WorldEvent.DESTROY_BLOCK.play(caster.getWorld(), origin, Blocks.SLIME_BLOCK.getDefaultState());
@ -166,7 +166,7 @@ public class ChangelingTrapSpell extends AbstractSpell implements TossedMagicEff
struggleCounter = 10;
if (caster.isLocal() && caster.getWorld().random.nextInt(3) == 0) {
setDead();
onDestroyed(caster);
CuccoonEntity cuccoon = UEntities.CUCCOON.create(caster.getWorld());
cuccoon.copyPositionAndRotation(caster.getEntity());

View file

@ -3,6 +3,8 @@ package com.minelittlepony.unicopia.magic.spell;
import com.minelittlepony.unicopia.entity.SpellcastEntity;
import com.minelittlepony.unicopia.magic.Affinity;
import com.minelittlepony.unicopia.magic.Caster;
import com.minelittlepony.unicopia.magic.EtherialListener;
import com.minelittlepony.unicopia.magic.MagicEffect;
import com.minelittlepony.unicopia.particles.MagicParticleEffect;
import com.minelittlepony.unicopia.util.shape.Shape;
import com.minelittlepony.unicopia.util.shape.Line;
@ -10,7 +12,7 @@ import com.minelittlepony.unicopia.util.shape.Line;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
public class ChargingSpell extends AbstractAttachableSpell {
public class ChargingSpell extends AbstractLinkedSpell implements EtherialListener {
private static final Box searchArea = new Box(-15, -15, -15, 15, 15, 15);
@ -71,4 +73,24 @@ public class ChargingSpell extends AbstractAttachableSpell {
return !isDead();
}
@Override
public void onPlaced(Caster<?> caster) {
caster.notifyNearbySpells(this, 12, ADDED);
}
@Override
public void onDestroyed(Caster<?> caster) {
super.onDestroyed(caster);
caster.notifyNearbySpells(this, 12, REMOVED);
}
@Override
public void onNearbySpellChange(Caster<?> source, MagicEffect effect, int newState) {
if (effect instanceof AttractiveSpell && !isDead()) {
setDead();
setDirty(true);
source.notifyNearbySpells(this, 12, newState);
}
}
}

View file

@ -29,7 +29,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
public class DarknessSpell extends AbstractAttachableSpell {
public class DarknessSpell extends AbstractLinkedSpell {
private static final Box searchArea = new Box(-5, -5, -5, 5, 5, 5);

View file

@ -16,7 +16,7 @@ import com.minelittlepony.unicopia.magic.CasterUtils;
import com.minelittlepony.unicopia.magic.AttachedMagicEffect;
import com.minelittlepony.unicopia.magic.Caster;
import com.minelittlepony.unicopia.magic.MagicEffect;
import com.minelittlepony.unicopia.magic.SuppressableEffect;
import com.minelittlepony.unicopia.magic.Suppressable;
import com.minelittlepony.unicopia.particles.MagicParticleEffect;
import com.minelittlepony.unicopia.particles.UParticles;
import com.minelittlepony.unicopia.util.projectile.ProjectileUtil;
@ -45,7 +45,7 @@ import net.minecraft.entity.vehicle.MinecartEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
public class DisguiseSpell extends AbstractSpell implements AttachedMagicEffect, SuppressableEffect, FlightPredicate, HeightPredicate {
public class DisguiseSpell extends AbstractSpell implements AttachedMagicEffect, Suppressable, FlightPredicate, HeightPredicate {
@Nonnull
private String entityId = "";

View file

@ -2,7 +2,7 @@ package com.minelittlepony.unicopia.magic.spell;
import com.minelittlepony.unicopia.magic.Affinity;
import com.minelittlepony.unicopia.magic.Caster;
import com.minelittlepony.unicopia.magic.SuppressableEffect;
import com.minelittlepony.unicopia.magic.Suppressable;
import com.minelittlepony.unicopia.particles.MagicParticleEffect;
import com.minelittlepony.unicopia.util.shape.Shape;
import com.minelittlepony.unicopia.util.shape.Sphere;
@ -32,7 +32,7 @@ public class RevealingSpell extends AbstractSpell {
@Override
public boolean update(Caster<?> source) {
source.findAllSpellsInRange(15).forEach(e -> {
SuppressableEffect spell = e.getEffect(SuppressableEffect.class, false);
Suppressable spell = e.getEffect(Suppressable.class, false);
if (spell != null && spell.isVulnerable(source, this)) {
spell.onSuppressed(source);

View file

@ -66,7 +66,7 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements AttachedMagi
cost *= costMultiplier / 5F;
if (!source.subtractEnergyCost(cost)) {
setDead();
onDestroyed(source);
}
}
}
@ -84,21 +84,25 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements AttachedMagi
return true;
}
protected int applyEntities(Caster<?> source) {
double radius = getDrawDropOffRange(source);
protected List<Entity> getTargets(Caster<?> source, double radius) {
Entity owner = source.getOwner();
boolean ownerIsValid = source.getAffinity() != Affinity.BAD && EquinePredicates.PLAYER_UNICORN.test(owner);
Vec3d origin = source.getOriginVector();
List<Entity> targets = source.findAllEntitiesInRange(radius)
return source.findAllEntitiesInRange(radius)
.filter(entity -> !(ownerIsValid && (
entity.equals(owner)
|| (entity instanceof PlayerEntity && owner instanceof PlayerEntity && Pony.equal((PlayerEntity)entity, (PlayerEntity)owner)))))
.collect(Collectors.toList());
}
protected int applyEntities(Caster<?> source) {
double radius = getDrawDropOffRange(source);
Vec3d origin = source.getOriginVector();
List<Entity> targets = getTargets(source, radius);
targets.forEach(i -> {
try {
double dist = i.getPos().distanceTo(origin);

View file

@ -84,7 +84,7 @@ public class SiphoningSpell extends AbstractRangedAreaSpell {
if (maxHealthGain <= 0) {
if (source.getWorld().random.nextInt(30) == 0) {
setDead();
onDestroyed(source);
} else {
e.damage(damage, e.getHealth() / 4);
}

View file

@ -55,7 +55,7 @@ public class EffectSync {
}
@SuppressWarnings("unchecked")
public <E extends MagicEffect> E get(Class<E> type, boolean update) {
public <E> E get(Class<E> type, boolean update) {
if (!update) {
if (effect == null || type == null || type.isAssignableFrom(effect.getClass())) {
return (E)effect;