Allow all entities to get the effects of wearing an alicorn amulet and not just the player

This commit is contained in:
Sollace 2022-12-09 20:45:49 +00:00
parent 26b297dd50
commit bc01a8a824
6 changed files with 156 additions and 147 deletions

View file

@ -0,0 +1,80 @@
package com.minelittlepony.unicopia.entity;
import java.util.*;
import java.util.stream.Stream;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class ItemTracker implements NbtSerialisable {
public static final long TICKS = 1;
public static final long SECONDS = 20 * TICKS;
public static final long HOURS = 1000 * TICKS;
public static final long DAYS = 24 * HOURS;
private final Map<Trackable, Long> items = new HashMap<>();
public void update(Living<?> living, Stream<ItemStack> stacks) {
final Set<Trackable> found = new HashSet<>();
final Set<ItemStack> foundStacks = new HashSet<>();
stacks.forEach(stack -> {
if (stack.getItem() instanceof Trackable trackable) {
items.compute(trackable, (item, prev) -> prev == null ? 1 : prev + 1);
found.add(trackable);
foundStacks.add(stack);
}
});
items.entrySet().removeIf(e -> {
if (!found.contains(e.getKey())) {
e.getKey().onUnequipped(living, e.getValue());
return true;
}
return false;
});
if (!(living.getEntity() instanceof PlayerEntity)) {
foundStacks.forEach(stack -> {
if (getTicks((Trackable)stack.getItem()) == 1) {
stack.inventoryTick(living.getReferenceWorld(), living.getEntity(), 0, false);
}
});
}
}
public long getTicks(Trackable charm) {
return items.getOrDefault(charm.asItem(), 0L);
}
public boolean contains(Trackable charm) {
return getTicks(charm) > 0;
}
@Override
public void toNBT(NbtCompound compound) {
items.forEach((charm, count) -> {
compound.putLong(Registry.ITEM.getId(charm.asItem()).toString(), count);
});
}
@Override
public void fromNBT(NbtCompound compound) {
items.clear();
compound.getKeys().stream().map(Identifier::tryParse)
.filter(Objects::nonNull)
.map(id -> Map.entry(Registry.ITEM.get(id), compound.getLong(id.toString())))
.filter(i -> i.getKey() instanceof Trackable && i.getValue() > 0)
.forEach(item -> items.put((Trackable)item.getKey(), item.getValue()));
}
public interface Trackable extends ItemConvertible {
void onUnequipped(Living<?> living, long timeWorn);
void onEquipped(Living<?> living);
}
}

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.entity;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -59,6 +60,8 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
private final Enchantments enchants = new Enchantments(this); private final Enchantments enchants = new Enchantments(this);
private final ItemTracker armour = new ItemTracker();
protected Living(T entity, TrackedData<NbtCompound> effect) { protected Living(T entity, TrackedData<NbtCompound> effect) {
this.entity = entity; this.entity = entity;
this.effectDelegate = new EffectSync(this, effect); this.effectDelegate = new EffectSync(this, effect);
@ -91,6 +94,10 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
return enchants; return enchants;
} }
public ItemTracker getArmour() {
return armour;
}
@Override @Override
public void setMaster(T owner) { public void setMaster(T owner) {
} }
@ -103,6 +110,8 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
@Override @Override
public void tick() { public void tick() {
armour.update(this, getArmourStacks());
try { try {
getSpellSlot().forEach(spell -> Operation.ofBoolean(spell.tick(this, Situation.BODY)), entity.world.isClient); getSpellSlot().forEach(spell -> Operation.ofBoolean(spell.tick(this, Situation.BODY)), entity.world.isClient);
} catch (Exception e) { } catch (Exception e) {
@ -217,6 +226,16 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
return Stream.of(entity.getStackInHand(Hand.MAIN_HAND), entity.getStackInHand(Hand.OFF_HAND)); return Stream.of(entity.getStackInHand(Hand.MAIN_HAND), entity.getStackInHand(Hand.OFF_HAND));
} }
protected Stream<ItemStack> getArmourStacks() {
if (!TrinketsDelegate.hasTrinkets()) {
return StreamSupport.stream(entity.getArmorItems().spliterator(), false);
}
return Stream.concat(
TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.NECKLACE),
StreamSupport.stream(entity.getArmorItems().spliterator(), false)
);
}
protected void giveBackItem(ItemStack stack) { protected void giveBackItem(ItemStack stack) {
entity.dropStack(stack); entity.dropStack(stack);
} }
@ -240,18 +259,24 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
public void toNBT(NbtCompound compound) { public void toNBT(NbtCompound compound) {
enchants.toNBT(compound); enchants.toNBT(compound);
effectDelegate.toNBT(compound); effectDelegate.toNBT(compound);
compound.put("armour", armour.toNBT());
} }
@Override @Override
public void fromNBT(NbtCompound compound) { public void fromNBT(NbtCompound compound) {
enchants.fromNBT(compound); enchants.fromNBT(compound);
effectDelegate.fromNBT(compound); effectDelegate.fromNBT(compound);
armour.fromNBT(compound.getCompound("armour"));
} }
public void updateVelocity() { public void updateVelocity() {
updateVelocity(entity); updateVelocity(entity);
} }
public static Living<?> living(Entity entity) {
return PonyContainer.of(entity).map(a -> a instanceof Living ? (Living<?>)a.get() : null).orElse(null);
}
public static void updateVelocity(Entity entity) { public static void updateVelocity(Entity entity) {
entity.velocityModified = true; entity.velocityModified = true;
//if (entity instanceof ServerPlayerEntity ply) { //if (entity instanceof ServerPlayerEntity ply) {

View file

@ -1,38 +1,22 @@
package com.minelittlepony.unicopia.entity.player; package com.minelittlepony.unicopia.entity.player;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.item.GemstoneItem; import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.trinkets.TrinketsDelegate;
import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.Tickable;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList; import net.minecraft.nbt.NbtList;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypedActionResult; import net.minecraft.util.TypedActionResult;
import net.minecraft.util.registry.Registry;
public class PlayerCharmTracker implements Tickable, NbtSerialisable { public class PlayerCharmTracker implements NbtSerialisable {
private final Pony pony; private final Pony pony;
private final ItemTracker armour = new ItemTracker();
private CustomisedSpellType<?>[] handSpells = new CustomisedSpellType<?>[] { private CustomisedSpellType<?>[] handSpells = new CustomisedSpellType<?>[] {
SpellType.SHIELD.withTraits(), SpellType.SHIELD.withTraits(),
SpellType.CATAPULT.withTraits() SpellType.CATAPULT.withTraits()
@ -42,25 +26,6 @@ public class PlayerCharmTracker implements Tickable, NbtSerialisable {
this.pony = pony; this.pony = pony;
} }
@Override
public void tick() {
armour.update(getAll());
}
private Stream<ItemStack> getAll() {
if (!TrinketsDelegate.hasTrinkets()) {
return pony.getMaster().getInventory().armor.stream();
}
return Stream.concat(
TrinketsDelegate.getInstance().getEquipped(pony.getMaster(), TrinketsDelegate.NECKLACE),
pony.getMaster().getInventory().armor.stream()
);
}
public ItemTracker getArmour() {
return armour;
}
public CustomisedSpellType<?>[] getHandSpells() { public CustomisedSpellType<?>[] getHandSpells() {
return handSpells; return handSpells;
} }
@ -85,7 +50,6 @@ public class PlayerCharmTracker implements Tickable, NbtSerialisable {
@Override @Override
public void toNBT(NbtCompound compound) { public void toNBT(NbtCompound compound) {
compound.put("armour", armour.toNBT());
NbtList equippedSpells = new NbtList(); NbtList equippedSpells = new NbtList();
for (CustomisedSpellType<?> spell : handSpells) { for (CustomisedSpellType<?> spell : handSpells) {
equippedSpells.add(spell.toNBT()); equippedSpells.add(spell.toNBT());
@ -95,7 +59,6 @@ public class PlayerCharmTracker implements Tickable, NbtSerialisable {
@Override @Override
public void fromNBT(NbtCompound compound) { public void fromNBT(NbtCompound compound) {
armour.fromNBT(compound.getCompound("armour"));
if (compound.contains("handSpells", NbtElement.LIST_TYPE)) { if (compound.contains("handSpells", NbtElement.LIST_TYPE)) {
NbtList list = compound.getList("handSpells", NbtElement.COMPOUND_TYPE); NbtList list = compound.getList("handSpells", NbtElement.COMPOUND_TYPE);
for (int i = 0; i < handSpells.length && i < list.size(); i++) { for (int i = 0; i < handSpells.length && i < list.size(); i++) {
@ -103,54 +66,4 @@ public class PlayerCharmTracker implements Tickable, NbtSerialisable {
} }
} }
} }
public class ItemTracker implements NbtSerialisable {
private final Map<Charm, Integer> items = new HashMap<>();
public void update(Stream<ItemStack> stacks) {
Set<Charm> found = new HashSet<>();
stacks.forEach(stack -> {
if (stack.getItem() instanceof Charm) {
items.compute((Charm)stack.getItem(), (item, prev) -> prev == null ? 1 : prev + 1);
found.add((Charm)stack.getItem());
}
});
items.entrySet().removeIf(e -> {
if (!found.contains(e.getKey())) {
e.getKey().onRemoved(pony, e.getValue());
return true;
}
return false;
});
}
public int getTicks(Charm charm) {
return items.getOrDefault(charm.asItem(), 0);
}
public boolean contains(Charm charm) {
return getTicks(charm) > 0;
}
@Override
public void toNBT(NbtCompound compound) {
items.forEach((charm, count) -> {
compound.putInt(Registry.ITEM.getId(charm.asItem()).toString(), count);
});
}
@Override
public void fromNBT(NbtCompound compound) {
items.clear();
compound.getKeys().stream().map(Identifier::tryParse)
.filter(Objects::nonNull)
.map(id -> Map.entry(Registry.ITEM.get(id), compound.getInt(id.toString())))
.filter(i -> i.getKey() instanceof Charm && i.getValue() > 0)
.forEach(item -> items.put((Charm)item.getKey(), item.getValue()));
}
}
public interface Charm extends Affine, ItemConvertible {
void onRemoved(Pony pony, int timeWorn);
}
} }

View file

@ -117,7 +117,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
this.mana = new ManaContainer(this); this.mana = new ManaContainer(this);
this.levels = new PlayerLevelStore(this, LEVEL, true, SoundEvents.ENTITY_PLAYER_LEVELUP); this.levels = new PlayerLevelStore(this, LEVEL, true, SoundEvents.ENTITY_PLAYER_LEVELUP);
this.corruption = new PlayerLevelStore(this, CORRUPTION, false, SoundEvents.PARTICLE_SOUL_ESCAPE); this.corruption = new PlayerLevelStore(this, CORRUPTION, false, SoundEvents.PARTICLE_SOUL_ESCAPE);
this.tickers = Lists.newArrayList(gravity, mana, attributes, charms); this.tickers = Lists.newArrayList(gravity, mana, attributes);
player.getDataTracker().startTracking(RACE, Race.DEFAULT_ID); player.getDataTracker().startTracking(RACE, Race.DEFAULT_ID);
} }
@ -516,7 +516,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
@Override @Override
public boolean isEnemy(Affine other) { public boolean isEnemy(Affine other) {
return getCharms().getArmour().contains(UItems.ALICORN_AMULET) || super.isEnemy(other); return getArmour().contains(UItems.ALICORN_AMULET) || super.isEnemy(other);
} }
@Override @Override

View file

@ -5,7 +5,6 @@ import java.util.*;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.*;
import com.minelittlepony.unicopia.entity.player.*; import com.minelittlepony.unicopia.entity.player.*;
@ -35,7 +34,7 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.world.LocalDifficulty; import net.minecraft.world.LocalDifficulty;
import net.minecraft.world.World; import net.minecraft.world.World;
public class AlicornAmuletItem extends AmuletItem implements PlayerCharmTracker.Charm, ItemImpl.ClingyItem, ItemImpl.GroundTickCallback { public class AlicornAmuletItem extends AmuletItem implements ItemTracker.Trackable, ItemImpl.ClingyItem, ItemImpl.GroundTickCallback {
private static final float EFFECT_UPDATE_FREQUENCY = 1000000; private static final float EFFECT_UPDATE_FREQUENCY = 1000000;
private static final UUID EFFECT_UUID = UUID.fromString("c0a870f5-99ef-4716-a23e-f320ee834b26"); private static final UUID EFFECT_UUID = UUID.fromString("c0a870f5-99ef-4716-a23e-f320ee834b26");
private static final Map<EntityAttribute, Float> EFFECT_SCALES = Map.of( private static final Map<EntityAttribute, Float> EFFECT_SCALES = Map.of(
@ -50,20 +49,15 @@ public class AlicornAmuletItem extends AmuletItem implements PlayerCharmTracker.
super(settings, 0, ImmutableMultimap.of()); super(settings, 0, ImmutableMultimap.of());
} }
@Override
public Affinity getAffinity() {
return Affinity.BAD;
}
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
@Override @Override
public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> tooltip, TooltipContext tooltipContext) { public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> tooltip, TooltipContext tooltipContext) {
Pony iplayer = Pony.of(MinecraftClient.getInstance().player); Pony iplayer = Pony.of(MinecraftClient.getInstance().player);
if (iplayer != null) { if (iplayer != null) {
int attachedTime = iplayer.getCharms().getArmour().getTicks(this); long attachedTime = iplayer.getArmour().getTicks(this);
if (attachedTime > 0) { if (attachedTime > 0) {
tooltip.add(Text.translatable(getTranslationKey() + ".lore", StringHelper.formatTicks(attachedTime))); tooltip.add(Text.translatable(getTranslationKey() + ".lore", StringHelper.formatTicks((int)attachedTime)));
} }
} }
} }
@ -112,49 +106,58 @@ public class AlicornAmuletItem extends AmuletItem implements PlayerCharmTracker.
} }
@Override @Override
public void onRemoved(Pony pony, int timeWorn) { public void onEquipped(Living<?> wearer) {
wearer.getReferenceWorld().playSound(null, wearer.getOrigin(), USounds.ITEM_ALICORN_AMULET_CURSE, wearer.getEntity().getSoundCategory(), 3, 1);
}
@Override
public void onUnequipped(Living<?> wearer, long timeWorn) {
float attachedTime = timeWorn / 100F; float attachedTime = timeWorn / 100F;
LocalDifficulty difficulty = pony.getReferenceWorld().getLocalDifficulty(pony.getOrigin()); LocalDifficulty difficulty = wearer.getReferenceWorld().getLocalDifficulty(wearer.getOrigin());
float amount = attachedTime * (1 + difficulty.getClampedLocalDifficulty()); float amount = attachedTime * (1 + difficulty.getClampedLocalDifficulty());
amount = Math.min(amount, pony.getMaster().getMaxHealth()); amount = Math.min(amount, wearer.getMaster().getMaxHealth());
pony.getMaster().getHungerManager().setFoodLevel(1); if (wearer.getMaster() instanceof PlayerEntity) {
pony.getMaster().damage(MagicalDamageSource.ALICORN_AMULET, amount); ((PlayerEntity)wearer.getMaster()).getHungerManager().setFoodLevel(1);
pony.getMaster().addStatusEffect(new StatusEffectInstance(StatusEffects.NAUSEA, 200, 1)); }
wearer.getMaster().damage(MagicalDamageSource.ALICORN_AMULET, amount);
wearer.getMaster().addStatusEffect(new StatusEffectInstance(StatusEffects.NAUSEA, 200, 1));
if (attachedTime > 120) { if (attachedTime > 120) {
pony.getMaster().takeKnockback(1, 1, 1); wearer.getMaster().takeKnockback(1, 1, 1);
pony.updateVelocity(); wearer.updateVelocity();
} }
EFFECT_SCALES.keySet().forEach(attribute -> { EFFECT_SCALES.keySet().forEach(attribute -> {
pony.getMaster().getAttributeInstance(attribute).tryRemoveModifier(EFFECT_UUID); wearer.getMaster().getAttributeInstance(attribute).tryRemoveModifier(EFFECT_UUID);
}); });
} }
@Override @Override
public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) { public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
if (!(entity instanceof PlayerEntity) || world.isClient) { if (world.isClient) {
return; return;
} }
PlayerEntity player = (PlayerEntity)entity;
// if we're in the main hand, try to equip ourselves // if we're in the main hand, try to equip ourselves
if (selected && !isApplicable(player) && world.random.nextInt(320) == 0) { if (entity instanceof PlayerEntity player && selected && !isApplicable(player) && world.random.nextInt(320) == 0) {
use(world, player, Hand.MAIN_HAND); use(world, player, Hand.MAIN_HAND);
return; return;
} }
Pony pony = Pony.of(player); Living<?> living = Living.living(entity);
if (!pony.getCharms().getArmour().contains(this)) { if (!living.getArmour().contains(this)) {
return; return;
} }
float attachedTicks = living.getArmour().getTicks(this);
float seconds = attachedTicks / EFFECT_UPDATE_FREQUENCY;
if (entity instanceof PlayerEntity player) {
// healing effect // healing effect
if (player.getHealth() < player.getMaxHealth()) { if (player.getHealth() < player.getMaxHealth()) {
player.heal(0.5F); player.heal(0.5F);
@ -164,28 +167,22 @@ public class AlicornAmuletItem extends AmuletItem implements PlayerCharmTracker.
player.removeStatusEffect(StatusEffects.NAUSEA); player.removeStatusEffect(StatusEffects.NAUSEA);
} }
MagicReserves reserves = pony.getMagicalReserves(); MagicReserves reserves = ((Pony)living).getMagicalReserves();
// constantly increase exertion // constantly increase exertion
if (reserves.getExertion().get() < reserves.getExertion().getMax()) { if (reserves.getExertion().get() < reserves.getExertion().getMax()) {
reserves.getExertion().add(2); reserves.getExertion().add(2);
} }
float attachedTicks = pony.getCharms().getArmour().getTicks(this);
float seconds = attachedTicks / EFFECT_UPDATE_FREQUENCY;
if (reserves.getEnergy().get() < 0.005F + seconds) { if (reserves.getEnergy().get() < 0.005F + seconds) {
reserves.getEnergy().add(2); reserves.getEnergy().add(2);
} }
if (attachedTicks == 1) {
world.playSound(null, player.getBlockPos(), USounds.ITEM_ALICORN_AMULET_CURSE, SoundCategory.PLAYERS, 3, 1);
} }
// every 1 second, update modifiers // every 1 second, update modifiers
if (attachedTicks % EFFECT_UPDATE_FREQUENCY == 0) { if (attachedTicks % EFFECT_UPDATE_FREQUENCY == 0) {
EFFECT_SCALES.entrySet().forEach(attribute -> { EFFECT_SCALES.entrySet().forEach(attribute -> {
EntityAttributeInstance instance = player.getAttributeInstance(attribute.getKey()); EntityAttributeInstance instance = living.getMaster().getAttributeInstance(attribute.getKey());
EntityAttributeModifier modifier = instance.getModifier(EFFECT_UUID); EntityAttributeModifier modifier = instance.getModifier(EFFECT_UUID);
float desiredValue = attribute.getValue() * seconds; float desiredValue = attribute.getValue() * seconds;
if (!MathHelper.approximatelyEquals(desiredValue, modifier.getValue())) { if (!MathHelper.approximatelyEquals(desiredValue, modifier.getValue())) {

View file

@ -1,8 +1,7 @@
package com.minelittlepony.unicopia.item; package com.minelittlepony.unicopia.item;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.entity.ItemTracker;
import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.player.PlayerCharmTracker;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings; import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
@ -11,23 +10,18 @@ import net.minecraft.item.ItemStack;
import net.minecraft.particle.ParticleTypes; import net.minecraft.particle.ParticleTypes;
import net.minecraft.world.World; import net.minecraft.world.World;
public class PegasusAmuletItem extends AmuletItem implements PlayerCharmTracker.Charm { public class PegasusAmuletItem extends AmuletItem implements ItemTracker.Trackable {
public PegasusAmuletItem(FabricItemSettings settings, int maxEnergy) { public PegasusAmuletItem(FabricItemSettings settings, int maxEnergy) {
super(settings, maxEnergy); super(settings, maxEnergy);
} }
@Override @Override
public Affinity getAffinity() { public void onUnequipped(Living<?> living, long timeWorn) {
return Affinity.NEUTRAL;
}
@Override
public void onRemoved(Living<?> living, int timeWorn) {
} }
@Override @Override
public void onAdded(Living<?> living) { public void onEquipped(Living<?> living) {
} }