mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-12-17 23:48:00 +01:00
Added the dragon breath scroll
This commit is contained in:
parent
8036844941
commit
33dba4db7e
10 changed files with 262 additions and 6 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)));
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 |
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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",
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue