Added the dragon breath scroll

This commit is contained in:
Sollace 2022-09-18 01:23:29 +02:00
parent 8036844941
commit 33dba4db7e
10 changed files with 262 additions and 6 deletions

View file

@ -0,0 +1,128 @@
package com.minelittlepony.unicopia.block.data;
import java.util.*;
import com.minelittlepony.unicopia.Unicopia;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.*;
import net.minecraft.util.Identifier;
import net.minecraft.world.PersistentState;
import net.minecraft.world.World;
public class DragonBreathStore extends PersistentState {
private static final long PURGE_INTERVAL = 1000 * 60 * 60; // 1 hour
private static final long MAX_MESSAGE_HOLD_TIME = PURGE_INTERVAL * 24; // 24 hours
private static final Identifier ID = Unicopia.id("dragon_breath");
public static DragonBreathStore get(World world) {
return WorldOverlay.getPersistableStorage(world, ID, DragonBreathStore::new, DragonBreathStore::new);
}
private final Map<String, List<Entry>> payloads = new HashMap<>();
private final Object locker = new Object();
DragonBreathStore(World world, NbtCompound compound) {
this(world);
compound.getKeys().forEach(key -> {
compound.getList(key, NbtElement.COMPOUND_TYPE).forEach(entry -> {
put(key, new Entry((NbtCompound)entry));
});
});
}
DragonBreathStore(World world) {
}
@Override
public NbtCompound writeNbt(NbtCompound compound) {
synchronized (locker) {
payloads.forEach((id, uuids) -> {
NbtList list = new NbtList();
uuids.forEach(entry -> {
if (entry.created < System.currentTimeMillis() - MAX_MESSAGE_HOLD_TIME) {
list.add(entry.toNBT(new NbtCompound()));
}
});
compound.put(id, list);
});
return compound;
}
}
public List<Entry> popEntries(String recipient) {
synchronized (locker) {
List<Entry> entries = doPurge().get(recipient);
if (entries == null) {
return List.of();
}
long now = System.currentTimeMillis();
List<Entry> collected = new ArrayList<>();
entries.removeIf(entry -> {
if (entry.created < now - 1000) {
collected.add(entry);
return true;
}
return false;
});
return collected;
}
}
public List<Entry> peekEntries(String recipient) {
synchronized (locker) {
return doPurge().getOrDefault(recipient, List.of());
}
}
public void put(String recipient, ItemStack payload) {
synchronized (locker) {
doPurge();
if (peekEntries(recipient).stream().noneMatch(i -> {
if (ItemStack.canCombine(i.payload(), payload)) {
int combinedCount = i.payload().getCount() + payload.getCount();
if (combinedCount <= i.payload().getMaxCount()) {
i.payload().setCount(combinedCount);
return true;
}
}
return false;
})) {
put(recipient, new Entry(System.currentTimeMillis() + (long)(Math.random() * 1999), payload));
}
}
}
private void put(String recipient, Entry entry) {
payloads.computeIfAbsent(recipient, id -> new ArrayList<>()).add(entry);
}
private Map<String, List<Entry>> doPurge() {
long now = System.currentTimeMillis();
if (now % PURGE_INTERVAL == 0) {
payloads.entrySet().removeIf(entry -> {
entry.getValue().removeIf(e -> e.created < now - MAX_MESSAGE_HOLD_TIME);
return entry.getValue().isEmpty();
});
}
return payloads;
}
public record Entry(
long created,
ItemStack payload) {
public Entry(NbtCompound compound) {
this(compound.getLong("created"), ItemStack.fromNbt(compound.getCompound("payload")));
}
public NbtCompound toNBT(NbtCompound compound) {
compound.putLong("created", created);
compound.put("payload", payload().writeNbt(new NbtCompound()));
return compound;
}
}
}

View file

@ -12,19 +12,26 @@ import com.minelittlepony.unicopia.ability.magic.SpellContainer;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation; import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.block.data.DragonBreathStore;
import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.datasync.EffectSync; import com.minelittlepony.unicopia.network.datasync.EffectSync;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.projectile.ProjectileImpactListener; import com.minelittlepony.unicopia.projectile.ProjectileImpactListener;
import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.entity.Entity; import net.minecraft.entity.*;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedData;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ProjectileEntity; import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public abstract class Living<T extends LivingEntity> implements Equine<T>, Caster<T> { public abstract class Living<T extends LivingEntity> implements Equine<T>, Caster<T> {
@ -108,6 +115,35 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
prevSneaking = entity.isSneaking(); prevSneaking = entity.isSneaking();
prevLanded = entity.isOnGround(); prevLanded = entity.isOnGround();
if (!entity.world.isClient && (entity instanceof PlayerEntity || entity.hasCustomName())) {
Vec3d targetPos = entity.getRotationVector().multiply(2).add(entity.getEyePos());
if (entity.getWorld().isAir(new BlockPos(targetPos))) {
DragonBreathStore store = DragonBreathStore.get(entity.world);
String name = entity.getDisplayName().getString();
store.popEntries(name).forEach(stack -> {
Vec3d randomPos = targetPos.add(VecHelper.supply(() -> entity.getRandom().nextTriangular(0.1, 0.5)));
if (!entity.getWorld().isAir(new BlockPos(randomPos))) {
store.put(name, stack.payload());
}
for (int i = 0; i < 10; i++) {
ParticleUtils.spawnParticle(entity.world, ParticleTypes.FLAME, randomPos.add(
VecHelper.supply(() -> entity.getRandom().nextTriangular(0.1, 0.5))
), Vec3d.ZERO);
}
ItemEntity item = EntityType.ITEM.create(entity.world);
item.setStack(stack.payload());
item.setPosition(randomPos);
item.world.spawnEntity(item);
entity.world.playSoundFromEntity(null, entity, SoundEvents.ITEM_FIRECHARGE_USE, entity.getSoundCategory(), 1, 1);
});
}
}
} }
@Override @Override

View file

@ -0,0 +1,42 @@
package com.minelittlepony.unicopia.item;
import java.util.UUID;
import com.minelittlepony.unicopia.block.data.DragonBreathStore;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;
public class DragonBreathScrollItem extends Item {
public DragonBreathScrollItem(Settings settings) {
super(settings);
}
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(hand);
ItemStack payload = player.getStackInHand(hand == Hand.MAIN_HAND ? Hand.OFF_HAND : Hand.MAIN_HAND);
if (payload.isEmpty() || !stack.hasCustomName()) {
return TypedActionResult.fail(stack);
}
stack.split(1);
if (!world.isClient) {
DragonBreathStore.get(world).put(stack.getName().getString(), payload.split(1));
}
player.playSound(SoundEvents.ITEM_FIRECHARGE_USE, 1, 1);
return TypedActionResult.consume(stack);
}
public static ItemStack setRecipient(ItemStack stack, UUID recipient) {
stack.getOrCreateSubNbt("recipient").putUuid("id", recipient);
return stack;
}
}

View file

@ -81,6 +81,8 @@ public interface UItems {
Item GOLDEN_FEATHER = register("golden_feather", new Item(new Item.Settings().rarity(Rarity.UNCOMMON).group(ItemGroup.MATERIALS))); Item GOLDEN_FEATHER = register("golden_feather", new Item(new Item.Settings().rarity(Rarity.UNCOMMON).group(ItemGroup.MATERIALS)));
Item GOLDEN_WING = register("golden_wing", new Item(new Item.Settings().rarity(Rarity.UNCOMMON).group(ItemGroup.MATERIALS))); Item GOLDEN_WING = register("golden_wing", new Item(new Item.Settings().rarity(Rarity.UNCOMMON).group(ItemGroup.MATERIALS)));
Item DRAGON_BREATH_SCROLL = register("dragon_breath_scroll", new DragonBreathScrollItem(new Item.Settings().rarity(Rarity.UNCOMMON).group(ItemGroup.TOOLS)));
Item BUTTERFLY_SPAWN_EGG = register("butterfly_spawn_egg", new SpawnEggItem(UEntities.BUTTERFLY, 0x222200, 0xaaeeff, new Item.Settings().group(ItemGroup.MISC))); Item BUTTERFLY_SPAWN_EGG = register("butterfly_spawn_egg", new SpawnEggItem(UEntities.BUTTERFLY, 0x222200, 0xaaeeff, new Item.Settings().group(ItemGroup.MISC)));
Item BUTTERFLY = register("butterfly", new Item(new Item.Settings().group(ItemGroup.FOOD).food(UFoodComponents.INSECTS))); Item BUTTERFLY = register("butterfly", new Item(new Item.Settings().group(ItemGroup.FOOD).food(UFoodComponents.INSECTS)));

View file

@ -36,6 +36,7 @@
"item.unicopia.crystal_heart": "Crystal Heart", "item.unicopia.crystal_heart": "Crystal Heart",
"item.unicopia.crystal_shard": "Crystal Shard", "item.unicopia.crystal_shard": "Crystal Shard",
"item.unicopia.dragon_breath_scroll": "Dragon's Breath Scroll",
"item.unicopia.gemstone": "Gemstone", "item.unicopia.gemstone": "Gemstone",
"item.unicopia.gemstone.enchanted": "%s Gem", "item.unicopia.gemstone.enchanted": "%s Gem",
"item.unicopia.gemstone.obfuscated": "Mysterious Gem", "item.unicopia.gemstone.obfuscated": "Mysterious Gem",

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "unicopia:item/dragon_breath_scroll"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,15 @@
{
"type": "unicopia:spellbook/crafting",
"material": {
"item": "minecraft:paper"
},
"traits": {
"fire": 1
},
"ingredients": [
{ "item": "minecraft:paper" }
],
"result": {
"item": "unicopia:dragon_breath_scroll"
}
}

View file

@ -23,13 +23,20 @@
"title": "", "title": "",
"level": 1, "level": 1,
"elements": [ "elements": [
"The Commander has also very graciously allowed me access to her library to continue my studies. I'm excited to see what combining unicorn and pegasus magics might bring." "The Commander has also very graciously allowed me access to her library to continue my studies. I'm excited to see what combining unicorn and pegasus magics might bring.",
"At the princess' behest",
"- Starswirl the Bearded"
] ]
}, },
{ {
"title": "", "title": "2nd Hoof '12",
"level": 0, "level": 0,
"elements": [] "elements": [
"Apologies for the, um, unusual entry in the appendices for today. It appears some little gremlin managed to obscond with my journal.",
"At the princess' behest, so dreadfully sorry",
"- Starswirl the Bearded"
]
}, },
{ {
"title": "Air Magic I", "title": "Air Magic I",

View file

@ -32,12 +32,31 @@
] ]
}, },
{ {
"title": "2nd Mare '12", "title": "5th Mare '12",
"level": 0, "level": 0,
"elements": [ "elements": [
"Other accounts say that this artefact only functions when mounted on a specific pedestal of diamond blocks, like a beacon." "Other accounts say that this artefact only functions when mounted on a specific pedestal of diamond blocks, like a beacon."
] ]
}, },
{
"title": "Dragon's Breath Scroll",
"level": 0,
"elements": [
{ "item": { "item": "unicopia:dragon_breath_scroll" } },
"Status: Confirmed",
"It's, um a scroll that you write somepony's name on it and you hold it in one hoof and something in the other hoof and, like, um it goes whooosh and the item is sent to that pony",
"- XOXOX Lulu"
]
},
{
"title": "2nd Hoof '12",
"level": 0,
"elements": [
"P.S. Uncle Starswirly is a dunderhead",
{ "recipe": "unicopia:dragon_breath_scroll" }
]
},
{ {
"title": "Bangle of Comradery", "title": "Bangle of Comradery",
"level": 0, "level": 0,