Reimplemented the alicorn amulet

This commit is contained in:
Sollace 2021-08-13 15:57:21 +02:00
parent 21812b613b
commit a1c3fa93dd
8 changed files with 426 additions and 9 deletions

View file

@ -0,0 +1,98 @@
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.minelittlepony.unicopia.ability.magic.Affine;
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.util.Identifier;
import net.minecraft.util.registry.Registry;
public class PlayerCharmTracker implements Tickable, NbtSerialisable {
private final Pony pony;
private final ItemTracker armour = new ItemTracker();
PlayerCharmTracker(Pony pony) {
this.pony = pony;
}
@Override
public void tick() {
armour.update(pony.getMaster().getInventory().armor.stream());
}
public ItemTracker getArmour() {
return armour;
}
@Override
public void toNBT(NbtCompound compound) {
compound.put("armour", armour.toNBT());
}
@Override
public void fromNBT(NbtCompound compound) {
armour.fromNBT(compound.getCompound("armour"));
}
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

@ -14,6 +14,7 @@ import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.WorldTribeManager;
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
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.advancement.UCriteria;
@ -22,6 +23,7 @@ import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.entity.Living;
import com.minelittlepony.unicopia.entity.Trap;
import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.item.toxin.FoodType;
import com.minelittlepony.unicopia.item.toxin.Toxicity;
import com.minelittlepony.unicopia.item.toxin.Toxin;
@ -71,6 +73,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
private final AbilityDispatcher powers = new AbilityDispatcher(this);
private final PlayerPhysics gravity = new PlayerPhysics(this);
private final PlayerCharmTracker charms = new PlayerCharmTracker(this);
private final PlayerAttributes attributes = new PlayerAttributes(this);
private final PlayerCamera camera = new PlayerCamera(this);
private final ManaContainer mana;
@ -100,7 +103,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
super(player, EFFECT);
this.mana = new ManaContainer(this);
this.levels = new PlayerLevelStore(this);
this.tickers = Lists.newArrayList(gravity, mana, attributes);
this.tickers = Lists.newArrayList(gravity, mana, attributes, charms);
player.getDataTracker().startTracking(RACE, Race.HUMAN.ordinal());
}
@ -135,6 +138,10 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
return mana;
}
public PlayerCharmTracker getCharms() {
return charms;
}
@Override
public LevelStore getLevel() {
return levels;
@ -395,12 +402,22 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
}
public Optional<Text> trySleep(BlockPos pos) {
if (UItems.ALICORN_AMULET.isApplicable(entity)) {
return Optional.of(new TranslatableText("block.unicopia.bed.not_tired"));
}
return findAllSpellsInRange(10)
.filter(p -> p instanceof Pony && ((Pony)p).isEnemy(this))
.findFirst()
.map(p -> new TranslatableText("block.unicopia.bed.not_safe"));
}
@Override
public boolean isEnemy(Affine other) {
return getCharms().getArmour().contains(UItems.ALICORN_AMULET) || super.isEnemy(other);
}
public void onEat(ItemStack stack) {
if (getSpecies() == Race.CHANGELING) {
Toxin.POISON.afflict(getMaster(), FoodType.RAW_MEAT, Toxicity.SAFE, stack);
@ -416,6 +433,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
compound.put("powers", powers.toNBT());
compound.put("gravity", gravity.toNBT());
compound.put("charms", charms.toNBT());
getSpellSlot().get(true).ifPresent(effect ->{
compound.put("effect", SpellType.toNBT(effect));
@ -430,6 +448,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
powers.fromNBT(compound.getCompound("powers"));
gravity.fromNBT(compound.getCompound("gravity"));
charms.fromNBT(compound.getCompound("charms"));
magicExhaustion = compound.getFloat("magicExhaustion");

View file

@ -0,0 +1,259 @@
package com.minelittlepony.unicopia.item;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.AwaitTickQueue;
import com.minelittlepony.unicopia.entity.IItemEntity;
import com.minelittlepony.unicopia.entity.ItemImpl;
import com.minelittlepony.unicopia.entity.ItemImpl.TickableItem;
import com.minelittlepony.unicopia.entity.player.MagicReserves;
import com.minelittlepony.unicopia.entity.player.PlayerCharmTracker;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.Entity;
import net.minecraft.entity.Entity.RemovalReason;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.ActionResult;
import net.minecraft.util.ChatUtil;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.LocalDifficulty;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion.DestructionType;
public class AlicornAmuletItem extends AmuletItem implements PlayerCharmTracker.Charm, ItemImpl.ClingyItem, TickableItem {
public AlicornAmuletItem(FabricItemSettings settings) {
super(settings, 0, new AmuletItem.ModifiersBuilder()
.add(EntityAttributes.GENERIC_ARMOR, 9000)
.add(EntityAttributes.GENERIC_ARMOR_TOUGHNESS, 9000)
.add(EntityAttributes.GENERIC_KNOCKBACK_RESISTANCE, 9000)
.add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 100)
.add(EntityAttributes.GENERIC_ATTACK_KNOCKBACK, 20)
.build());
}
@Override
public Affinity getAffinity() {
return Affinity.BAD;
}
@Environment(EnvType.CLIENT)
@Override
public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> tooltip, TooltipContext tooltipContext) {
Pony iplayer = Pony.of(MinecraftClient.getInstance().player);
if (iplayer != null) {
int attachedTime = iplayer.getCharms().getArmour().getTicks(this);
if (attachedTime > 0) {
tooltip.add(new TranslatableText(getTranslationKey() + ".lore", ChatUtil.ticksToString(attachedTime)));
}
}
}
@Override
public ParticleEffect getParticleEffect(IItemEntity entity) {
return ((ItemEntity)entity).world.random.nextBoolean() ? ParticleTypes.LARGE_SMOKE : ParticleTypes.FLAME;
}
@Override
public boolean isClingy(ItemStack stack) {
return true;
}
@Override
public float getFollowDistance(IItemEntity entity) {
return Math.max(20, ItemImpl.ClingyItem.super.getFollowDistance(entity));
}
@Override
public float getFollowSpeed(IItemEntity entity) {
return Math.max(0.12F, ItemImpl.ClingyItem.super.getFollowSpeed(entity));
}
@Override
public void interactWithPlayer(IItemEntity item, PlayerEntity player) {
ItemEntity entity = (ItemEntity)item;
if (!player.world.isClient && !entity.isRemoved()) {
if (player.getPos().distanceTo(entity.getPos()) < 0.5) {
if (entity.world.random.nextInt(150) == 0) {
entity.setPickupDelay(0);
entity.onPlayerCollision(player);
if (player.getMainHandStack().getItem() == this) {
TypedActionResult<ItemStack> result = use(player.world, player, Hand.MAIN_HAND);
if (result.getResult() == ActionResult.SUCCESS) {
entity.setPickupDelay(1000);
entity.setRemoved(RemovalReason.DISCARDED);
}
}
}
}
}
}
@Override
public void onRemoved(Pony pony, int timeWorn) {
float attachedTime = timeWorn / 100F;
LocalDifficulty difficulty = pony.getWorld().getLocalDifficulty(pony.getOrigin());
float amount = attachedTime * (1 + difficulty.getClampedLocalDifficulty());
amount = Math.min(amount, pony.getMaster().getMaxHealth());
pony.getMaster().getHungerManager().setFoodLevel(1);
pony.getMaster().damage(MagicalDamageSource.ALICORN_AMULET, amount);
pony.getMaster().addStatusEffect(new StatusEffectInstance(StatusEffects.NAUSEA, 200, 1));
if (attachedTime > 120) {
pony.getMaster().takeKnockback(1, 1, 1);
}
}
@Override
public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
if (!(entity instanceof PlayerEntity)) {
return;
}
if (world.isClient) {
return;
}
PlayerEntity player = (PlayerEntity)entity;
if (selected && !isApplicable(player) && world.random.nextInt(320) == 0) {
use(world, player, Hand.MAIN_HAND);
return;
}
Pony pony = Pony.of(player);
if (!pony.getCharms().getArmour().contains(this)) {
return;
}
float attachedTime = pony.getCharms().getArmour().getTicks(this);
MagicReserves reserves = pony.getMagicalReserves();
if (player.getHealth() < player.getMaxHealth()) {
player.heal(0.5F);
} else if (player.canConsume(false)) {
player.getHungerManager().add(1, 0);
} else {
player.removeStatusEffect(StatusEffects.NAUSEA);
}
if (reserves.getExertion().get() < reserves.getExertion().getMax()) {
reserves.getExertion().add(2);
}
if (reserves.getEnergy().get() < 0.005F + (attachedTime / 1000000)) {
reserves.getEnergy().add(2);
}
if (attachedTime == 1) {
world.playSound(null, player.getBlockPos(), SoundEvents.ENTITY_ELDER_GUARDIAN_CURSE, SoundCategory.PLAYERS, 3, 1);
}
// attempt to play 3 tricks every tick
Trick.ALL.stream().filter(trick -> trick.play(attachedTime, player)).limit(3).toList();
if (stack.getDamage() >= getMaxDamage() - 1) {
stack.damage(10, player, p -> p.sendEquipmentBreakStatus(EquipmentSlot.CHEST));
player.damage(MagicalDamageSource.ALICORN_AMULET, player.getMaxHealth() - 0.01F);
player.getHungerManager().setFoodLevel(1);
Vec3d pos = player.getPos();
player.world.createExplosion(player, pos.x, pos.y, pos.z, 10, DestructionType.NONE);
AwaitTickQueue.scheduleTask(player.world, w -> {
w.createExplosion(player, pos.x, pos.y, pos.z, 6, DestructionType.BREAK);
}, 50);
}
}
@Override
public ActionResult onGroundTick(IItemEntity item) {
ItemEntity entity = (ItemEntity)item;
if (entity.world.random.nextInt(500) == 0) {
entity.world.playSound(null, entity.getBlockPos(), SoundEvents.AMBIENT_CAVE, SoundCategory.HOSTILE, 0.5F, 1);
}
return ActionResult.PASS;
}
public static class Trick {
private static final List<Trick> ALL = new ArrayList<>();
public static final Trick SPOOK = new Trick(0, 1050, player -> player.world.playSound(null, player.getBlockPos(), SoundEvents.AMBIENT_NETHER_WASTES_MOOD, SoundCategory.PLAYERS, 3, 1));
public static final Trick WITHER = new Trick(20000, 100, player -> {
StatusEffectInstance effect = new StatusEffectInstance(player.world.random.nextInt(32000) == 0 ? StatusEffects.WITHER : StatusEffects.HUNGER, 300, 3);
effect.setPermanent(true);
player.addStatusEffect(effect);
});
public static final Trick POKE = new Trick(13000, 300, player -> player.damage(MagicalDamageSource.ALICORN_AMULET, 1F));
public static final Trick SPIN = new Trick(6000, 300, player -> player.setYaw(player.getYaw() + 180));
public static final Trick BUTTER_FINGERS = new Trick(1000, 300, player -> player.dropSelectedItem(false));
public static final Trick MOVE = new Trick(3000, 300, player -> {
float amount = player.world.random.nextFloat() - 0.5F;
boolean sideways = player.world.random.nextBoolean();
player.addVelocity(sideways ? 0 : amount, 0, sideways ? amount : 0);
});
public static final Trick SWING = new Trick(2000, 100, player -> player.swingHand(Hand.MAIN_HAND));
public static final Trick BAD_JOO_JOO = new Trick(1000, 10, player -> {
if (!player.hasStatusEffect(StatusEffects.BAD_OMEN)) {
player.addStatusEffect(new StatusEffectInstance(StatusEffects.BAD_OMEN, 300, 3));
}
});
private final int minTime;
private final int chance;
private final Consumer<PlayerEntity> action;
public Trick(int minTime, int chance, Consumer<PlayerEntity> action) {
this.minTime = minTime;
this.chance = chance;
this.action = action;
ALL.add(this);
}
public boolean play(float ticks, PlayerEntity player) {
if (ticks > minTime && (chance <= 0 || player.world.random.nextInt(chance) == 0)) {
action.accept(player);
return true;
}
return false;
}
}
}

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.item;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
@ -35,13 +36,13 @@ public class AmuletItem extends WearableItem {
private final ImmutableMultimap<EntityAttribute, EntityAttributeModifier> modifiers;
public AmuletItem(FabricItemSettings settings, int maxEnergy) {
this(settings, maxEnergy, ImmutableMultimap.builder());
this(settings, maxEnergy, ImmutableMultimap.of());
}
public AmuletItem(FabricItemSettings settings, int maxEnergy, ImmutableMultimap.Builder<EntityAttribute, EntityAttributeModifier> modifiers) {
public AmuletItem(FabricItemSettings settings, int maxEnergy, ImmutableMultimap<EntityAttribute, EntityAttributeModifier> modifiers) {
super(settings);
this.maxEnergy = maxEnergy;
this.modifiers = modifiers.build();
this.modifiers = modifiers;
}
@Override
@ -89,7 +90,7 @@ public class AmuletItem extends WearableItem {
}
public boolean isApplicable(ItemStack stack) {
return stack.getItem() == this && getEnergy(stack) > 0;
return stack.getItem() == this && (!isChargable() || getEnergy(stack) > 0);
}
public boolean isApplicable(LivingEntity entity) {
@ -123,4 +124,19 @@ public class AmuletItem extends WearableItem {
stack.getOrCreateTag().putFloat("energy", energy);
}
}
public static class ModifiersBuilder {
private static final UUID SLOT_UUID = UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E");
private final ImmutableMultimap.Builder<EntityAttribute, EntityAttributeModifier> modifiers = new ImmutableMultimap.Builder<>();
public ModifiersBuilder add(EntityAttribute attribute, double amount) {
modifiers.put(attribute, new EntityAttributeModifier(SLOT_UUID, "Armor modifier", amount, EntityAttributeModifier.Operation.ADDITION));
return this;
}
public ImmutableMultimap<EntityAttribute, EntityAttributeModifier> build() {
return modifiers.build();
}
}
}

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.item;
import java.util.ArrayList;
import java.util.List;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.item.toxin.UFoodComponents;
@ -72,13 +73,15 @@ public interface UItems {
Item GOLDEN_WING = register("golden_wing", new Item(new Item.Settings().rarity(Rarity.UNCOMMON).group(ItemGroup.MATERIALS)));
AmuletItem PEGASUS_AMULET = register("pegasus_amulet", new AmuletItem(new FabricItemSettings()
.maxCount(1)
.maxDamage(890)
.rarity(Rarity.UNCOMMON)
.group(ItemGroup.DECORATIONS), 900));
/*AmuletItem ALICORN_AMULET = register("alicorn_amulet", new AmuletItem(new AmuletItem.Settings("alicorn_amulet")
.toughness(900000000)
.resistance(90000000)
.group(ItemGroup.DECORATIONS), 0));*/
AlicornAmuletItem ALICORN_AMULET = register("alicorn_amulet", new AlicornAmuletItem(new FabricItemSettings()
.maxCount(1)
.maxDamage(1000)
.rarity(Rarity.RARE)
.group(ItemGroup.DECORATIONS)));
static <T extends Item> T register(String name, T item) {
ITEMS.add(item);

View file

@ -1,6 +1,7 @@
{
"block.unicopia.bed.not_safe": "You may not rest here, there are enemies nearby",
"block.unicopia.bed.not_tired": "You do not feel tired right now",
"ability.unicopia.empty_hooves": "I need to find a jar",
"ability.unicopia.indoors": "I can't see the sky from here",
@ -53,6 +54,9 @@
"item.unicopia.pegasus_amulet.lore": "Grants temporary flight to whoever wears it",
"item.unicopia.amulet.energy": "Energy: %d / %d",
"item.unicopia.alicorn_amulet": "Alicorn Amulet",
"item.unicopia.alicorn_amulet.lore": "Time worn: %d",
"item.unicopia.music_disc_pet": "Music Disc",
"item.unicopia.music_disc_pet.desc": "Danial Ingram - pet",
"item.unicopia.music_disc_popular": "Music Disc",

View file

@ -0,0 +1,18 @@
{
"parent": "item/generated",
"textures": {
"layer0": "unicopia:item/alicorn_amulet"
},
"display": {
"thirdperson": {
"rotation": [ -90, 0, 0 ],
"translation": [ 0, 1, -3 ],
"scale": [ 0.55, 0.55, 0.55 ]
},
"firstperson": {
"rotation": [ 0, -135, 25 ],
"translation": [ 0, 4, 2 ],
"scale": [ 1.7, 1.7, 1.7 ]
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B