mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 23:27:59 +01:00
Make the necromancy spell better
This commit is contained in:
parent
fb3a642acd
commit
d03c0586cf
6 changed files with 141 additions and 34 deletions
|
@ -95,9 +95,7 @@ public class LightSpell extends AbstractSpell {
|
|||
lights.clear();
|
||||
if (compound.contains("lights", NbtElement.LIST_TYPE)) {
|
||||
compound.getList("lights", NbtElement.COMPOUND_TYPE).forEach(nbt -> {
|
||||
EntityReference<FairyEntity> light = new EntityReference<>();
|
||||
light.fromNBT((NbtCompound)nbt);
|
||||
lights.add(light);
|
||||
lights.add(new EntityReference<>((NbtCompound)nbt));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,22 @@ import com.minelittlepony.unicopia.ability.magic.spell.AbstractAreaEffectSpell;
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.entity.Creature;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
import com.minelittlepony.unicopia.entity.Equine;
|
||||
import com.minelittlepony.unicopia.util.Weighted;
|
||||
import com.minelittlepony.unicopia.util.shape.Shape;
|
||||
import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.mob.ZombieEntity;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.nbt.NbtList;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.Difficulty;
|
||||
|
@ -28,16 +33,17 @@ import net.minecraft.world.WorldEvents;
|
|||
* An area-effect spell that summons the undead.
|
||||
*/
|
||||
public class NecromancySpell extends AbstractAreaEffectSpell {
|
||||
|
||||
private final Weighted<EntityType<? extends LivingEntity>> spawnPool = new Weighted<EntityType<? extends LivingEntity>>()
|
||||
.put(7, EntityType.ZOMBIE)
|
||||
.put(4, EntityType.HUSK)
|
||||
.put(3, EntityType.DROWNED)
|
||||
.put(2, EntityType.ZOMBIFIED_PIGLIN)
|
||||
.put(1, EntityType.ZOMBIE_VILLAGER)
|
||||
.put(1, EntityType.ZOMBIE_HORSE);
|
||||
.put(1, EntityType.ZOMBIE_VILLAGER);
|
||||
|
||||
private final List<EntityReference<LivingEntity>> summonedEntities = new ArrayList<>();
|
||||
|
||||
private int spawnCountdown;
|
||||
|
||||
protected NecromancySpell(SpellType<?> type, SpellTraits traits) {
|
||||
super(type, traits);
|
||||
}
|
||||
|
@ -50,10 +56,14 @@ public class NecromancySpell extends AbstractAreaEffectSpell {
|
|||
return false;
|
||||
}
|
||||
|
||||
boolean rainy = source.getWorld().hasRain(source.getOrigin());
|
||||
|
||||
if (source.isClient()) {
|
||||
source.spawnParticles(new Sphere(false, radius), 5, pos -> {
|
||||
if (!source.getWorld().isAir(new BlockPos(pos).down())) {
|
||||
source.addParticle(ParticleTypes.FLAME, pos, Vec3d.ZERO);
|
||||
source.spawnParticles(new Sphere(true, radius * 2), rainy ? 98 : 125, pos -> {
|
||||
BlockPos bpos = new BlockPos(pos);
|
||||
|
||||
if (!source.getWorld().isAir(bpos.down())) {
|
||||
source.addParticle(source.getWorld().hasRain(bpos) ? ParticleTypes.SMOKE : ParticleTypes.FLAME, pos, Vec3d.ZERO);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
@ -63,19 +73,27 @@ public class NecromancySpell extends AbstractAreaEffectSpell {
|
|||
return true;
|
||||
}
|
||||
|
||||
summonedEntities.removeIf(ref -> !ref.isPresent(source.getWorld()));
|
||||
summonedEntities.removeIf(ref -> ref.getOrEmpty(source.getWorld()).filter(e -> {
|
||||
if (e.getPos().distanceTo(source.getOriginVector()) > radius * 2) {
|
||||
e.world.sendEntityStatus(e, (byte)60);
|
||||
e.discard();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).isEmpty());
|
||||
|
||||
float additional = source.getWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty() + getTraits().get(Trait.CHAOS, 0, 10);
|
||||
|
||||
Shape affectRegion = new Sphere(false, radius);
|
||||
if (--spawnCountdown > 0) {
|
||||
return true;
|
||||
}
|
||||
spawnCountdown = source.getWorld().random.nextInt(rainy ? 2000 : 100);
|
||||
|
||||
if (source.getWorld().random.nextInt(100) != 0) {
|
||||
if (summonedEntities.size() > 10 + additional) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source.findAllEntitiesInRange(radius, e -> e instanceof ZombieEntity).count() >= 10 * (1 + additional)) {
|
||||
return false;
|
||||
}
|
||||
Shape affectRegion = new Sphere(false, radius);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Vec3d pos = affectRegion.computePoint(source.getWorld().random).add(source.getOriginVector());
|
||||
|
@ -107,26 +125,31 @@ public class NecromancySpell extends AbstractAreaEffectSpell {
|
|||
}
|
||||
|
||||
protected void spawnMonster(Caster<?> source, Vec3d pos, EntityType<? extends LivingEntity> type) {
|
||||
LivingEntity zombie = type.create(source.getWorld());
|
||||
LivingEntity minion = type.create(source.getWorld());
|
||||
|
||||
source.subtractEnergyCost(3);
|
||||
|
||||
zombie.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
|
||||
zombie.setVelocity(0, 0.3, 0);
|
||||
minion.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
|
||||
minion.setVelocity(0, 0.3, 0);
|
||||
|
||||
source.getWorld().syncWorldEvent(WorldEvents.ZOMBIE_BREAKS_WOODEN_DOOR, zombie.getBlockPos(), 0);
|
||||
source.getWorld().syncWorldEvent(WorldEvents.DRAGON_BREATH_CLOUD_SPAWNS, minion.getBlockPos(), 0);
|
||||
source.playSound(SoundEvents.BLOCK_BELL_USE, 1, 0.3F);
|
||||
source.spawnParticles(ParticleTypes.LARGE_SMOKE, 10);
|
||||
minion.equipStack(EquipmentSlot.HEAD, Items.IRON_HELMET.getDefaultStack());
|
||||
|
||||
source.getWorld().spawnEntity(zombie);
|
||||
Equine.of(minion).filter(eq -> eq instanceof Creature).ifPresent(eq -> {
|
||||
((Creature)eq).setMaster(source.getMaster());
|
||||
});
|
||||
|
||||
EntityReference<LivingEntity> ref = new EntityReference<>();
|
||||
ref.set(zombie);
|
||||
summonedEntities.add(ref);
|
||||
source.getWorld().spawnEntity(minion);
|
||||
summonedEntities.add(new EntityReference<>(minion));
|
||||
setDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toNBT(NbtCompound compound) {
|
||||
super.toNBT(compound);
|
||||
spawnCountdown = compound.getInt("spawnCountdown");
|
||||
if (summonedEntities.size() > 0) {
|
||||
NbtList list = new NbtList();
|
||||
summonedEntities.forEach(ref -> list.add(ref.toNBT()));
|
||||
|
@ -137,12 +160,11 @@ public class NecromancySpell extends AbstractAreaEffectSpell {
|
|||
@Override
|
||||
public void fromNBT(NbtCompound compound) {
|
||||
super.fromNBT(compound);
|
||||
compound.putInt("spawnCountdown", spawnCountdown);
|
||||
summonedEntities.clear();
|
||||
if (compound.contains("summonedEntities")) {
|
||||
compound.getList("summonedEntities", 10).forEach(tag -> {
|
||||
EntityReference<LivingEntity> ref = new EntityReference<>();
|
||||
ref.fromNBT((NbtCompound)tag);
|
||||
summonedEntities.add(ref);
|
||||
compound.getList("summonedEntities", NbtElement.COMPOUND_TYPE).forEach(tag -> {
|
||||
summonedEntities.add(new EntityReference<>((NbtCompound)tag));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.function.Predicate;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.ability.magic.Affine;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
|
@ -55,7 +56,7 @@ public class TargetSelecter {
|
|||
return targets.values().stream().filter(Target::canHurt).count();
|
||||
}
|
||||
|
||||
public static Predicate<Entity> notOwnerOrFriend(Spell spell, Caster<?> source) {
|
||||
public static <T extends Entity> Predicate<T> notOwnerOrFriend(Affine spell, Caster<?> source) {
|
||||
Entity owner = source.getMaster();
|
||||
|
||||
boolean ownerIsValid = spell.isFriendlyTogether(source) && (EquinePredicates.PLAYER_UNICORN.test(owner));
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.entity.Living;
|
|||
import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
|
||||
import com.minelittlepony.unicopia.entity.behaviour.FallingBlockBehaviour;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
|
@ -62,7 +63,12 @@ public class WorldRenderDelegate {
|
|||
|
||||
matrices.push();
|
||||
|
||||
Entity owner = pony.getMaster();
|
||||
Entity owner = pony.getEntity();
|
||||
Entity master = pony.getMaster();
|
||||
|
||||
if (master != owner) {
|
||||
RenderSystem.setShaderColor(0, 0, 1, 1);
|
||||
}
|
||||
|
||||
boolean negative = pony.getPhysics().isGravityNegative();
|
||||
|
||||
|
@ -77,7 +83,6 @@ public class WorldRenderDelegate {
|
|||
if (pony instanceof Pony) {
|
||||
roll = ((Pony)pony).getCamera().calculateRoll();
|
||||
|
||||
|
||||
matrices.multiply(Vec3f.NEGATIVE_Y.getDegreesQuaternion(yaw));
|
||||
matrices.multiply(Vec3f.POSITIVE_Z.getDegreesQuaternion(roll));
|
||||
matrices.multiply(Vec3f.POSITIVE_Y.getDegreesQuaternion(yaw));
|
||||
|
@ -91,7 +96,7 @@ public class WorldRenderDelegate {
|
|||
|
||||
if (pony instanceof Caster<?>) {
|
||||
|
||||
int fireTicks = pony.getMaster().doesRenderOnFire() ? 1 : 0;
|
||||
int fireTicks = owner.doesRenderOnFire() ? 1 : 0;
|
||||
|
||||
return ((Caster<?>)pony).getSpellSlot().get(SpellPredicate.IS_DISGUISE, true).map(effect -> {
|
||||
effect.update(pony, false);
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
package com.minelittlepony.unicopia.entity;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.TargetSelecter;
|
||||
import com.minelittlepony.unicopia.entity.ai.BreakHeartGoal;
|
||||
import com.minelittlepony.unicopia.entity.ai.DynamicTargetGoal;
|
||||
import com.minelittlepony.unicopia.entity.ai.WantItTakeItGoal;
|
||||
import com.minelittlepony.unicopia.entity.player.PlayerAttributes;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.SpawnGroup;
|
||||
import net.minecraft.entity.ai.goal.ActiveTargetGoal;
|
||||
import net.minecraft.entity.ai.goal.GoalSelector;
|
||||
import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
||||
import net.minecraft.entity.attribute.EntityAttributes;
|
||||
|
@ -19,11 +27,14 @@ import net.minecraft.entity.damage.DamageSource;
|
|||
import net.minecraft.entity.data.DataTracker;
|
||||
import net.minecraft.entity.data.TrackedData;
|
||||
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
||||
import net.minecraft.entity.mob.HostileEntity;
|
||||
import net.minecraft.entity.mob.MobEntity;
|
||||
import net.minecraft.entity.mob.SlimeEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
|
||||
public class Creature extends Living<LivingEntity> {
|
||||
|
||||
private static final TrackedData<NbtCompound> EFFECT = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.TAG_COMPOUND);
|
||||
public static final TrackedData<Float> GRAVITY = DataTracker.registerData(LivingEntity.class, TrackedDataHandlerRegistry.FLOAT);
|
||||
|
||||
|
@ -33,18 +44,72 @@ public class Creature extends Living<LivingEntity> {
|
|||
|
||||
private final EntityPhysics<LivingEntity> physics;
|
||||
|
||||
private final EntityReference<LivingEntity> master = new EntityReference<>();
|
||||
|
||||
@Nullable
|
||||
private GoalSelector goals;
|
||||
@Nullable
|
||||
private GoalSelector targets;
|
||||
|
||||
public Creature(LivingEntity entity) {
|
||||
super(entity, EFFECT);
|
||||
physics = new EntityPhysics<>(entity, GRAVITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaster(LivingEntity owner) {
|
||||
master.set(owner);
|
||||
if (targets != null && owner != null) {
|
||||
initMinionAi();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LivingEntity getMaster() {
|
||||
return master.getOrEmpty(getWorld()).orElse(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public Optional<GoalSelector> getTargets() {
|
||||
return Optional.ofNullable(targets);
|
||||
}
|
||||
|
||||
public Optional<GoalSelector> getGoals() {
|
||||
return Optional.ofNullable(goals);
|
||||
}
|
||||
|
||||
public void initAi(GoalSelector goals, GoalSelector targets) {
|
||||
this.goals = goals;
|
||||
this.targets = targets;
|
||||
|
||||
DynamicTargetGoal targetter = new DynamicTargetGoal((MobEntity)entity);
|
||||
targets.add(1, targetter);
|
||||
goals.add(1, new WantItTakeItGoal((MobEntity)entity, targetter));
|
||||
if (entity.getType().getSpawnGroup() == SpawnGroup.MONSTER) {
|
||||
goals.add(3, new BreakHeartGoal((MobEntity)entity, targetter));
|
||||
}
|
||||
|
||||
if (master.isPresent(getWorld())) {
|
||||
initMinionAi();
|
||||
}
|
||||
}
|
||||
|
||||
private void initMinionAi() {
|
||||
Predicate<LivingEntity> filter = TargetSelecter.<LivingEntity>notOwnerOrFriend(this, this).and(e -> {
|
||||
return Equine.of(e)
|
||||
.filter(eq -> eq instanceof Creature)
|
||||
.filter(eq -> ((Creature)eq).getMaster() == getMaster())
|
||||
.isEmpty();
|
||||
});
|
||||
|
||||
targets.clear();
|
||||
targets.add(2, new ActiveTargetGoal<>((MobEntity)entity, PlayerEntity.class, true, filter));
|
||||
targets.add(2, new ActiveTargetGoal<>((MobEntity)entity, HostileEntity.class, true, filter));
|
||||
targets.add(2, new ActiveTargetGoal<>((MobEntity)entity, SlimeEntity.class, true, filter));
|
||||
}
|
||||
|
||||
public static void registerAttributes(DefaultAttributeContainer.Builder builder) {
|
||||
|
@ -53,7 +118,6 @@ public class Creature extends Living<LivingEntity> {
|
|||
builder.add(PlayerAttributes.ENTITY_GRAVTY_MODIFIER);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
@ -99,6 +163,7 @@ public class Creature extends Living<LivingEntity> {
|
|||
getSpellSlot().get(true).ifPresent(effect -> {
|
||||
compound.put("effect", Spell.writeNbt(effect));
|
||||
});
|
||||
compound.put("master", master.toNBT());
|
||||
physics.toNBT(compound);
|
||||
}
|
||||
|
||||
|
@ -108,6 +173,12 @@ public class Creature extends Living<LivingEntity> {
|
|||
if (compound.contains("effect")) {
|
||||
getSpellSlot().put(Spell.readNbt(compound.getCompound("effect")));
|
||||
}
|
||||
if (compound.contains("master", NbtElement.COMPOUND_TYPE)) {
|
||||
master.fromNBT(compound.getCompound("master"));
|
||||
if (master.isPresent(getWorld()) && targets != null) {
|
||||
initMinionAi();
|
||||
}
|
||||
}
|
||||
physics.fromNBT(compound);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,16 @@ public class EntityReference<T extends Entity> implements NbtSerialisable {
|
|||
|
||||
private Optional<Vec3d> pos = Optional.empty();
|
||||
|
||||
public EntityReference() {}
|
||||
|
||||
public EntityReference(T entity) {
|
||||
set(entity);
|
||||
}
|
||||
|
||||
public EntityReference(NbtCompound nbt) {
|
||||
fromNBT(nbt);
|
||||
}
|
||||
|
||||
public void set(@Nullable T entity) {
|
||||
if (entity != null) {
|
||||
uuid = entity.getUuid();
|
||||
|
|
Loading…
Reference in a new issue