From c1c37881ebe7b7c93c41ef8fb70bbc5a538f2fbd Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 15 Dec 2023 20:45:29 +0000 Subject: [PATCH 01/98] Fix missing mixin target --- .../minelittlepony/unicopia/mixin/MixinPufferfishEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPufferfishEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPufferfishEntity.java index ecadb831..1e799247 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinPufferfishEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinPufferfishEntity.java @@ -16,7 +16,7 @@ import net.minecraft.entity.passive.PufferfishEntity; abstract class MixinPufferfishEntity extends FishEntity { MixinPufferfishEntity() { super(null, null); } - @Inject(method = "method_6591(Lnet/minecraft/class_1309;)Z", at = @At("HEAD"), cancellable = true) + @Inject(method = "method_6591(Lnet/minecraft/entity/LivingEntity;)Z", at = @At("HEAD"), cancellable = true) private static void onShouldTarget(LivingEntity entity, CallbackInfoReturnable info) { Pony.of(entity).filter(pony -> pony.getCompositeRace().includes(Race.SEAPONY)).ifPresent(pony -> { info.setReturnValue(false); From bad793aabfcf0e3acb279d8e9ccd428f1f1c36d0 Mon Sep 17 00:00:00 2001 From: LingVarr <104311317+LingVarr@users.noreply.github.com> Date: Thu, 21 Dec 2023 00:29:24 +1100 Subject: [PATCH 02/98] Update ru_ru.json (#224) --- .../resources/assets/unicopia/lang/ru_ru.json | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/ru_ru.json b/src/main/resources/assets/unicopia/lang/ru_ru.json index d7ce9dc2..b151cf02 100644 --- a/src/main/resources/assets/unicopia/lang/ru_ru.json +++ b/src/main/resources/assets/unicopia/lang/ru_ru.json @@ -298,9 +298,15 @@ "block.unicopia.dense_cloud_slab": "Плита из плотного облака", "block.unicopia.dense_cloud_stairs": "Ступеньки из плотного облака", "block.unicopia.compacted_dense_cloud": "Плотное облако", + "block.unicopia.etched_cloud": "Выгравированное облако", + "block.unicopia.etched_cloud_slab": "Плита выгравированного облака", + "block.unicopia.etched_cloud_stairs": "Ступеньки из выгравированного облака", + "block.unicopia.compacted_etched_cloud": "Выгравированное облако", "block.unicopia.cloud_pillar": "Колонна из облака", "block.unicopia.cloth_bed": "Кровать из шикарной ткани", "block.unicopia.cloud_bed": "Кровать из облака", + "block.unicopia.cloud_chest": "Клаудсдейльский сундук", + "block.unicopia.cloud_chest.double": "Большой клаудсдейльский сундук", "block.unicopia.oats": "Овёс", "block.unicopia.oats_stem": "Овёс", @@ -374,6 +380,7 @@ "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_hippogriff": "Туманное зелье метаморфоз гиппогрифа", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_hippogriff": "Стрела метаморфоз гиппогрифа", + "potion.withChance": "Шанс 1 к %s получить %s", "potion.potency.6": "VII", "spell.unicopia.frost": "Заморозка", @@ -468,11 +475,57 @@ "trait.unicopia.poison.name": "Яда", "trait.unicopia.poison.description": "Смертоносный дротик убивает зверя", - "toxicity.safe.name": "Безопасный", - "toxicity.mild.name": "Слаботоксичный", - "toxicity.fair.name": "Достаточно токсичный", - "toxicity.severe.name": "Токсичный", - "toxicity.lethal.name": "Смертельный", + "unicopia.diet.information": "Информация о диете:", + "unicopia.diet.side_effects": "Побочные эффекты:", + "unicopia.diet.not_edible": "Предмет не съедобен", + "unicopia.diet.base_multiplier": "Базовый множитель: %s%%", + "unicopia.diet.hunger.detailed": "Голода восстановлено: %s из %s (%s%%)", + "unicopia.diet.saturation.detailed": "Насыщения получено: %s (%s%%)", + "unicopia.diet.hunger": "Коэффициент голода: %s%%", + "unicopia.diet.saturation": "Коэффициент насыщения: %s%%", + + "tag.unicopia.food_types.rotten_meat": "Гниющее мясо", + "tag.unicopia.food_types.raw_meat": "Свежее мясо", + "tag.unicopia.food_types.cooked_meat": "Готовое мясо", + "tag.unicopia.food_types.raw_fish": "Свежая рыба", + "tag.unicopia.food_types.cooked_fish": "Готовая рыба", + "tag.unicopia.food_types.raw_insect": "Жуки и насекомые", + "tag.unicopia.food_types.cooked_insect": "Приготовленные жуки и насекомые", + "tag.unicopia.food_types.love": "Любовь", + "tag.unicopia.food_types.rocks": "Камни", + "tag.unicopia.food_types.pinecone": "Орехи и семена", + "tag.unicopia.food_types.bat_ponys_delight": "Лакомства бэтпони", + "tag.unicopia.food_types.cooked_sea_vegitables": "Готовая рыбная еда", + "tag.unicopia.food_types.raw_sea_vegitables": "Свежая рыбная еда", + "tag.unicopia.food_types.shells": "Морские ракушки", + "tag.unicopia.food_types.shelly": "Морские ракушки", + "tag.unicopia.food_types.candy": "Конфеты", + "tag.unicopia.food_types.desserts": "Десерты", + "tag.unicopia.food_types.fruits_and_vegetables": "Фрукты и овощи", + "tag.unicopia.food_types.drinks": "Напитки", + + "tag.unicopia.food_types.forage_edible_filling": "Крупная растительная масса", + "tag.unicopia.food_types.forage_edible": "Растительная масса", + "tag.unicopia.food_types.forage_nauseating": "Тошнотворное", + "tag.unicopia.food_types.forage_prickly": "Колючее", + "tag.unicopia.food_types.forage_risky": "Небезопасное", + "tag.unicopia.food_types.forage_strengthening": "Повышающее силу", + "tag.unicopia.food_types.forage_severely_prickly": "Очень колючее", + "tag.unicopia.food_types.forage_severely_nauseating": "Отвратительное", + "tag.unicopia.food_types.forage_radioactive": "Светящееся", + "tag.unicopia.food_types.forage_dangerous": "Опасное", + "tag.unicopia.food_types.forage_blinding": "Токсичное", + + "toxicity.safe.name": "Безопасное", + "toxicity.mild.name": "Слаботоксичное", + "toxicity.fair.name": "Достаточно токсичное", + "toxicity.severe.name": "Токсичное", + "toxicity.lethal.name": "Смертельное", + + "affliction.unicopia.empty": "Нет эффекта", + "affliction.unicopia.healing": "Восполняет %s%% здоровья", + "affliction.unicopia.cure_love_sickness": "Лечит от любовной болезни", + "affliction.unicopia.lose_hunger": "Теряет %s%% сытости", "ability.unicopia.shoot": "Стрелять магией", "ability.unicopia.shoot.with_spell": "Стрелять \"%s\"", @@ -1127,6 +1180,8 @@ "advancements.unicopia.tempted.description": "Наденьте амулет аликорна", "advancements.unicopia.hello_darkness_my_old_friend.title": "Здравствуй, Тьма...", "advancements.unicopia.hello_darkness_my_old_friend.description": "Углубитесь в силы тёмной стороны", + "advancements.unicopia.a_falling_wizard.title": "Падший волшебник", + "advancements.unicopia.a_falling_wizard.description": "Единорог безуспешно попытался наложить заклинание полёта", "advancements.unicopia.split_the_sea.title": "Ты кто? Моисей?", "advancements.unicopia.split_the_sea.description": "Используйте магию, чтобы переместить более 100 блоков воды за один раз", From cc5e39aebbfaa5f104bb600d13a38381b8b881ec Mon Sep 17 00:00:00 2001 From: Cryghast Date: Wed, 29 Nov 2023 22:41:12 +0800 Subject: [PATCH 03/98] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B1=89=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/assets/unicopia/lang/zh_cn.json | 112 ++++++++++++++++-- 1 file changed, 100 insertions(+), 12 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index 0a69a685..76299c4e 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -1,6 +1,6 @@ { - "block.unicopia.bed.not_safe": "你现在不能休息,附近有怪物在游荡", + "block.unicopia.bed.not_safe": "你现在不能休息,附近有敌人在游荡", "block.unicopia.bed.not_tired": "你现在并不觉得累", "block.unicopia.bed.no_sleep.nocturnal": "你只能在白天或雷暴中入眠", "sleep.not_possible.nocturnal": "休息再久也不能跳过这一天了", @@ -21,6 +21,7 @@ "itemGroup.unicopia.unicorn": "奇幻小马国 - 坎特洛特", "itemGroup.unicopia.pegasus": "奇幻小马国 - 云中城", "itemGroup.unicopia.bat_pony": "奇幻小马国 - 林阴镇", + "itemGroup.unicopia.sea_pony": "奇幻小马国 - 来自深海", "itemGroup.unicopia.changeling": "奇幻小马国 - 幻形虫巢", "item.unicopia.friendship_bracelet": "联谊手环", @@ -44,6 +45,7 @@ "item.unicopia.spellbook": "魔法书", "emi.category.unicopia.spellbook": "魔法书", + "emi.category.unicopia.cloud_shaping": "塑形", "item.unicopia.alicorn_badge": "天角兽徽章", "item.unicopia.unicorn_badge": "独角兽徽章", @@ -52,10 +54,13 @@ "item.unicopia.changeling_badge": "幻形灵徽章", "item.unicopia.bat_badge": "夜骐徽章", "item.unicopia.kirin_badge": "麒麟徽章", + "item.unicopia.hippogriff_badge": "骏鹰徽章", "item.unicopia.butterfly_spawn_egg": "蝴蝶刷怪蛋", "item.unicopia.butterfly": "蝴蝶", - + "item.unicopia.loot_bug_spawn_egg": "Loot Bug Spawn Egg", + "item.unicopia.loot_bug": "Loot bug", + "item.unicopia.green_apple": "史密斯婆婆苹果", "item.unicopia.sweet_apple": "甜苹果园苹果", "item.unicopia.sour_apple": "酸苹果", @@ -140,6 +145,13 @@ "item.unicopia.alicorn_amulet": "天角兽护符", "item.unicopia.alicorn_amulet.lore": "老化度: %d", + "item.unicopia.pearl_necklace": "珍珠项链", + "item.unicopia.pearl_necklace.lore": "给予佩带者水下活动的能力", + "item.unicopia.clam_shell": "蛤蜊壳", + "item.unicopia.scallop_shell": "扇贝壳", + "item.unicopia.turret_shell": "锥螺壳", + "item.unicopia.shelly": "壳儿", + "item.unicopia.horse_shoe.accuracy": "精准度%d%%", "item.unicopia.horse_shoe.speed": "速度:%d", "item.unicopia.iron_horse_shoe": "铁蹄铁", @@ -175,7 +187,35 @@ "item.unicopia.music_disc_funk": "音乐唱片", "item.unicopia.music_disc_funk.desc": "funk, just funk", + "item.unicopia.cloud_lump": "云团", + "item.unicopia.light_gray_bed_sheets": "淡灰色床单", + "item.unicopia.gray_bed_sheets": "灰色床单", + "item.unicopia.black_bed_sheets": "黑色床单", + "item.unicopia.brown_bed_sheets": "棕色床单", + "item.unicopia.red_bed_sheets": "红色床单", + "item.unicopia.orange_bed_sheets": "橙色床单", + "item.unicopia.yellow_bed_sheets": "黄色床单", + "item.unicopia.lime_bed_sheets": "黄绿色床单", + "item.unicopia.green_bed_sheets": "绿色床单", + "item.unicopia.cyan_bed_sheets": "青色床单", + "item.unicopia.light_blue_bed_sheets": "淡蓝色床单", + "item.unicopia.blue_bed_sheets": "蓝色床单", + "item.unicopia.purple_bed_sheets": "紫色床单", + "item.unicopia.magenta_bed_sheets": "品红色床单", + "item.unicopia.pink_bed_sheets": "粉色床单", + "item.unicopia.apple_bed_sheets": "苹果纹饰床单", + "item.unicopia.barred_bed_sheets": "条纹床单", + "item.unicopia.checkered_bed_sheets": "棋盘格床单", + "item.unicopia.kelp_bed_sheets": "海藻纹饰床单", + "item.unicopia.rainbow_bed_sheets": "彩虹纹饰床单", + "item.unicopia.rainbow_bpw_bed_sheets": "BPW 彩虹纹饰床单", + "item.unicopia.rainbow_bpy_bed_sheets": "BPY 彩虹纹饰床单", + "item.unicopia.rainbow_pbg_bed_sheets": "PGB 彩虹纹饰床单", + "item.unicopia.rainbow_pwr_bed_sheets": "PWR 彩虹纹饰床单", + "block.unicopia.rocks": "一些石块", + "block.unicopia.plunder_vine": "掠夺之藤", + "block.unicopia.plunder_vine_bud": "掠夺之藤幼芽", "block.unicopia.bananas": "香蕉", "block.unicopia.zapling": "魔虹苹果树苗", "block.unicopia.zap_log": "魔虹苹果木原木", @@ -209,6 +249,10 @@ "block.unicopia.mango_leaves": "芒果树叶", "block.unicopia.mango_sapling": "芒果树苗", "block.unicopia.pineapple": "菠萝树", + + "block.unicopia.clam_shell": "蛤蜊壳", + "block.unicopia.scallop_shell": "扇贝壳", + "block.unicopia.turret_shell": "锥螺壳", "block.unicopia.green_apple_leaves": "史密斯婆婆苹果树树叶", "block.unicopia.green_apple_sapling": "史密斯婆婆苹果树树苗", @@ -231,6 +275,7 @@ "block.unicopia.chiselled_chitin_slab": "雕纹几丁质台阶", "block.unicopia.chiselled_chitin_stairs": "雕纹几丁质楼梯", + "block.unicopia.shaping_bench": "塑形长椅", "block.unicopia.cloud": "浮云", "block.unicopia.cloud_slab": "云台阶", "block.unicopia.cloud_stairs": "云楼梯", @@ -241,12 +286,19 @@ "block.unicopia.cloud_planks": "浮云板", "block.unicopia.cloud_planks_slab": "云板台阶", "block.unicopia.cloud_planks_stairs": "云板楼梯", + "block.unicopia.cloud_bricks": "云砖", + "block.unicopia.cloud_brick_slab": "云砖台阶", + "block.unicopia.cloud_brick_stairs": "云砖楼梯", + "block.unicopia.carved_cloud": "雕刻过的云", + "block.unicopia.compacted_cloud_bricks": "云砖", "block.unicopia.compacted_cloud_planks": "云板", "block.unicopia.unstable_cloud": "不稳定的云", "block.unicopia.dense_cloud": "稠云", "block.unicopia.dense_cloud_slab": "稠云台阶", "block.unicopia.dense_cloud_stairs": "稠云楼梯", + "block.unicopia.compacted_dense_cloud": "稠云", "block.unicopia.cloud_pillar": "云柱块", + "block.unicopia.cloth_bed": "高档精装床", "block.unicopia.cloud_bed": "云床", "block.unicopia.oats": "燕麦", @@ -315,6 +367,13 @@ "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_kirin": "滞留型转生麒麟药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_kirin": "转生麒麟之箭", + "effect.unicopia.change_race_hippogriff": "转生骏鹰", + "item.minecraft.potion.effect.unicopia.tribe_swap_hippogriff": "转生骏鹰药水", + "item.minecraft.splash_potion.effect.unicopia.tribe_swap_hippogriff": "喷溅型转生骏鹰药水", + "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_hippogriff": "滞留型转生骏鹰药水", + "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_hippogriff": "转生骏鹰之箭", + + "potion.withChance": "%s 分之 1 的概率为 %s", "potion.potency.6": "VII", "spell.unicopia.frost": "霜冻", @@ -450,7 +509,14 @@ "ability.unicopia.disguise": "变形", "ability.unicopia.rainboom": "彩虹音爆", "ability.unicopia.rage": "发火", - "ability.unicopia.nirik_blast": "逆麟", + "ability.unicopia.nirik_blast": "逆麟爆火", + "ability.unicopia.screech": "尖啸", + "ability.unicopia.peck": "啄/鸣叫", + "ability.unicopia.peck.block.fled": "方块被吓跑了", + "ability.unicopia.peck.block.unfased": "方块在你的威胁下仍泰然自若", + "ability.unicopia.dash": "飞行冲刺", + "ability.unicopia.change_form": "改变形态", + "ability.unicopia.sonar_pulse": "声呐脉冲", "gui.unicopia.trait.label": "%s 元素", "gui.unicopia.trait.group": "\n %s", @@ -482,6 +548,7 @@ "gui.unicopia.tribe_selection.describe.unicopia.pegasus": "加入天马族,与闪电天马一起翱翔", "gui.unicopia.tribe_selection.describe.unicopia.bat": "加入夜骐族,成为最黑暗的夜晚", "gui.unicopia.tribe_selection.describe.unicopia.kirin": "加入麒麟村,遵循沉默的誓言", + "gui.unicopia.tribe_selection.describe.unicopia.hippogriff": "加入骏鹰族,向你的邻居尖啸", "gui.unicopia.tribe_selection.describe.unicopia.changeling": "加入幻形灵蜂巢,你的女王需要你", "gui.unicopia.tribe_selection.confirm": "您已选择 %s", @@ -490,11 +557,13 @@ "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.human": " - 是杂食动物,可以吃除了爱以外的所有食物", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.human": " - 对生拇指", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.human": " - 直立行走", - "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.human": "", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.earth": " - 更强的击退力和魔抗力", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.earth": " - 额外的体重使它们对魔法和物理攻击抗性很高", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.earth": " - 与土地有特殊联系,使耕作效率提高10000%!", + "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.earth": " - 拥有强大的击,踢和踏力,可用于防御或向四周出击", + "gui.unicopia.tribe_selection.confirm.goods.5.unicopia.earth": " - 唯一能和苦力怕拥抱而不会被炸死的种族", + "gui.unicopia.tribe_selection.confirm.goods.6.unicopia.earth": " - 可以吃石头", "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.earth": " - 可爱软乎的小马耳朵", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.unicorn": " - 瞬移和魔法咒语", @@ -502,9 +571,10 @@ "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.unicorn": " - 可以使用魔法以检测或揭露附近的幻形灵", "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.unicorn": " - 头上有独角", - "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.pegasus": " - 能够飞行,能通过训练提高耐力", + "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.pegasus": " - 能够飞行,通过训练提高耐力", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.pegasus": " - 使用储存的魔力在短时间内冲刺或蓄力做出彩虹音爆", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.pegasus": " - 移动速度更快,摔落伤害更低", + "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.pegasus": " - 可以直接和云与云类物品互动", "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.pegasus": " - 可以吃蔬菜和某些鱼", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.bat": " - 能够飞行,能通过训练提高耐力", @@ -514,13 +584,20 @@ "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.changeling": " - 能够在原地飞行和悬停", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.changeling": " - 几乎能变形成任何生物或任何物体", + "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.changeling": " - 通过爬墙可越过任何任何物体", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.changeling": " - 是肉食性的。可以吃任何不会让它们生病的东西", - "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.changeling": "", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.kirin": " - 免疫一切火焰伤害", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.kirin": " - 受到伤害后可以发怒", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.kirin": " - 正常情况下很安静,可以在监守者周围自由行动", "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.kirin": " - 可以吃水果和蔬菜,红肉,以及一些对其他种族来说有毒的食物", + + "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.hippogriff": " - 能够飞行,通过训练提高耐力", + "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.hippogriff": " - 可在飞行时冲刺", + "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.hippogriff": " - 在尖啸和啄时会发出尖锐而吵闹的声响", + "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.hippogriff": " - 可以吃生的或熟的鱼,以及熟肉", + "gui.unicopia.tribe_selection.confirm.goods.5.unicopia.hippogriff": " - 吃松果能恢复更多生命值", + "gui.unicopia.tribe_selection.confirm.goods.6.unicopia.hippogriff": " - 喜爱楼梯", "gui.unicopia.tribe_selection.confirm.bads": "但它们……", @@ -532,6 +609,7 @@ "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.earth": " - 不能飞", "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.earth": " - 对某些类型的魔法抗性很弱", "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.earth": " - 只能吃植物和蔬菜", + "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.earth": " - 更重,比其他小马移动更慢", "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.unicorn": " - 不能飞", "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.unicorn": " - 对物理攻击抗性很弱", @@ -540,23 +618,26 @@ "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.pegasus": " - 由于体重轻,它们对物理攻击抗性最弱", "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.pegasus": " - 持续飞行时必须休息以恢复体力", "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.pegasus": " - 没有其它小马的帮助就无法使用魔法", - "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.pegasus": "", "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.bat": " - 由于体重轻,它们对物理攻击抗性很弱", "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.bat": " - 持续飞行时必须休息以恢复体力", "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.bat": " - 有时甚至自己吓到自己", - "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.bat": " - 是肉食性的。可以吃任何不会让它们生病的东西", + "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.bat": " - 是肉食性的。可以吃生的或熟的肉,或者纯靠偷来的爱维生", "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.changeling": " - 永远处于饥饿中 ", "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.changeling": " - 需要爱,从小马或其它敌对生物那里收集来充饥", "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.changeling": " - 吃普通食物会生病,必须汲取爱来加速治愈", - "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.changeling": "", "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.kirin": " - 发怒时就没那么沉默了", "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.kirin": " - 容易使东西燃起来,发怒时更是如此", "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.kirin": " - 比其他小马轻,更有可能被击退", "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.kirin": " - 不喜欢水", + "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.hippogriff": " - 对物理攻击抗性很弱", + "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.hippogriff": " - 不能与云互动", + "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.hippogriff": " - 持续飞行时必须休息以恢复体力", + "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.hippogriff": " - 脑子和鸟一样小", + "gui.unicopia.tribe_selection.join": "加入种族", "gui.unicopia.tribe_selection.cancel": "返回", @@ -780,8 +861,12 @@ "unicopia.race.changeling.alt": "幻形灵", "unicopia.race.bat": "夜骐", "unicopia.race.bat.alt": "夜骐", - "unicopia.race.kirin": "Kirin", - "unicopia.race.kirin.alt": "Kirins", + "unicopia.race.kirin": "麒麟", + "unicopia.race.kirin.alt": "麒麟", + "unicopia.race.hippogriff": "骏鹰", + "unicopia.race.hippogriff.alt": "骏鹰", + "unicopia.race.seapony": "海马", + "unicopia.race.seapony.alt": "海马", "death.attack.unicopia.generic.and_also": "%1$s 和 %2$s", "death.attack.unicopia.generic.whilst_flying": "%1$s而死于飞行途中", @@ -895,14 +980,17 @@ "unicopia.subtitle.insects": "昆虫乱舞", "unicopia.subtitle.changeling_buzz": "雄峰嗡嗡", "unicopia.subtitle.batpony_eeee": "夜骐尖叫", + "unicopia.subtitle.sonar": "声呐脉冲", "unicopia.subtitle.changeling.transform": "幻形灵 变形", + "unicopia.subtitle.screech": "骏鹰尖啸", "unicopia.subtitle.pegasus.molt": "天马脱羽", "unicopia.subtitle.unicorn.teleport": "魔法泡泡", "unicopia.subtitle.player.wololo": "Wololo!", "unicopia.subtitle.entity.player.whistle": "玩家吹口哨", "unicopia.subtitle.entity.player.kick": "玩家踢腿", "unicopia.subtitle.magic_aura": "魔法嗡嗡", - "unicopia.subtitle.player.rebound": "玩家从墙上跳下", + "unicopia.subtitle.player.rebound": "玩家从墙上弹开", + "unicopia.subtitle.screech": "玩家尖啸", "unicopia.subtitle.ears_ringing": "耳鸣", "unicopia.subtitle.heartbeat": "心悸", "unicopia.subtitle.entity.artefact.ambient": "魔法嗡", From ab0b412945fe32e1a419639d7303ba90b9cf359c Mon Sep 17 00:00:00 2001 From: Cryghast Date: Wed, 29 Nov 2023 23:36:00 +0800 Subject: [PATCH 04/98] loot bug --- src/main/resources/assets/unicopia/lang/zh_cn.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index 76299c4e..7cc4028d 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -58,8 +58,8 @@ "item.unicopia.butterfly_spawn_egg": "蝴蝶刷怪蛋", "item.unicopia.butterfly": "蝴蝶", - "item.unicopia.loot_bug_spawn_egg": "Loot Bug Spawn Egg", - "item.unicopia.loot_bug": "Loot bug", + "item.unicopia.loot_bug_spawn_egg": "劫掠虫刷怪蛋", + "item.unicopia.loot_bug": "劫掠虫", "item.unicopia.green_apple": "史密斯婆婆苹果", "item.unicopia.sweet_apple": "甜苹果园苹果", From 17b437a0608fc95ecfc5a54c9635849764abd96a Mon Sep 17 00:00:00 2001 From: Cryghast Date: Sat, 16 Dec 2023 20:57:21 +0800 Subject: [PATCH 05/98] =?UTF-8?q?update=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/assets/unicopia/lang/zh_cn.json | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index 7cc4028d..263b298f 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -297,9 +297,15 @@ "block.unicopia.dense_cloud_slab": "稠云台阶", "block.unicopia.dense_cloud_stairs": "稠云楼梯", "block.unicopia.compacted_dense_cloud": "稠云", + "block.unicopia.etched_cloud": "蚀刻过的云", + "block.unicopia.etched_cloud_slab": "蚀刻过的云台阶", + "block.unicopia.etched_cloud_stairs": "蚀刻过的云楼梯", + "block.unicopia.compacted_etched_cloud": "云蚀刻过的云", "block.unicopia.cloud_pillar": "云柱块", "block.unicopia.cloth_bed": "高档精装床", "block.unicopia.cloud_bed": "云床", + "block.unicopia.cloud_chest": "云中箱", + "block.unicopia.cloud_chest.double": "大云中箱", "block.unicopia.oats": "燕麦", "block.unicopia.oats_stem": "燕麦", @@ -468,12 +474,58 @@ "trait.unicopia.poison.name": "毒性", "trait.unicopia.poison.description": "一枚绝命毒镖能轻松放倒一头巨兽。", + "unicopia.diet.information": "饮食信息:", + "unicopia.diet.side_effects": "副作用:", + "unicopia.diet.not_edible": "物品不可食用", + "unicopia.diet.base_multiplier": "基础倍率: %s%%", + "unicopia.diet.hunger.detailed": "获取饥饿值: %s of %s (%s%%)", + "unicopia.diet.saturation.detailed": "获取饱和度: %s (%s%%)", + "unicopia.diet.hunger": "饥饿度比例: %s%%", + "unicopia.diet.saturation": "饱和度比例: %s%%", + + "tag.unicopia.food_types.rotten_meat": "腐烂的肉", + "tag.unicopia.food_types.raw_meat": "新鲜的肉", + "tag.unicopia.food_types.cooked_meat": "处理好的肉", + "tag.unicopia.food_types.raw_fish": "新鲜的鱼", + "tag.unicopia.food_types.cooked_fish": "处理好的鱼", + "tag.unicopia.food_types.raw_insect": "虫类", + "tag.unicopia.food_types.cooked_insect": "烤虫类", + "tag.unicopia.food_types.love": "爱", + "tag.unicopia.food_types.rocks": "石块", + "tag.unicopia.food_types.pinecone": "坚果和种子", + "tag.unicopia.food_types.bat_ponys_delight": "夜骐的处理", + "tag.unicopia.food_types.cooked_sea_vegitables": "处理好的鱼类食物", + "tag.unicopia.food_types.raw_sea_vegitables": "新鲜的鱼类食物", + "tag.unicopia.food_types.shells": "海贝", + "tag.unicopia.food_types.shelly": "海贝", + "tag.unicopia.food_types.candy": "糖果", + "tag.unicopia.food_types.desserts": "甜品", + "tag.unicopia.food_types.fruits_and_vegetables": "水果和蔬菜", + "tag.unicopia.food_types.drinks": "饮品", + + "tag.unicopia.food_types.forage_edible_filling": "大型植物料", + "tag.unicopia.food_types.forage_edible": "植物料", + "tag.unicopia.food_types.forage_nauseating": "恶心", + "tag.unicopia.food_types.forage_prickly": "多刺", + "tag.unicopia.food_types.forage_risky": "不安全", + "tag.unicopia.food_types.forage_strengthening": "力量增强", + "tag.unicopia.food_types.forage_severely_prickly": "刺特多", + "tag.unicopia.food_types.forage_severely_nauseating": "特恶心", + "tag.unicopia.food_types.forage_radioactive": "发光", + "tag.unicopia.food_types.forage_dangerous": "危险", + "tag.unicopia.food_types.forage_blinding": "剧毒", + "toxicity.safe.name": "无毒", "toxicity.mild.name": "微毒", "toxicity.fair.name": "中毒", "toxicity.severe.name": "剧毒", "toxicity.lethal.name": "致命", + "affliction.unicopia.empty": "无效果", + "affliction.unicopia.healing": "获得 %s%% 点生命值", + "affliction.unicopia.cure_love_sickness": "治愈爱的病", + "affliction.unicopia.lose_hunger": "失去 %s%% 点饥饿值", + "ability.unicopia.shoot": "发射魔法", "ability.unicopia.shoot.with_spell": "发射 %s 魔咒", "ability.unicopia.shoot.with_spell.active": "正在发射 %s 魔咒", @@ -1127,6 +1179,8 @@ "advancements.unicopia.tempted.description": "戴上天角兽护符", "advancements.unicopia.hello_darkness_my_old_friend.title": "你好,黑暗……", "advancements.unicopia.hello_darkness_my_old_friend.description": "追求黑暗面的力量", + "advancements.unicopia.a_falling_wizard.title": "堕落的巫师", + "advancements.unicopia.a_falling_wizard.description": "一只想要用魔法飞升但却失败的独角兽", "advancements.unicopia.split_the_sea.title": "您就是海王?", "advancements.unicopia.split_the_sea.description": "使用魔法一次性排开超过一百格水", From 24868633a5f9555af45d6d60dc094ae8ebe6e514 Mon Sep 17 00:00:00 2001 From: Cryghast Date: Wed, 27 Dec 2023 21:14:14 +0800 Subject: [PATCH 06/98] =?UTF-8?q?time=20worn=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/assets/unicopia/lang/zh_cn.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index 263b298f..bdbed780 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -143,7 +143,7 @@ "item.unicopia.amulet.energy": "能量: %d / %d", "item.unicopia.alicorn_amulet": "天角兽护符", - "item.unicopia.alicorn_amulet.lore": "老化度: %d", + "item.unicopia.alicorn_amulet.lore": "佩戴时长: %d", "item.unicopia.pearl_necklace": "珍珠项链", "item.unicopia.pearl_necklace.lore": "给予佩带者水下活动的能力", From 80225bf1e740ede400927288ee2a1a36af9dffbb Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 2 Jan 2024 23:03:41 +0100 Subject: [PATCH 07/98] Added the second magical plant --- assets/models/tentacle.bbmodel | 1 + assets/models/tentacle.java | 169 ++++++++++ assets/models/tentacle.png | Bin 0 -> 19159 bytes .../ability/EarthPonyGrowAbility.java | 9 + .../unicopia/client/URenderers.java | 1 + .../render/entity/TentacleEntityModel.java | 142 +++++++++ .../render/entity/TentacleEntityRenderer.java | 42 +++ .../unicopia/entity/mob/TentacleEntity.java | 289 ++++++++++++++++++ .../unicopia/entity/mob/UEntities.java | 3 + .../textures/entity/poison_joke/tentacle.png | Bin 0 -> 19159 bytes 10 files changed, 656 insertions(+) create mode 100644 assets/models/tentacle.bbmodel create mode 100644 assets/models/tentacle.java create mode 100644 assets/models/tentacle.png create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java create mode 100644 src/main/resources/assets/unicopia/textures/entity/poison_joke/tentacle.png diff --git a/assets/models/tentacle.bbmodel b/assets/models/tentacle.bbmodel new file mode 100644 index 00000000..0ccec62c --- /dev/null +++ b/assets/models/tentacle.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"modded_entity","box_uv":true},"name":"tentacle","model_identifier":"","modded_entity_version":"Fabric 1.17+","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":128,"height":128},"elements":[{"name":"a","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-7,-6,-7],"to":[7,10,7],"autouv":0,"color":7,"origin":[0,-6,0],"faces":{"north":{"uv":[14,14,28,30],"texture":0},"east":{"uv":[0,14,14,30],"texture":0},"south":{"uv":[42,14,56,30],"texture":0},"west":{"uv":[28,14,42,30],"texture":0},"up":{"uv":[28,14,14,0],"texture":0},"down":{"uv":[42,0,28,14],"texture":0}},"type":"cube","uuid":"43deca74-ef0e-9496-f060-a569a5f66a3c"},{"name":"b","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,8,-6],"to":[6,27,6],"autouv":0,"color":7,"origin":[0,0,0],"uv_offset":[0,30],"faces":{"north":{"uv":[12,42,24,61],"texture":0},"east":{"uv":[0,42,12,61],"texture":0},"south":{"uv":[36,42,48,61],"texture":0},"west":{"uv":[24,42,36,61],"texture":0},"up":{"uv":[24,42,12,30],"texture":0},"down":{"uv":[36,30,24,42],"texture":0}},"type":"cube","uuid":"2f7bd122-e685-5397-518a-d566774b5f7f"},{"name":"c","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,25,-5],"to":[5,48,5],"autouv":0,"color":7,"origin":[0,26,0],"uv_offset":[48,20],"faces":{"north":{"uv":[58,30,68,53],"texture":0},"east":{"uv":[48,30,58,53],"texture":0},"south":{"uv":[78,30,88,53],"texture":0},"west":{"uv":[68,30,78,53],"texture":0},"up":{"uv":[68,30,58,20],"texture":0},"down":{"uv":[78,20,68,30],"texture":0}},"type":"cube","uuid":"a32e4c53-99d7-93d5-23f7-ae163af4dae5"},{"name":"d","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-4,45,-4],"to":[4,66,4],"autouv":0,"color":7,"origin":[0,26,0],"uv_offset":[40,53],"faces":{"north":{"uv":[48,61,56,82],"texture":0},"east":{"uv":[40,61,48,82],"texture":0},"south":{"uv":[64,61,72,82],"texture":0},"west":{"uv":[56,61,64,82],"texture":0},"up":{"uv":[56,61,48,53],"texture":0},"down":{"uv":[64,53,56,61],"texture":0}},"type":"cube","uuid":"a4c5b57f-c555-154e-e673-7e114f6e5a21"},{"name":"e","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-3,64,-3],"to":[3,86,3],"autouv":0,"color":7,"origin":[-1.1793215979571126e-16,26,-4.382008627821946e-33],"uv_offset":[0,61],"faces":{"north":{"uv":[6,67,12,89],"texture":0},"east":{"uv":[0,67,6,89],"texture":0},"south":{"uv":[18,67,24,89],"texture":0},"west":{"uv":[12,67,18,89],"texture":0},"up":{"uv":[12,67,6,61],"texture":0},"down":{"uv":[18,61,12,67],"texture":0}},"type":"cube","uuid":"c8af392a-28ea-8a9f-cc0f-e8f19558f993"},{"name":"f","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-3,85,-3],"to":[3,100,3],"autouv":0,"color":7,"origin":[-2.620714662126916e-16,47,-9.737796950715435e-33],"uv_offset":[72,53],"faces":{"north":{"uv":[78,59,84,74],"texture":0},"east":{"uv":[72,59,78,74],"texture":0},"south":{"uv":[90,59,96,74],"texture":0},"west":{"uv":[84,59,90,74],"texture":0},"up":{"uv":[84,59,78,53],"texture":0},"down":{"uv":[90,53,84,59],"texture":0}},"type":"cube","uuid":"1d4836bc-6b01-9b8b-2958-1280f80f91b7"},{"name":"g","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-2.5999999999999996,99,-2.5999999999999996],"to":[2.5999999999999996,114,2.599999999999998],"autouv":0,"color":7,"origin":[-3.6034826604245074e-16,0,1],"uv_offset":[56,0],"faces":{"north":{"uv":[61.199999999999996,5.1999999999999975,66.39999999999999,20.199999999999996],"texture":0},"east":{"uv":[56,5.1999999999999975,61.199999999999996,20.199999999999996],"texture":0},"south":{"uv":[71.6,5.1999999999999975,76.8,20.199999999999996],"texture":0},"west":{"uv":[66.39999999999999,5.1999999999999975,71.6,20.199999999999996],"texture":0},"up":{"uv":[66.39999999999999,5.1999999999999975,61.199999999999996,0],"texture":0},"down":{"uv":[71.6,0,66.39999999999999,5.1999999999999975],"texture":0}},"type":"cube","uuid":"4001f6b1-caf3-d76f-2d5b-8636c85cd0fb"},{"name":"h","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-2.0999999999999996,113,-2.0999999999999996],"to":[2.0999999999999996,128,2.099999999999998],"autouv":0,"color":7,"origin":[-4.455214925615753e-16,14,1],"uv_offset":[24,61],"faces":{"north":{"uv":[28.199999999999996,65.2,32.4,80.19999999999999],"texture":0},"east":{"uv":[24,65.2,28.199999999999996,80.19999999999999],"texture":0},"south":{"uv":[36.599999999999994,65.2,40.8,80.19999999999999],"texture":0},"west":{"uv":[32.4,65.2,36.599999999999994,80.19999999999999],"texture":0},"up":{"uv":[32.4,65.2,28.199999999999996,61],"texture":0},"down":{"uv":[36.599999999999994,61,32.4,65.2],"texture":0}},"type":"cube","uuid":"4ba83545-3fcf-726d-033c-a1ac411a4ae3"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,-0.6703481918674496,-7.772537561589672],"to":[17,-0.6703481918674496,6.227462438410338],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[3,-0.6703481918674496,-7.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"43754e70-3fcd-b60a-62ee-01eadc22607a"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,-0.6703481918674496,-7.772537561589672],"to":[17,-0.6703481918674496,6.227462438410338],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[3,-0.6703481918674496,-7.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100,14.000000000000002,114,14.000000000000002],"texture":0},"east":{"uv":[86,14.000000000000002,100,14.000000000000002],"texture":0},"south":{"uv":[128,14.000000000000002,142,14.000000000000002],"texture":0},"west":{"uv":[114,14.000000000000002,128,14.000000000000002],"texture":0},"up":{"uv":[114,14.000000000000002,100,0],"texture":0},"down":{"uv":[128,0,114,14.000000000000002],"texture":0}},"type":"cube","uuid":"e5054bff-a980-a8dc-0221-58208085978f"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[5,-0.6703481918674511,4.227462438410328],"to":[19,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[5,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"13ebb478-c830-3a6e-858d-2269a5ea0642"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[5,-0.6703481918674511,4.227462438410328],"to":[19,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[5,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"2a87966b-98f6-fe32-0698-06d07dd0caa9"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674511,4.227462438410328],"to":[8,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-6,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"b2e2dbad-e1e3-ecb6-41d7-21cb1908deb8"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674511,4.227462438410328],"to":[8,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-6,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"f93aa31f-c197-3e66-58ad-a1c638ecce97"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674499,-6.772537561589672],"to":[8,-0.6703481918674499,7.227462438410338],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-6,-0.6703481918674499,-6.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"c1ca47a6-f9ba-555a-cc32-58d55b8809b9"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674499,-6.772537561589672],"to":[8,-0.6703481918674499,7.227462438410338],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-6,-0.6703481918674499,-6.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"b8b98b2d-632b-3acd-29a8-55efa4850547"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,-0.6703481918674496,-7.772537561589672],"to":[17,-0.6703481918674496,6.227462438410338],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[3,-0.6703481918674496,-7.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"e3aaee1e-958c-370c-a10d-94e142df629c"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[3,-0.6703481918674496,-7.772537561589672],"to":[17,-0.6703481918674496,6.227462438410338],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[3,-0.6703481918674496,-7.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"6bf59ab2-03d6-2995-17bc-76e27e52bf6c"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[5,-0.6703481918674511,4.227462438410328],"to":[19,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[5,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"666a373c-9dd2-0cfc-c1b7-13a5fe7fe392"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[5,-0.6703481918674511,4.227462438410328],"to":[19,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[5,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"ebaca892-208f-1ec8-a7b0-4b8bf692d869"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674511,4.227462438410328],"to":[8,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-6,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"02bb3148-7ad4-1fbe-f233-fcd02c1f00f4"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674511,4.227462438410328],"to":[8,-0.6703481918674511,18.22746243841034],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-6,-0.6703481918674511,4.227462438410328],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"ea8790f9-39c1-9607-7faf-4054f0782315"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674499,-6.772537561589672],"to":[8,-0.6703481918674499,7.227462438410338],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-6,-0.6703481918674499,-6.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"42f35de8-e9da-314a-ea8b-4ec3162ccc7b"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-0.6703481918674499,-6.772537561589672],"to":[8,-0.6703481918674499,7.227462438410338],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-6,-0.6703481918674499,-6.772537561589672],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"2a148807-d884-e05b-680e-36ba4ce450e5"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-4,40.62422566484149,-3.3435187525409757],"to":[10,40.62422566484149,10.656481247459041],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-4,40.62422566484149,-3.3435187525409757],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"e0119d00-cad6-3842-bbe5-6885628ff35e"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-4,40.62422566484149,-3.3435187525409757],"to":[10,40.62422566484149,10.656481247459041],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-4,40.62422566484149,-3.3435187525409757],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.00000000000001,114.00000000000001,14.00000000000001],"texture":0},"east":{"uv":[86,14.00000000000001,100.00000000000001,14.00000000000001],"texture":0},"south":{"uv":[128.00000000000003,14.00000000000001,142.00000000000003,14.00000000000001],"texture":0},"west":{"uv":[114.00000000000001,14.00000000000001,128.00000000000003,14.00000000000001],"texture":0},"up":{"uv":[114.00000000000001,14.00000000000001,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.00000000000001],"texture":0}},"type":"cube","uuid":"abe6d75d-fccf-7b97-e8ca-156f68f30b76"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2,60.13549813476108,3.0055163398616545],"to":[16,60.13549813476108,17.005516339861675],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[2,60.13549813476108,3.0055163398616545],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.000000000000018,114.00000000000001,14.000000000000018],"texture":0},"east":{"uv":[86,14.000000000000018,100.00000000000001,14.000000000000018],"texture":0},"south":{"uv":[128.00000000000003,14.000000000000018,142.00000000000003,14.000000000000018],"texture":0},"west":{"uv":[114.00000000000001,14.000000000000018,128.00000000000003,14.000000000000018],"texture":0},"up":{"uv":[114.00000000000001,14.000000000000018,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.000000000000018],"texture":0}},"type":"cube","uuid":"bf2e26ba-937a-73c8-3ab2-9240c2690b0b"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2,60.13549813476108,3.0055163398616545],"to":[16,60.13549813476108,17.005516339861675],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[2,60.13549813476108,3.0055163398616545],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000001,14.000000000000018,114.00000000000001,14.000000000000018],"texture":0},"east":{"uv":[86,14.000000000000018,100.00000000000001,14.000000000000018],"texture":0},"south":{"uv":[128.00000000000003,14.000000000000018,142.00000000000003,14.000000000000018],"texture":0},"west":{"uv":[114.00000000000001,14.000000000000018,128.00000000000003,14.000000000000018],"texture":0},"up":{"uv":[114.00000000000001,14.000000000000018,100.00000000000001,0],"texture":0},"down":{"uv":[128,0,114.00000000000001,14.000000000000018],"texture":0}},"type":"cube","uuid":"61246d8c-3a21-f2e2-2b5e-656b618b9315"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2.0000000000000004,89.02514437942168,0.47799980017960014],"to":[16,89.02514437942168,14.47799980017963],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[2.0000000000000004,89.02514437942168,0.47799980017960014],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000003,14.000000000000021,114.00000000000003,14.000000000000021],"texture":0},"east":{"uv":[86,14.000000000000021,100.00000000000003,14.000000000000021],"texture":0},"south":{"uv":[128.00000000000006,14.000000000000021,142.00000000000006,14.000000000000021],"texture":0},"west":{"uv":[114.00000000000003,14.000000000000021,128.00000000000006,14.000000000000021],"texture":0},"up":{"uv":[114.00000000000003,14.000000000000021,100.00000000000003,0],"texture":0},"down":{"uv":[128.00000000000003,0,114.00000000000003,14.000000000000021],"texture":0}},"type":"cube","uuid":"ff5dbdd3-d6eb-27ba-e1ab-c1a0f550fd3e"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2.0000000000000004,89.02514437942168,0.47799980017960014],"to":[16,89.02514437942168,14.47799980017963],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[2.0000000000000004,89.02514437942168,0.47799980017960014],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000003,14.000000000000021,114.00000000000003,14.000000000000021],"texture":0},"east":{"uv":[86,14.000000000000021,100.00000000000003,14.000000000000021],"texture":0},"south":{"uv":[128.00000000000006,14.000000000000021,142.00000000000006,14.000000000000021],"texture":0},"west":{"uv":[114.00000000000003,14.000000000000021,128.00000000000006,14.000000000000021],"texture":0},"up":{"uv":[114.00000000000003,14.000000000000021,100.00000000000003,0],"texture":0},"down":{"uv":[128.00000000000003,0,114.00000000000003,14.000000000000021],"texture":0}},"type":"cube","uuid":"8e0f248f-1228-4a38-2a74-cb0d23718fdc"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-22.881490097225765,145.88837332325411,6.8760974475566785],"to":[-8.881490097225756,145.88837332325411,20.876097447556763],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000009,14.000000000000085,114.0000000000001,14.000000000000085],"texture":0},"east":{"uv":[86,14.000000000000085,100.00000000000009,14.000000000000085],"texture":0},"south":{"uv":[128.00000000000017,14.000000000000085,142.0000000000002,14.000000000000085],"texture":0},"west":{"uv":[114.0000000000001,14.000000000000085,128.00000000000017,14.000000000000085],"texture":0},"up":{"uv":[114.0000000000001,14.000000000000085,100.00000000000009,0],"texture":0},"down":{"uv":[128.0000000000001,0,114.0000000000001,14.000000000000085],"texture":0}},"type":"cube","uuid":"817666d5-286f-6161-8446-3970704a3265"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0.16641666179338577,107.95005796968688,-16.171809311462464],"to":[14.166416661793397,107.95005796968688,-2.17180931146247],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100,13.999999999999993,114,13.999999999999993],"texture":0},"east":{"uv":[86,13.999999999999993,100,13.999999999999993],"texture":0},"south":{"uv":[128,13.999999999999993,142,13.999999999999993],"texture":0},"west":{"uv":[114,13.999999999999993,128,13.999999999999993],"texture":0},"up":{"uv":[114,13.999999999999993,100,0],"texture":0},"down":{"uv":[128,0,114,13.999999999999993],"texture":0}},"type":"cube","uuid":"5f3e16dd-eed6-733e-e1db-f1934b558e88"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-22.881490097225765,145.88837332325411,6.8760974475566785],"to":[-8.881490097225756,145.88837332325411,20.876097447556763],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000009,14.000000000000085,114.00000000000009,14.000000000000085],"texture":0},"east":{"uv":[86,14.000000000000085,100.00000000000009,14.000000000000085],"texture":0},"south":{"uv":[128.00000000000017,14.000000000000085,142.00000000000017,14.000000000000085],"texture":0},"west":{"uv":[114.00000000000009,14.000000000000085,128.00000000000017,14.000000000000085],"texture":0},"up":{"uv":[114.00000000000009,14.000000000000085,100.00000000000009,0],"texture":0},"down":{"uv":[128.0000000000001,0,114.00000000000009,14.000000000000085],"texture":0}},"type":"cube","uuid":"573cf719-c66a-d73e-148d-d14e9617d0ac"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0.16641666179338577,107.95005796968688,-16.171809311462464],"to":[14.166416661793397,107.95005796968688,-2.17180931146247],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100,13.999999999999995,114,13.999999999999995],"texture":0},"east":{"uv":[86,13.999999999999995,100,13.999999999999995],"texture":0},"south":{"uv":[128,13.999999999999995,142,13.999999999999995],"texture":0},"west":{"uv":[114,13.999999999999995,128,13.999999999999995],"texture":0},"up":{"uv":[114,13.999999999999995,100,0],"texture":0},"down":{"uv":[128,0,114,13.999999999999995],"texture":0}},"type":"cube","uuid":"e4e3e2ae-0684-ee78-4ec9-33f0f9461b19"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-22.881490097225765,145.88837332325411,6.8760974475566785],"to":[-8.881490097225756,145.88837332325411,20.876097447556763],"autouv":0,"color":0,"rotation":[-180,-45.000000000000156,90],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100.00000000000009,14.000000000000085,114.00000000000009,14.000000000000085],"texture":0},"east":{"uv":[86,14.000000000000085,100.00000000000009,14.000000000000085],"texture":0},"south":{"uv":[128.00000000000017,14.000000000000085,142.00000000000017,14.000000000000085],"texture":0},"west":{"uv":[114.00000000000009,14.000000000000085,128.00000000000017,14.000000000000085],"texture":0},"up":{"uv":[114.00000000000009,14.000000000000085,100.00000000000009,0],"texture":0},"down":{"uv":[128.0000000000001,0,114.00000000000009,14.000000000000085],"texture":0}},"type":"cube","uuid":"c59ea2f3-dbd7-5b92-4640-4acc2121a8b2"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0.16641666179338577,107.95005796968688,-16.171809311462464],"to":[14.166416661793397,107.95005796968688,-2.17180931146247],"autouv":0,"color":0,"rotation":[-89.99999999999994,-6.3611093629270335e-15,45],"origin":[-36.04320341964982,110.62188448501286,-2.507082580585397],"uv_offset":[86,0],"faces":{"north":{"uv":[100,13.999999999999995,114,13.999999999999995],"texture":0},"east":{"uv":[86,13.999999999999995,100,13.999999999999995],"texture":0},"south":{"uv":[128,13.999999999999995,142,13.999999999999995],"texture":0},"west":{"uv":[114,13.999999999999995,128,13.999999999999995],"texture":0},"up":{"uv":[114,13.999999999999995,100,0],"texture":0},"down":{"uv":[128,0,114,13.999999999999995],"texture":0}},"type":"cube","uuid":"3bb469e2-e5a5-7cfc-3e9c-6fd43b8a3ca0"}],"outliner":[{"name":"bone_a","origin":[0,0,0],"rotation":[-7.951386703658792e-15,0,0],"color":0,"uuid":"836e9354-0309-70b5-cb60-e7f38118e743","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["43deca74-ef0e-9496-f060-a569a5f66a3c",{"name":"flower_4","origin":[-6,-0.6703481918674499,-6.772537561589672],"rotation":[-91.26598229300457,52.08446097361874,-64.05377573888674],"color":0,"uuid":"2ea8b2cf-a2c0-c454-2fb5-d9bc522a921c","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["c1ca47a6-f9ba-555a-cc32-58d55b8809b9","b8b98b2d-632b-3acd-29a8-55efa4850547",{"name":"flower_8","origin":[-6,-0.6703481918674499,-6.772537561589672],"rotation":[-167.64364206893785,46.98788282368353,-125.90165504433261],"color":0,"uuid":"02ef3343-dfcc-9af4-29a2-6dc427c70898","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["42f35de8-e9da-314a-ea8b-4ec3162ccc7b","2a148807-d884-e05b-680e-36ba4ce450e5"]}]},{"name":"flower_3","origin":[-6,-0.6703481918674511,4.227462438410328],"rotation":[137.96042965468234,-13.430580338215425,-145.38236406964168],"color":0,"uuid":"2fc2f20b-05ce-0410-dd40-8d55ec1d814a","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["b2e2dbad-e1e3-ecb6-41d7-21cb1908deb8","f93aa31f-c197-3e66-58ad-a1c638ecce97",{"name":"flower_7","origin":[-6,-0.6703481918674511,4.227462438410328],"rotation":[137.96042965468234,-13.430580338215425,-145.38236406964168],"color":0,"uuid":"3c08633a-6d30-3951-f49a-b815826de0cd","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["02bb3148-7ad4-1fbe-f233-fcd02c1f00f4","ea8790f9-39c1-9607-7faf-4054f0782315"]}]},{"name":"flower_2","origin":[5,-0.6703481918674511,4.227462438410328],"rotation":[72.75299322235041,-55.44873947197246,-103.02095072899253],"color":0,"uuid":"8cc35e91-dbf2-1e20-efc6-d0c6dc9e1e32","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["13ebb478-c830-3a6e-858d-2269a5ea0642","2a87966b-98f6-fe32-0698-06d07dd0caa9",{"name":"flower_6","origin":[5,-0.6703481918674511,4.227462438410328],"rotation":[72.75299322235041,-55.44873947197246,-103.02095072899253],"color":0,"uuid":"93297f86-e3ca-4d71-a6c8-298ff77bbb1a","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["666a373c-9dd2-0cfc-c1b7-13a5fe7fe392","ebaca892-208f-1ec8-a7b0-4b8bf692d869"]}]},{"name":"flower_1","origin":[3,-0.6703481918674496,-7.772537561589672],"rotation":[-34.96649542120608,3.067430398401471,-33.59739024796235],"color":0,"uuid":"59363886-4b5a-2a3c-75cd-8d6276de69b3","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["e5054bff-a980-a8dc-0221-58208085978f","43754e70-3fcd-b60a-62ee-01eadc22607a",{"name":"flower_5","origin":[3,-0.6703481918674496,-7.772537561589672],"rotation":[-34.96649542120608,3.067430398401471,-33.59739024796235],"color":0,"uuid":"0265d420-aeaa-b538-1252-89df12b0f9b6","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["e3aaee1e-958c-370c-a10d-94e142df629c","6bf59ab2-03d6-2995-17bc-76e27e52bf6c"]}]},{"name":"bone_b","origin":[0,9,0],"rotation":[15,0,0],"color":0,"uuid":"aa6d7f64-89b9-a900-a3dc-3a9fcdc22618","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["2f7bd122-e685-5397-518a-d566774b5f7f",{"name":"bone_c","origin":[0,25,0],"rotation":[-6.599650964036798e-14,0,0],"color":0,"uuid":"7b4502a2-fe6a-53ad-2f9f-391f31027828","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":[{"name":"flower_9","origin":[-4,40.62422566484149,-3.3435187525409757],"rotation":[-167.64364206893785,46.987882823683556,-140.9016550443326],"color":0,"uuid":"5ba196d8-95ae-bace-28cb-48bdb2d6cbac","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["e0119d00-cad6-3842-bbe5-6885628ff35e","abe6d75d-fccf-7b97-e8ca-156f68f30b76"]},"a32e4c53-99d7-93d5-23f7-ae163af4dae5",{"name":"bone_d","origin":[0,43,0],"rotation":[10.000000000000005,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"286e227f-8a7a-cfd3-27a0-86aed0c10bb7","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":[{"name":"flower_10","origin":[2,60.13549813476108,3.0055163398616545],"rotation":[163.90070263038493,34.045350837165316,140.2721846400254],"color":0,"uuid":"1ed2c36a-c359-91ea-40ba-572a6c8757f7","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["bf2e26ba-937a-73c8-3ab2-9240c2690b0b","61246d8c-3a21-f2e2-2b5e-656b618b9315"]},"a4c5b57f-c555-154e-e673-7e114f6e5a21",{"name":"bone_e","origin":[0,61,0],"rotation":[-10,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"960ce6d7-f146-7814-e0c9-b11b5b43956a","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["c8af392a-28ea-8a9f-cc0f-e8f19558f993",{"name":"bone_f","origin":[0,83,0],"rotation":[-10,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"3f876c03-f9b2-1dac-08bc-990abce00d76","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["1d4836bc-6b01-9b8b-2958-1280f80f91b7",{"name":"flower_11","origin":[2.0000000000000004,89.02514437942168,0.47799980017960014],"rotation":[-158.0615625451014,-19.935030254861065,145.712572187446],"color":0,"uuid":"42c04c52-8414-cc27-f1a2-bce1e6fdc48c","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["ff5dbdd3-d6eb-27ba-e1ab-c1a0f550fd3e","8e0f248f-1228-4a38-2a74-cb0d23718fdc"]},{"name":"bone_g","origin":[0,98,0],"rotation":[-17.5,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"87fdafb8-862b-0fc4-f194-483e4c6ab1d2","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["4001f6b1-caf3-d76f-2d5b-8636c85cd0fb",{"name":"bone_h","origin":[0,111,0],"rotation":[-15,2.1289409148363706e-15,3.753897236198147e-16],"color":0,"uuid":"bd8fb7b0-ef45-7564-a12a-05cb47421493","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["4ba83545-3fcf-726d-033c-a1ac411a4ae3",{"name":"flower_12","origin":[-4.852359674770397e-16,126.1462282992986,0.6365198729059891],"rotation":[-158.06156254510137,-19.93503025486101,170.7125721874457],"color":0,"uuid":"faa91113-8626-24ec-baa2-9003ea1e0eda","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["817666d5-286f-6161-8446-3970704a3265","5f3e16dd-eed6-733e-e1db-f1934b558e88",{"name":"flower_13","origin":[-4.852359674770397e-16,126.1462282992986,0.6365198729059891],"rotation":[-158.06156254510134,-19.935030254861015,148.21257218744574],"color":0,"uuid":"749d951a-ecbc-4197-9c32-365bbb0a98a9","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["573cf719-c66a-d73e-148d-d14e9617d0ac","e4e3e2ae-0684-ee78-4ec9-33f0f9461b19"]},{"name":"flower_14","origin":[-4.852359674770397e-16,126.1462282992986,0.6365198729059891],"rotation":[111.22147558087971,59.83922506916349,99.72701164210366],"color":0,"uuid":"3c36c30a-bdb5-bede-7d1c-4073204f716c","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["c59ea2f3-dbd7-5b92-4640-4acc2121a8b2","3bb469e2-e5a5-7cfc-3e9c-6fd43b8a3ca0"]}]}]}]}]}]}]}]}]}]}],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/assets/models/tentacle.png","name":"tentacle.png","folder":"block","namespace":"","id":"0","particle":false,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"59729803-364e-77c5-ab9a-5c088a316d59","relative_path":"../tentacle.png","source":"data:image/png;base64,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"}],"fabricOptions":{"header":"package com.example.mod;","entity":"Entity","render":"","members":""}} \ No newline at end of file diff --git a/assets/models/tentacle.java b/assets/models/tentacle.java new file mode 100644 index 00000000..81792fec --- /dev/null +++ b/assets/models/tentacle.java @@ -0,0 +1,169 @@ +// Made with Blockbench 4.8.3 +// Exported for Minecraft version 1.17+ for Yarn +// Paste this class into your mod and generate all required imports +public class tentacle extends EntityModel { + private final ModelPart bone_a; + private final ModelPart flower_4; + private final ModelPart cube_r1; + private final ModelPart cube_r2; + private final ModelPart flower_8; + private final ModelPart cube_r3; + private final ModelPart cube_r4; + private final ModelPart flower_3; + private final ModelPart cube_r5; + private final ModelPart cube_r6; + private final ModelPart flower_7; + private final ModelPart cube_r7; + private final ModelPart cube_r8; + private final ModelPart flower_2; + private final ModelPart cube_r9; + private final ModelPart cube_r10; + private final ModelPart flower_6; + private final ModelPart cube_r11; + private final ModelPart cube_r12; + private final ModelPart flower_1; + private final ModelPart cube_r13; + private final ModelPart cube_r14; + private final ModelPart flower_5; + private final ModelPart cube_r15; + private final ModelPart cube_r16; + private final ModelPart bone_b; + private final ModelPart bone_c; + private final ModelPart flower_9; + private final ModelPart cube_r17; + private final ModelPart cube_r18; + private final ModelPart bone_d; + private final ModelPart flower_10; + private final ModelPart cube_r19; + private final ModelPart cube_r20; + private final ModelPart bone_e; + private final ModelPart bone_f; + private final ModelPart flower_11; + private final ModelPart cube_r21; + private final ModelPart cube_r22; + private final ModelPart bone_g; + private final ModelPart bone_h; + private final ModelPart flower_12; + private final ModelPart cube_r23; + private final ModelPart cube_r24; + private final ModelPart flower_13; + private final ModelPart cube_r25; + private final ModelPart cube_r26; + private final ModelPart flower_14; + private final ModelPart cube_r27; + private final ModelPart cube_r28; + public tentacle(ModelPart root) { + this.bone_a = root.getChild("bone_a"); + } + public static TexturedModelData getTexturedModelData() { + ModelData modelData = new ModelData(); + ModelPartData modelPartData = modelData.getRoot(); + ModelPartData bone_a = modelPartData.addChild("bone_a", ModelPartBuilder.create().uv(0, 0).cuboid(-7.0F, -10.0F, -7.0F, 14.0F, 16.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 24.0F, 0.0F, 0.0F, 0.0F, 0.0F)); + + ModelPartData flower_4 = bone_a.addChild("flower_4", ModelPartBuilder.create(), ModelTransform.of(6.0F, 0.6703F, -6.7725F, 1.5929F, -0.909F, -1.1179F)); + + ModelPartData cube_r1 = flower_4.addChild("cube_r1", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r2 = flower_4.addChild("cube_r2", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_8 = flower_4.addChild("flower_8", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.9259F, -0.8201F, -2.1974F)); + + ModelPartData cube_r3 = flower_8.addChild("cube_r3", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r4 = flower_8.addChild("cube_r4", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_3 = bone_a.addChild("flower_3", ModelPartBuilder.create(), ModelTransform.of(6.0F, 0.6703F, 4.2275F, -2.4079F, 0.2344F, -2.5374F)); + + ModelPartData cube_r5 = flower_3.addChild("cube_r5", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r6 = flower_3.addChild("cube_r6", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_7 = flower_3.addChild("flower_7", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, -2.4079F, 0.2344F, -2.5374F)); + + ModelPartData cube_r7 = flower_7.addChild("cube_r7", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r8 = flower_7.addChild("cube_r8", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_2 = bone_a.addChild("flower_2", ModelPartBuilder.create(), ModelTransform.of(-5.0F, 0.6703F, 4.2275F, -1.2698F, 0.9678F, -1.7981F)); + + ModelPartData cube_r9 = flower_2.addChild("cube_r9", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r10 = flower_2.addChild("cube_r10", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_6 = flower_2.addChild("flower_6", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, -1.2698F, 0.9678F, -1.7981F)); + + ModelPartData cube_r11 = flower_6.addChild("cube_r11", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r12 = flower_6.addChild("cube_r12", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_1 = bone_a.addChild("flower_1", ModelPartBuilder.create(), ModelTransform.of(-3.0F, 0.6703F, -7.7725F, 0.6103F, -0.0535F, -0.5864F)); + + ModelPartData cube_r13 = flower_1.addChild("cube_r13", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r14 = flower_1.addChild("cube_r14", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_5 = flower_1.addChild("flower_5", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, 0.6103F, -0.0535F, -0.5864F)); + + ModelPartData cube_r15 = flower_5.addChild("cube_r15", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r16 = flower_5.addChild("cube_r16", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData bone_b = bone_a.addChild("bone_b", ModelPartBuilder.create().uv(0, 30).cuboid(-6.0F, -18.0F, -6.0F, 12.0F, 19.0F, 12.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -9.0F, 0.0F, -0.2618F, 0.0F, 0.0F)); + + ModelPartData bone_c = bone_b.addChild("bone_c", ModelPartBuilder.create().uv(48, 20).cuboid(-5.0F, -23.0F, -5.0F, 10.0F, 23.0F, 10.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -16.0F, 0.0F, 0.0F, 0.0F, 0.0F)); + + ModelPartData flower_9 = bone_c.addChild("flower_9", ModelPartBuilder.create(), ModelTransform.of(4.0F, -15.6242F, -3.3435F, 2.9259F, -0.8201F, -2.4592F)); + + ModelPartData cube_r17 = flower_9.addChild("cube_r17", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r18 = flower_9.addChild("cube_r18", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData bone_d = bone_c.addChild("bone_d", ModelPartBuilder.create().uv(40, 53).cuboid(-4.0F, -23.0F, -4.0F, 8.0F, 21.0F, 8.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -18.0F, 0.0F, -0.1745F, 0.0F, 0.0F)); + + ModelPartData flower_10 = bone_d.addChild("flower_10", ModelPartBuilder.create(), ModelTransform.of(-2.0F, -17.1355F, 3.0055F, -2.8606F, -0.5942F, 2.4482F)); + + ModelPartData cube_r19 = flower_10.addChild("cube_r19", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r20 = flower_10.addChild("cube_r20", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData bone_e = bone_d.addChild("bone_e", ModelPartBuilder.create().uv(0, 61).cuboid(-3.0F, -25.0F, -3.0F, 6.0F, 22.0F, 6.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -18.0F, 0.0F, 0.1745F, 0.0F, 0.0F)); + + ModelPartData bone_f = bone_e.addChild("bone_f", ModelPartBuilder.create().uv(72, 53).cuboid(-3.0F, -17.0F, -3.0F, 6.0F, 15.0F, 6.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -22.0F, 0.0F, 0.1745F, 0.0F, 0.0F)); + + ModelPartData flower_11 = bone_f.addChild("flower_11", ModelPartBuilder.create(), ModelTransform.of(-2.0F, -6.0251F, 0.478F, 2.7587F, 0.3479F, 2.5432F)); + + ModelPartData cube_r21 = flower_11.addChild("cube_r21", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r22 = flower_11.addChild("cube_r22", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData bone_g = bone_f.addChild("bone_g", ModelPartBuilder.create().uv(56, 0).cuboid(-2.6F, -16.0F, -2.6F, 5.2F, 15.0F, 5.2F, new Dilation(0.0F)), ModelTransform.of(0.0F, -15.0F, 0.0F, 0.3054F, 0.0F, 0.0F)); + + ModelPartData bone_h = bone_g.addChild("bone_h", ModelPartBuilder.create().uv(24, 61).cuboid(-2.1F, -17.0F, -2.1F, 4.2F, 15.0F, 4.2F, new Dilation(0.0F)), ModelTransform.of(0.0F, -13.0F, 0.0F, 0.2618F, 0.0F, 0.0F)); + + ModelPartData flower_12 = bone_h.addChild("flower_12", ModelPartBuilder.create(), ModelTransform.of(0.0F, -15.1462F, 0.6365F, 2.7587F, 0.3479F, 2.9795F)); + + ModelPartData cube_r23 = flower_12.addChild("cube_r23", ModelPartBuilder.create().uv(86, 0).cuboid(-50.2096F, 2.6718F, -13.6647F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r24 = flower_12.addChild("cube_r24", ModelPartBuilder.create().uv(86, 0).cuboid(-27.1617F, -35.2665F, 9.3832F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_13 = flower_12.addChild("flower_13", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.7587F, 0.3479F, 2.5868F)); + + ModelPartData cube_r25 = flower_13.addChild("cube_r25", ModelPartBuilder.create().uv(86, 0).cuboid(-50.2096F, 2.6718F, -13.6647F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r26 = flower_13.addChild("cube_r26", ModelPartBuilder.create().uv(86, 0).cuboid(-27.1617F, -35.2665F, 9.3832F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 3.1416F, 0.7854F, 1.5708F)); + + ModelPartData flower_14 = flower_12.addChild("flower_14", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, -1.9412F, -1.0444F, 1.7406F)); + + ModelPartData cube_r27 = flower_14.addChild("cube_r27", ModelPartBuilder.create().uv(86, 0).cuboid(-50.2096F, 2.6718F, -13.6647F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 1.5708F, 0.0F, 0.7854F)); + + ModelPartData cube_r28 = flower_14.addChild("cube_r28", ModelPartBuilder.create().uv(86, 0).cuboid(-27.1617F, -35.2665F, 9.3832F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 3.1416F, 0.7854F, 1.5708F)); + return TexturedModelData.of(modelData, 128, 128); + } + @Override + public void setAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + @Override + public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) { + bone_a.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); + } +} \ No newline at end of file diff --git a/assets/models/tentacle.png b/assets/models/tentacle.png new file mode 100644 index 0000000000000000000000000000000000000000..1026c1c0a49236c48e11c922a74a22597bec6819 GIT binary patch literal 19159 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]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 { w.setBlockState(pos.down(), Blocks.DIRT.getDefaultState()); } w.setBlockState(pos, UBlocks.PLUNDER_VINE_BUD.getDefaultState()); + } else if (w.random.nextInt(5000) == 0) { + if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) { + w.setBlockState(pos.down(), Blocks.DIRT.getDefaultState()); + } + w.breakBlock(pos, true); + TentacleEntity tentacle = new TentacleEntity(w, pos); + tentacle.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); + w.spawnEntity(tentacle); } return 1; } diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 5bf94a22..93fc054a 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -97,6 +97,7 @@ public interface URenderers { EntityRendererRegistry.register(UEntities.AIR_BALLOON, AirBalloonEntityRenderer::new); EntityRendererRegistry.register(UEntities.FRIENDLY_CREEPER, FriendlyCreeperEntityRenderer::new); EntityRendererRegistry.register(UEntities.LOOT_BUG, LootBugEntityRenderer::new); + EntityRendererRegistry.register(UEntities.TENTACLE, TentacleEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.FANCY_BED, CloudBedBlockEntityRenderer::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java new file mode 100644 index 00000000..e365457a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java @@ -0,0 +1,142 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.minelittlepony.unicopia.entity.mob.TentacleEntity; + +import net.minecraft.client.model.Dilation; +import net.minecraft.client.model.ModelData; +import net.minecraft.client.model.ModelPart; +import net.minecraft.client.model.ModelPartBuilder; +import net.minecraft.client.model.ModelPartData; +import net.minecraft.client.model.ModelTransform; +import net.minecraft.client.model.TexturedModelData; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; + +public class TentacleEntityModel extends EntityModel { + private final ModelPart part; + + private final Map parts = new HashMap<>(); + private final List bones = new ArrayList<>(); + private final List brushes; + + private final ModelPart tip; + + public TentacleEntityModel(ModelPart root) { + super(RenderLayer::getEntityTranslucent); + this.part = root; + for (String key : List.of("bone_a", "bone_b", "bone_c", "bone_d", "bone_e", "bone_f", "bone_g", "bone_h")) { + parts.put(key, root = root.getChild(key)); + bones.add(root); + } + var bone_a = parts.get("bone_a"); + brushes = List.of( + bone_a.getChild("brush_1"), + bone_a.getChild("brush_2"), + bone_a.getChild("brush_3"), + bone_a.getChild("brush_4"), + parts.get("bone_c").getChild("flower_1"), + parts.get("bone_d").getChild("flower_2"), + parts.get("bone_f").getChild("flower_3")); + tip = parts.get("bone_h").getChild("tip"); + } + + public static TexturedModelData getTexturedModelData() { + ModelData data = new ModelData(); + data.getRoot() + .addChild("bone_a", ModelPartBuilder.create().uv(0, 0).cuboid(-7, -10, -7, 14, 16, 14, Dilation.NONE), ModelTransform.pivot(0, 24, 0)) + .addChild("bone_b", ModelPartBuilder.create().uv(0, 30).cuboid(-6, -18, -6, 12, 19, 12, Dilation.NONE), ModelTransform.of(0, -9, 0, -0.2618F, 0, 0)) + .addChild("bone_c", ModelPartBuilder.create().uv(48, 20).cuboid(-5, -23, -5, 10, 23, 10, Dilation.NONE), ModelTransform.of(0, -16, 0, 0, 0, 0)) + .addChild("bone_d", ModelPartBuilder.create().uv(40, 53).cuboid(-4, -23, -4, 8, 21, 8, Dilation.NONE), ModelTransform.of(0, -18, 0, -0.1745F, 0, 0)) + .addChild("bone_e", ModelPartBuilder.create().uv(0, 61).cuboid(-3, -25, -3, 6, 22, 6, Dilation.NONE), ModelTransform.of(0, -18, 0, 0.1745F, 0, 0)) + .addChild("bone_f", ModelPartBuilder.create().uv(72, 53).cuboid(-3, -17, -3, 6, 15, 6, Dilation.NONE), ModelTransform.of(0, -22, 0, 0.1745F, 0, 0)) + .addChild("bone_g", ModelPartBuilder.create().uv(56, 0).cuboid(-2.6F, -16, -2.6F, 5.2F, 15, 5.2F, Dilation.NONE), ModelTransform.of(0, -15, 0, 0.3054F, 0, 0)) + .addChild("bone_h", ModelPartBuilder.create().uv(24, 61).cuboid(-2.1F, -17, -2.1F, 4.2F, 15, 4.2F, Dilation.NONE), ModelTransform.of(0, -13, 0, 0.2618F, 0, 0)); + return TexturedModelData.of(addFlowers(data), 128, 128); + } + + private static ModelData addFlowers(ModelData data) { + ModelPartData bone_a = data.getRoot().getChild("bone_a"); + createFlowerSinglet(createFlowerSinglet(bone_a.addChild("brush_1", ModelPartBuilder.create(), ModelTransform.of(-3, 0.6703F, -7.7725F, 0.6103F, -0.0535F, -0.5864F))).addChild("bundle", ModelPartBuilder.create(), ModelTransform.rotation(0.6103F, -0.0535F, -0.5864F))); + createFlowerSinglet(createFlowerSinglet(bone_a.addChild("brush_2", ModelPartBuilder.create(), ModelTransform.of(-5, 0.6703F, 4.2275F, -1.2698F, 0.9678F, -1.7981F))).addChild("bundle", ModelPartBuilder.create(), ModelTransform.rotation(-1.2698F, 0.9678F, -1.7981F))); + createFlowerSinglet(createFlowerSinglet(bone_a.addChild("brush_3", ModelPartBuilder.create(), ModelTransform.of( 6, 0.6703F, 4.2275F, -2.4079F, 0.2344F, -2.5374F))).addChild("bundle", ModelPartBuilder.create(), ModelTransform.rotation(-2.4079F, 0.2344F, -2.5374F))); + createFlowerSinglet(createFlowerSinglet(bone_a.addChild("brush_4", ModelPartBuilder.create(), ModelTransform.of( 6, 0.6703F, -6.7725F, 1.5929F, -0.909F, -1.1179F))).addChild("bundle", ModelPartBuilder.create(), ModelTransform.rotation(2.9259F, -0.8201F, -2.1974F))); + + ModelPartData bone_c = bone_a.getChild("bone_b").getChild("bone_c"); + createFlowerSinglet(bone_c.addChild("flower_1", ModelPartBuilder.create(), ModelTransform.of( 4, -15.6242F, -3.3435F, 2.9259F, -0.8201F, -2.4592F))); + + ModelPartData bone_d = bone_c.getChild("bone_d"); + createFlowerSinglet(bone_d.addChild("flower_2", ModelPartBuilder.create(), ModelTransform.of(-2, -17.1355F, 3.0055F, -2.8606F, -0.5942F, 2.4482F))); + ModelPartData bone_f = bone_d.getChild("bone_e").getChild("bone_f"); + createFlowerSinglet(bone_f.addChild("flower_3", ModelPartBuilder.create(), ModelTransform.of(-2, -6.0251F, 0.478F, 2.7587F, 0.3479F, 2.5432F))); + + ModelPartData flower_12 = createFlowerBunch(bone_f.getChild("bone_g").getChild("bone_h").addChild("tip", ModelPartBuilder.create(), ModelTransform.of(0, -15.1462F, 0.6365F, 2.7587F, 0.3479F, 2.9795F))); + createFlowerBunch(flower_12.addChild("bundle_1", ModelPartBuilder.create(), ModelTransform.rotation( 2.7587F, 0.3479F, 2.5868F))); + createFlowerBunch(flower_12.addChild("bundle_2", ModelPartBuilder.create(), ModelTransform.rotation(-1.9412F, -1.0444F, 1.7406F))); + return data; + } + + private static ModelPartData createFlowerSinglet(ModelPartData parent) { + parent.addChild("flower_1", ModelPartBuilder.create().uv(86, 0).cuboid(-14, 0, 0, 14, 0, 14, Dilation.NONE), ModelTransform.rotation(1.5708F, 0, 0.7854F)); + parent.addChild("flower_2", ModelPartBuilder.create().uv(86, 0).cuboid(-14, 0, 0, 14, 0, 14, Dilation.NONE), ModelTransform.rotation(3.1416F, 0.7854F, 1.5708F)); + return parent; + } + + private static ModelPartData createFlowerBunch(ModelPartData parent) { + parent.addChild("flower_1", ModelPartBuilder.create().uv(86, 0).cuboid(-50.2096F, 2.6718F, -13.6647F, 14, 0, 14, Dilation.NONE), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 1.5708F, 0, 0.7854F)); + parent.addChild("flower_2", ModelPartBuilder.create().uv(86, 0).cuboid(-27.1617F, -35.2665F, 9.3832F, 14, 0, 14, Dilation.NONE), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 3.1416F, 0.7854F, 1.5708F)); + return parent; + } + + @Override + public void setAngles(TentacleEntity entity, float limbSwing, float limbSwingAmount, float tickDelta, float yaw, float pitch) { + float age = entity.age + tickDelta + (entity.getUuid().getMostSignificantBits() % 100); + float idleWaveTimer = entity.getAnimationTimer(tickDelta); + + float attackProgress = entity.isAttacking() ? Math.abs(MathHelper.sin(entity.getAttackProgress(tickDelta) * MathHelper.PI)) : 0; + float attackCurve = attackProgress * -0.5F; + float sweepDirection = 1; + + float bendIntentisty = 1 + entity.getAttackProgress(tickDelta) / 2F; + + part.yaw = (yaw * MathHelper.RADIANS_PER_DEGREE) + MathHelper.HALF_PI * attackProgress; + + for (ModelPart bone : bones) { + float idlePitch = MathHelper.sin(idleWaveTimer) * 0.0226F * bendIntentisty; + float idleYaw = MathHelper.cos(idleWaveTimer + 0.53F) * 0.07F; + float idleRoll = MathHelper.sin(idleWaveTimer * 0.2F) * 0.0226F * bendIntentisty; + idleWaveTimer += 1.5F; + bendIntentisty += 3F; + bone.resetTransform(); + + bone.pitch = MathHelper.lerp(attackProgress, idlePitch, bone.pitch + attackCurve); + bone.yaw = MathHelper.lerp(attackProgress, idleYaw, bone.yaw + sweepDirection * attackCurve); + bone.roll = MathHelper.lerp(attackProgress, idleRoll, bone.roll); + attackCurve *= 1.04F; + } + + float direction = 1; + + for (ModelPart brush : brushes) { + brush.resetTransform(); + brush.pitch += MathHelper.sin(age * 0.003F) * 0.03F; + brush.yaw += MathHelper.cos(age * 0.003F) * 0.1F * (direction *= -1); + } + + tip.resetTransform(); + tip.pitch += MathHelper.sin(age * 0.003F) * 0.3F; + tip.yaw += MathHelper.sin(age * 0.03F) * 0.3F; + } + + @Override + public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) { + part.render(matrices, vertices, light, overlay); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java new file mode 100644 index 00000000..f6ba79b9 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java @@ -0,0 +1,42 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.mob.TentacleEntity; + +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; + +public class TentacleEntityRenderer extends EntityRenderer { + private static final Identifier TEXTURE = Unicopia.id("textures/entity/poison_joke/tentacle.png"); + + private final TentacleEntityModel model; + + public TentacleEntityRenderer(EntityRendererFactory.Context context) { + super(context); + model = new TentacleEntityModel(TentacleEntityModel.getTexturedModelData().createModel()); + } + + @Override + public void render(TentacleEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + matrices.push(); + matrices.scale(-1, -1, 1); + float scale = entity.getGrowth(tickDelta); + + matrices.scale(scale, scale, scale); + matrices.translate(0, -0.9F, 0); + + model.setAngles(entity, 0, 0, tickDelta, entity.getYaw(tickDelta), entity.getPitch(tickDelta)); + model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); + matrices.pop(); + super.render(entity, yaw, tickDelta, matrices, vertices, light); + } + + @Override + public Identifier getTexture(TentacleEntity entity) { + return TEXTURE; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java new file mode 100644 index 00000000..cec6b0df --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java @@ -0,0 +1,289 @@ +package com.minelittlepony.unicopia.entity.mob; + +import java.util.Comparator; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.util.shape.Sphere; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.MovementType; +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.decoration.AbstractDecorationEntity; +import net.minecraft.entity.mob.HostileEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.registry.tag.ItemTags; +import net.minecraft.sound.SoundEvents; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.RaycastContext; +import net.minecraft.world.World; + +public class TentacleEntity extends AbstractDecorationEntity { + static final byte ATTACK_STATUS = 54; + static final int MAX_GROWTH = 25; + private static final TrackedData GROWTH = DataTracker.registerData(TentacleEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData MOTION_OFFSET = DataTracker.registerData(TentacleEntity.class, TrackedDataHandlerRegistry.INTEGER); + + @Nullable + private Box visibilityBox; + + private int prevGrowth; + private int attackingTicks; + private float prevAttackingTicks; + + private int ticksActive; + private int prevMotionOffset; + + @Nullable + private LivingEntity target; + private final Comparator targetSorting = Comparator.comparing(this::distanceTo); + + public TentacleEntity(EntityType type, World world) { + super(type, world); + } + + public TentacleEntity(World world, BlockPos pos) { + super(UEntities.TENTACLE, world, pos); + } + + @Override + protected void initDataTracker() { + super.initDataTracker(); + dataTracker.startTracking(GROWTH, 0); + dataTracker.startTracking(MOTION_OFFSET, 0); + } + + public void attack(BlockPos pos) { + var offset = pos.toCenterPos().subtract(getBlockPos().toCenterPos()); + + double dX = offset.x; + double dY = offset.y; + double dZ = offset.z; + double radius = Math.sqrt(dX * dX + dZ * dZ); + + setPitch(MathHelper.wrapDegrees((float)(-(MathHelper.atan2(dY, radius) * MathHelper.DEGREES_PER_RADIAN)))); + setYaw(MathHelper.wrapDegrees((float)(MathHelper.atan2(dZ, dX) * MathHelper.DEGREES_PER_RADIAN) - 90)); + getWorld().sendEntityStatus(this, ATTACK_STATUS); + attackingTicks = 30; + } + + public float getAttackProgress(float tickDelta) { + return (30F - MathHelper.lerp(tickDelta, prevAttackingTicks, attackingTicks)) / 30F; + } + + public float getGrowth(float tickDelta) { + return MathHelper.lerp(tickDelta, prevGrowth, getGrowth()) / (float)MAX_GROWTH; + } + + public int getGrowth() { + return dataTracker.get(GROWTH); + } + + public void setGrowth(int growth) { + dataTracker.set(GROWTH, Math.max(0, growth)); + } + + public float getAnimationTimer(float tickDelta) { + return (age + tickDelta + (getUuid().getMostSignificantBits() % 100)) * 0.00043F + + MathHelper.lerp(tickDelta, prevMotionOffset, getMotionOffset()) * 0.002F; + } + + public int getMotionOffset() { + return dataTracker.get(MOTION_OFFSET); + } + + public void setMotionOffset(int motionOffset) { + dataTracker.set(MOTION_OFFSET, motionOffset); + } + + public boolean isAttacking() { + return attackingTicks > 0; + } + + @Override + public int getWidthPixels() { + return 9; + } + + @Override + public int getHeightPixels() { + return 9; + } + + @Override + public boolean damage(DamageSource source, float amount) { + if (source.getAttacker() instanceof PlayerEntity player) { + if (player.getStackInHand(Hand.MAIN_HAND).isIn(ItemTags.SHOVELS)) { + kill(); + ParticleUtils.spawnParticles(ParticleTypes.EFFECT, this, 10); + } + if (getWorld().random.nextInt(5) == 0 && canTarget(player)) { + setTarget(player); + } + } + ticksActive += 20; + playSound(SoundEvents.ENTITY_RAVAGER_ROAR, 5, 1); + return true; + } + + @Override + public void tick() { + prevMotionOffset = getMotionOffset(); + prevGrowth = getGrowth(); + super.tick(); + prevAttackingTicks = attackingTicks; + if (isAttacking()) { + if (--attackingTicks == 12) { + if (target != null) { + target.damage(getDamageSources().generic(), 15); + Vec3d diff = target.getPos().subtract(getPos()); + target.takeKnockback(1, diff.x, diff.z); + + ParticleUtils.spawnParticles(ParticleTypes.CLOUD, target, 10); + + for (Entity bystander : getWorld().getOtherEntities(target, target.getBoundingBox().expand(3))) { + if (bystander instanceof LivingEntity l) { + diff = l.getPos().subtract(getPos()); + l.takeKnockback(1, diff.x, diff.z); + ParticleUtils.spawnParticles(ParticleTypes.CLOUD, target, 10); + } + } + + target = null; + } + + + playSound(SoundEvents.BLOCK_POINTED_DRIPSTONE_LAND, 1, 1); + } + } + + ParticleUtils.spawnParticles(ParticleTypes.ASH, this, 4); + var sphere = new Sphere(false, 10).translate(getPos()); + ParticleUtils.spawnParticles(getWorld(), sphere, ParticleTypes.ASH, 4); + + if (!getWorld().isClient) { + int growth = getGrowth(); + + if (growth >= MAX_GROWTH / 2) { + if (age % 50 == 0) { + updateTarget(); + } + + if (target != null && !isAttacking()) { + attack(target.getBlockPos()); + } + } + + if (growth < MAX_GROWTH) { + setGrowth(growth + 1); + } + + if (getWorld().random.nextInt(110) == 0) { + playSound(SoundEvents.BLOCK_CONDUIT_AMBIENT_SHORT, 1, 0.3F); + } + + if (ticksActive > 0) { + ticksActive--; + setMotionOffset(getMotionOffset() + ticksActive); + } + } + } + + public void setTarget(LivingEntity target) { + this.target = target; + playSound(SoundEvents.ENTITY_RAVAGER_ROAR, 5, 1); + + if (target instanceof PlayerEntity player) { + Pony.of(player).getMagicalReserves().getEnergy().add(6); + } + } + + private void updateTarget() { + if (!canTarget(target)) { + target = null; + } + + if (target == null && !isAttacking()) { + getWorld().getEntitiesByClass(HostileEntity.class, getBoundingBox().expand(10, 3, 10), this::canTarget) + .stream() + .sorted(targetSorting) + .findFirst() + .ifPresent(this::setTarget); + } + } + + protected boolean canTarget(LivingEntity target) { + return target != null + && !target.isRemoved() + && !target.isSneaky() + && !(target instanceof PlayerEntity player && (player.isCreative() || player.isSpectator())) + && canSee(target); + } + + private boolean canSee(Entity entity) { + return entity.getWorld() == getWorld() + && distanceTo(entity) <= 128 + && getWorld().raycast(new RaycastContext(getPos(), entity.getPos(), RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, this)).getType() == HitResult.Type.MISS; + } + + @Override + public void handleStatus(byte status) { + switch (status) { + case ATTACK_STATUS: + attackingTicks = 30; + break; + default: + super.handleStatus(status); + } + } + + @Override + public void onBreak(Entity breaker) { + + } + + @Override + public void onPlace() { + + } + + @Override + public boolean canStayAttached() { + return getWorld().isTopSolid(getBlockPos().down(), this); + } + + @Override + public Box getVisibilityBoundingBox() { + if (visibilityBox == null) { + visibilityBox = getBoundingBox().expand(10, 0, 10).stretch(0, 10, 0); + } + return visibilityBox; + } + + @Override + protected void updateAttachmentPosition() { + visibilityBox = null; + Vec3d pos = attachmentPos.toCenterPos(); + setPos(pos.x, pos.y, pos.z); + setBoundingBox(Box.of(pos, 1, 1, 1).stretch(0, 2, 0)); + } + + @Override + public void move(MovementType movementType, Vec3d movement) { } + + @Override + public void addVelocity(double deltaX, double deltaY, double deltaZ) { } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java index 3e4b122b..40cab42d 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java @@ -67,6 +67,9 @@ public interface UEntities { EntityType LOOT_BUG = register("loot_bug", FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, LootBugEntity::new) .trackRangeChunks(8) .dimensions(EntityDimensions.fixed(0.8F, 0.6F))); + EntityType TENTACLE = register("tentacle", FabricEntityTypeBuilder.create(SpawnGroup.MISC, TentacleEntity::new) + .trackRangeChunks(8) + .dimensions(EntityDimensions.fixed(0.8F, 0.8F))); static EntityType register(String name, FabricEntityTypeBuilder builder) { EntityType type = builder.build(); diff --git a/src/main/resources/assets/unicopia/textures/entity/poison_joke/tentacle.png b/src/main/resources/assets/unicopia/textures/entity/poison_joke/tentacle.png new file mode 100644 index 0000000000000000000000000000000000000000..1026c1c0a49236c48e11c922a74a22597bec6819 GIT binary patch literal 19159 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]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 Date: Wed, 3 Jan 2024 21:51:24 +0100 Subject: [PATCH 08/98] Added curing joke and ignimeous bulbs --- assets/models/bulb_angry.png | Bin 0 -> 43302 bytes assets/models/bulb_idle.png | Bin 0 -> 43186 bytes assets/models/ignimious_bulb.bbmodel | 1 + assets/models/ignimious_bulb.java | 69 ++++++++ .../com/minelittlepony/unicopia/USounds.java | 11 +- .../ability/EarthPonyGrowAbility.java | 7 +- .../unicopia/block/CuringJokeBlock.java | 24 +++ .../unicopia/block/UBlocks.java | 4 +- .../unicopia/client/URenderers.java | 1 + .../entity/IgnimeousBulbEntityModel.java | 77 ++++++++ .../entity/IgnimeousBulbEntityRenderer.java | 39 ++++ .../render/entity/TentacleEntityModel.java | 11 +- .../render/entity/TentacleEntityRenderer.java | 4 +- .../entity/mob/IgnimeousBulbEntity.java | 167 ++++++++++++++++++ .../unicopia/entity/mob/TentacleEntity.java | 63 +++++-- .../unicopia/entity/mob/UEntities.java | 6 +- .../unicopia/item/CuringJokeItem.java | 119 +++++++++++++ .../unicopia/item/UFoodComponents.java | 2 + .../minelittlepony/unicopia/item/UItems.java | 1 + .../unicopia/blockstates/curing_joke.json | 5 + .../resources/assets/unicopia/lang/en_us.json | 3 + .../unicopia/models/block/curing_joke.json | 6 + .../unicopia/models/item/curing_joke.json | 6 + .../unicopia/textures/block/curing_joke.png | Bin 0 -> 5132 bytes .../entity/poison_joke/bulb_angry.png | Bin 0 -> 43302 bytes .../textures/entity/poison_joke/bulb_idle.png | Bin 0 -> 43186 bytes .../unicopia/textures/item/curing_joke.png | Bin 0 -> 4857 bytes 27 files changed, 604 insertions(+), 22 deletions(-) create mode 100644 assets/models/bulb_angry.png create mode 100644 assets/models/bulb_idle.png create mode 100644 assets/models/ignimious_bulb.bbmodel create mode 100644 assets/models/ignimious_bulb.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityModel.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityRenderer.java create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/mob/IgnimeousBulbEntity.java create mode 100644 src/main/java/com/minelittlepony/unicopia/item/CuringJokeItem.java create mode 100644 src/main/resources/assets/unicopia/blockstates/curing_joke.json create mode 100644 src/main/resources/assets/unicopia/models/block/curing_joke.json create mode 100644 src/main/resources/assets/unicopia/models/item/curing_joke.json create mode 100644 src/main/resources/assets/unicopia/textures/block/curing_joke.png create mode 100644 src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_angry.png create mode 100644 src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_idle.png create mode 100644 src/main/resources/assets/unicopia/textures/item/curing_joke.png diff --git a/assets/models/bulb_angry.png b/assets/models/bulb_angry.png new file mode 100644 index 0000000000000000000000000000000000000000..5f5cc603f03d8b2ee2eaa6aa1e49ae0830c93422 GIT binary patch literal 43302 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 literal 0 HcmV?d00001 diff --git a/assets/models/bulb_idle.png b/assets/models/bulb_idle.png new file mode 100644 index 0000000000000000000000000000000000000000..7fe663ff3245675c5f5c5a5a7b9afd3de490c734 GIT binary patch literal 43186 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 literal 0 HcmV?d00001 diff --git a/assets/models/ignimious_bulb.bbmodel b/assets/models/ignimious_bulb.bbmodel new file mode 100644 index 00000000..2c3b6593 --- /dev/null +++ b/assets/models/ignimious_bulb.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"modded_entity","box_uv":true},"name":"ignimious_bulb","model_identifier":"","modded_entity_version":"Fabric 1.17+","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":256,"height":256},"elements":[{"name":"jaw","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-23.956753782540364,-5.009498774448022,-24.13052619222006],"to":[24.043246217459636,17.990501225551977,23.86947380777994],"autouv":0,"color":8,"origin":[0.04324621745963608,-5.009498774448022,-0.13052619222006],"faces":{"north":{"uv":[48,48,96,71],"texture":0},"east":{"uv":[0,48,48,71],"texture":0},"south":{"uv":[144,48,192,71],"texture":0},"west":{"uv":[96,48,144,71],"texture":0},"up":{"uv":[96,48,48,0],"texture":0},"down":{"uv":[144,0,96,48],"texture":0}},"type":"cube","uuid":"800563f2-e637-d42d-6c38-616b6b34c9ca"},{"name":"head","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-22.956753782540364,10.990501225551977,-22.13052619222006],"to":[23.043246217459636,33.99050122555197,23.86947380777994],"autouv":0,"color":8,"rotation":[7.5,0,0],"origin":[0.04324621745963608,17.990501225551977,23.86947380777994],"uv_offset":[0,71],"faces":{"north":{"uv":[46,117,92,140],"texture":0},"east":{"uv":[0,117,46,140],"texture":0},"south":{"uv":[138,117,184,140],"texture":0},"west":{"uv":[92,117,138,140],"texture":0},"up":{"uv":[92,117,46,71],"texture":0},"down":{"uv":[138,71,92,117],"texture":0}},"type":"cube","uuid":"e6d05459-6b50-12b5-a46a-eb72b97f7c09"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-19,-9,-61],"to":[13,-9,-29],"autouv":0,"color":9,"rotation":[27.320650966819105,-50.33976442181192,-6.793186828559589],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"42f404db-2eb0-2efd-bb09-8810376fcfbc"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-16,-3,-60],"to":[16,-3,-28],"autouv":0,"color":9,"rotation":[16.681567243909033,33.56061965535128,3.4685333161320835],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"4c00dc9a-fef0-30b4-0f06-8bee9fe9b898"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-16,-6,-56],"to":[16,-6,-24],"autouv":0,"color":9,"rotation":[-162.2403464416216,58.02175378934316,-176.80418113816847],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"5db8a4ea-e690-f657-b64a-323ec0c3a305"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-24,-6,-62],"to":[8,-6,-30],"autouv":0,"color":9,"rotation":[-169.75684992659043,-35.13964450125722,-170.6241287065347],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"b2bbe86a-3379-0246-9181-127aed715432"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-16,-13,-58],"to":[16,-13,-26],"autouv":0,"color":9,"rotation":[144.36680378552091,-83.99459398899594,-115.75078983303227],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"c63affed-9e2a-84af-5766-631a13679757"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-16,-9,-59],"to":[16,-9,-27],"autouv":0,"color":9,"rotation":[28.459049469615778,-5.7440354970636935,0.03610759351412133],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"89bc35ae-6e61-dfa8-745f-0e317bb63260"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-12,-4,-52],"to":[20,-4,-20],"autouv":0,"color":9,"rotation":[40.560964930961916,83.77970397136126,27.74070945144022],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"0a5c37c6-ad13-697a-58cd-934841edb237"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-18,-15,-61],"to":[14,-15,-29],"autouv":0,"color":9,"rotation":[-149.33066739822777,4.299459188610997,-178.742402181358],"origin":[0,-3,-1],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"d8b4bbe0-9707-0652-35e6-e28a54179fa3"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-34,-10,-84],"to":[30,-10,-20],"autouv":0,"color":9,"rotation":[111.7261647239295,-85.5550612830785,-96.58324240444844],"origin":[0,-3,-1],"uv_offset":[-64,140],"faces":{"north":{"uv":[0,204,64,204],"texture":0},"east":{"uv":[-64,204,0,204],"texture":0},"south":{"uv":[128,204,192,204],"texture":0},"west":{"uv":[64,204,128,204],"texture":0},"up":{"uv":[64,204,0,140],"texture":0},"down":{"uv":[128,140,64,204],"texture":0}},"type":"cube","uuid":"4e7c3e97-535b-9261-83a4-80fb76cc13d4"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-34,-10,-84],"to":[30,-10,-20],"autouv":0,"color":9,"rotation":[62.10613287479732,85.7200584727182,46.23842221938251],"origin":[0,-3,-1],"uv_offset":[-64,140],"faces":{"north":{"uv":[0,204,64,204],"texture":0},"east":{"uv":[-64,204,0,204],"texture":0},"south":{"uv":[128,204,192,204],"texture":0},"west":{"uv":[64,204,128,204],"texture":0},"up":{"uv":[64,204,0,140],"texture":0},"down":{"uv":[128,140,64,204],"texture":0}},"type":"cube","uuid":"2184d35d-e78b-c493-b1c4-a817cb45fa8b"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-34,-10,-84],"to":[30,-10,-20],"autouv":0,"color":9,"rotation":[-164.484342402648,7.951483564968085,-175.61972440808503],"origin":[0,-3,-1],"uv_offset":[-64,140],"faces":{"north":{"uv":[0,204,64,204],"texture":0},"east":{"uv":[-64,204,0,204],"texture":0},"south":{"uv":[128,204,192,204],"texture":0},"west":{"uv":[64,204,128,204],"texture":0},"up":{"uv":[64,204,0,140],"texture":0},"down":{"uv":[128,140,64,204],"texture":0}},"type":"cube","uuid":"24212edf-8ab0-e1e5-b67e-dd27ca7fee4f"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-34,-6,-84],"to":[30,-6,-20],"autouv":0,"color":9,"rotation":[15.186954129789529,2.0049709481247944,-1.9011648291348386],"origin":[0,-3,-1],"uv_offset":[-64,140],"faces":{"north":{"uv":[0,204,64,204],"texture":0},"east":{"uv":[-64,204,0,204],"texture":0},"south":{"uv":[128,204,192,204],"texture":0},"west":{"uv":[64,204,128,204],"texture":0},"up":{"uv":[64,204,0,140],"texture":0},"down":{"uv":[128,140,64,204],"texture":0}},"type":"cube","uuid":"dfd15693-f566-9bc7-b953-d303dec3ba18"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-17.373117897112767,65.61517788582364,17.389978221486494],"to":[14.626882102887233,65.61517788582364,49.389978221486494],"autouv":0,"color":9,"rotation":[113.4590494696158,-5.744035497063647,0.03610759351412453],"origin":[-1.3731178971127687,38.61517788582365,12.389978221486501],"uv_offset":[112,0],"faces":{"north":{"uv":[144,32,176,32],"texture":0},"east":{"uv":[112,32,144,32],"texture":0},"south":{"uv":[208,32,240,32],"texture":0},"west":{"uv":[176,32,208,32],"texture":0},"up":{"uv":[176,32,144,0],"texture":0},"down":{"uv":[208,0,176,32],"texture":0}},"type":"cube","uuid":"e020ca0b-24eb-a3cc-43ed-e87b0013e04d"}],"outliner":[{"name":"head","origin":[0,17,24],"rotation":[7.492942810808386,-0.32621370972236063,2.478638942680466],"color":0,"uuid":"e54419b9-7a64-8b11-6ad9-cea74cb14f41","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["e6d05459-6b50-12b5-a46a-eb72b97f7c09","800563f2-e637-d42d-6c38-616b6b34c9ca","e020ca0b-24eb-a3cc-43ed-e87b0013e04d"]},{"name":"leaves","origin":[0,-3,-1],"color":0,"uuid":"7cf17cab-353d-fddc-814d-3b708cb33637","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["4c00dc9a-fef0-30b4-0f06-8bee9fe9b898","42f404db-2eb0-2efd-bb09-8810376fcfbc","b2bbe86a-3379-0246-9181-127aed715432","c63affed-9e2a-84af-5766-631a13679757","4e7c3e97-535b-9261-83a4-80fb76cc13d4","dfd15693-f566-9bc7-b953-d303dec3ba18","2184d35d-e78b-c493-b1c4-a817cb45fa8b","24212edf-8ab0-e1e5-b67e-dd27ca7fee4f","89bc35ae-6e61-dfa8-745f-0e317bb63260","0a5c37c6-ad13-697a-58cd-934841edb237","d8b4bbe0-9707-0652-35e6-e28a54179fa3","5db8a4ea-e690-f657-b64a-323ec0c3a305"]}],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/assets/models/bulb.png","name":"bulb.png","folder":"block","namespace":"","id":"0","particle":false,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"742fdfc6-ad6a-1d7e-e84d-c8133537cff8","relative_path":"../bulb.png","source":"data:image/png;base64,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"}],"fabricOptions":{"header":"package com.example.mod;","entity":"Entity","render":"","members":""}} \ No newline at end of file diff --git a/assets/models/ignimious_bulb.java b/assets/models/ignimious_bulb.java new file mode 100644 index 00000000..8eb9b255 --- /dev/null +++ b/assets/models/ignimious_bulb.java @@ -0,0 +1,69 @@ +// Made with Blockbench 4.8.3 +// Exported for Minecraft version 1.17+ for Yarn +// Paste this class into your mod and generate all required imports +public class ignimious_bulb extends EntityModel { + private final ModelPart head; + private final ModelPart cube_r1; + private final ModelPart head_r1; + private final ModelPart leaves; + private final ModelPart cube_r2; + private final ModelPart cube_r3; + private final ModelPart cube_r4; + private final ModelPart cube_r5; + private final ModelPart cube_r6; + private final ModelPart cube_r7; + private final ModelPart cube_r8; + private final ModelPart cube_r9; + private final ModelPart cube_r10; + private final ModelPart cube_r11; + private final ModelPart cube_r12; + private final ModelPart cube_r13; + public ignimious_bulb(ModelPart root) { + this.head = root.getChild("head"); + this.leaves = root.getChild("leaves"); + } + public static TexturedModelData getTexturedModelData() { + ModelData modelData = new ModelData(); + ModelPartData modelPartData = modelData.getRoot(); + ModelPartData head = modelPartData.addChild("head", ModelPartBuilder.create().uv(0, 0).cuboid(-24.0432F, -0.9905F, -48.1305F, 48.0F, 23.0F, 48.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 7.0F, 24.0F, -0.1308F, 0.0057F, 0.0433F)); + + ModelPartData cube_r1 = head.addChild("cube_r1", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, -27.0F, 5.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(1.3731F, -21.6152F, -11.61F, -1.9802F, 0.1003F, 0.0006F)); + + ModelPartData head_r1 = head.addChild("head_r1", ModelPartBuilder.create().uv(0, 71).cuboid(-23.0F, -16.0F, -46.0F, 46.0F, 23.0F, 46.0F, new Dilation(0.0F)), ModelTransform.of(-0.0432F, -0.9905F, -0.1305F, -0.1309F, 0.0F, 0.0F)); + + ModelPartData leaves = modelPartData.addChild("leaves", ModelPartBuilder.create(), ModelTransform.pivot(0.0F, 27.0F, -1.0F)); + + ModelPartData cube_r2 = leaves.addChild("cube_r2", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, 3.0F, -55.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.8316F, -1.0127F, -3.0858F)); + + ModelPartData cube_r3 = leaves.addChild("cube_r3", ModelPartBuilder.create().uv(112, 0).cuboid(-14.0F, 12.0F, -60.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.6063F, -0.075F, -3.1196F)); + + ModelPartData cube_r4 = leaves.addChild("cube_r4", ModelPartBuilder.create().uv(112, 0).cuboid(-20.0F, 1.0F, -51.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.7079F, -1.4622F, 0.4842F)); + + ModelPartData cube_r5 = leaves.addChild("cube_r5", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, 6.0F, -58.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.4967F, 0.1003F, 0.0006F)); + + ModelPartData cube_r6 = leaves.addChild("cube_r6", ModelPartBuilder.create().uv(-64, 140).cuboid(-30.0F, 7.0F, -83.0F, 64.0F, 0.0F, 64.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.8708F, -0.1388F, -3.0651F)); + + ModelPartData cube_r7 = leaves.addChild("cube_r7", ModelPartBuilder.create().uv(-64, 140).cuboid(-30.0F, 7.0F, -83.0F, 64.0F, 0.0F, 64.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -1.084F, -1.4961F, 0.807F)); + + ModelPartData cube_r8 = leaves.addChild("cube_r8", ModelPartBuilder.create().uv(-64, 140).cuboid(-30.0F, 3.0F, -83.0F, 64.0F, 0.0F, 64.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.2651F, -0.035F, -0.0332F)); + + ModelPartData cube_r9 = leaves.addChild("cube_r9", ModelPartBuilder.create().uv(-64, 140).cuboid(-30.0F, 7.0F, -83.0F, 64.0F, 0.0F, 64.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -1.95F, 1.4932F, -1.6857F)); + + ModelPartData cube_r10 = leaves.addChild("cube_r10", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, 10.0F, -57.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -2.5197F, 1.466F, -2.0202F)); + + ModelPartData cube_r11 = leaves.addChild("cube_r11", ModelPartBuilder.create().uv(112, 0).cuboid(-8.0F, 3.0F, -61.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.9628F, 0.6133F, -2.978F)); + + ModelPartData cube_r12 = leaves.addChild("cube_r12", ModelPartBuilder.create().uv(112, 0).cuboid(-13.0F, 6.0F, -60.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.4768F, 0.8786F, -0.1186F)); + + ModelPartData cube_r13 = leaves.addChild("cube_r13", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, 0.0F, -59.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.2911F, -0.5857F, 0.0605F)); + return TexturedModelData.of(modelData, 256, 256); + } + @Override + public void setAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + @Override + public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) { + head.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); + leaves.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/USounds.java b/src/main/java/com/minelittlepony/unicopia/USounds.java index 23980e16..3f53eca3 100644 --- a/src/main/java/com/minelittlepony/unicopia/USounds.java +++ b/src/main/java/com/minelittlepony/unicopia/USounds.java @@ -62,11 +62,18 @@ public interface USounds { SoundEvent ENTITY_SOMBRA_AMBIENT = register("entity.sombra.ambient"); SoundEvent ENTITY_SOMBRA_LAUGH = register("entity.sombra.laugh"); SoundEvent ENTITY_SOMBRA_SNICKER = register("entity.sombra.snicker"); - SoundEvent ENTITY_SOMBRA_SCARY = USounds.Vanilla.ENTITY_GHAST_AMBIENT; + SoundEvent ENTITY_SOMBRA_SCARY = ENTITY_GHAST_AMBIENT; SoundEvent ENTITY_CRYSTAL_SHARDS_AMBIENT = BLOCK_AMETHYST_BLOCK_HIT; SoundEvent ENTITY_CRYSTAL_SHARDS_JOSTLE = BLOCK_AMETHYST_BLOCK_BREAK; + SoundEvent ENTITY_IGNIMEOUS_BULB_HURT = ENTITY_WARDEN_HURT; + SoundEvent ENTITY_IGNIMEOUS_BULB_DEATH = ENTITY_WARDEN_DEATH; + + SoundEvent ENTITY_TENTACLE_ROAR = ENTITY_RAVAGER_ROAR; + SoundEvent ENTITY_TENTACLE_AMBIENT = BLOCK_CONDUIT_AMBIENT_SHORT; + SoundEvent ENTITY_TENTACLE_DIG = ENTITY_WARDEN_DIG; + SoundEvent ITEM_AMULET_CHARGING = register("item.amulet.charging"); SoundEvent ITEM_AMULET_RECHARGE = register("item.amulet.recharge"); @@ -90,6 +97,8 @@ public interface USounds { SoundEvent ITEM_STAFF_STRIKE = ENTITY_PLAYER_ATTACK_CRIT; SoundEvent ITEM_MAGIC_STAFF_CHARGE = ENTITY_GUARDIAN_ATTACK; + SoundEvent ITEM_CURING_JOKE_CURE = BLOCK_AMETHYST_BLOCK_BREAK; + SoundEvent ITEM_ROCK_LAND = BLOCK_STONE_HIT; RegistryEntry.Reference ITEM_MUFFIN_BOUNCE = BLOCK_NOTE_BLOCK_BANJO; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index 56b10bf7..6d982e43 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.util.TraceHelper; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; +import net.minecraft.block.FarmlandBlock; import net.minecraft.item.BoneMealItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; @@ -84,11 +85,11 @@ public class EarthPonyGrowAbility implements Ability { w.setBlockState(pos.down(), Blocks.DIRT.getDefaultState()); } w.setBlockState(pos, UBlocks.PLUNDER_VINE_BUD.getDefaultState()); - } else if (w.random.nextInt(5000) == 0) { + } else if (w.random.nextInt(5000) == 0 || w.getBlockState(pos).isOf(UBlocks.CURING_JOKE)) { if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) { - w.setBlockState(pos.down(), Blocks.DIRT.getDefaultState()); + FarmlandBlock.setToDirt(null, state, w, pos.down()); } - w.breakBlock(pos, true); + w.breakBlock(pos, false); TentacleEntity tentacle = new TentacleEntity(w, pos); tentacle.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); w.spawnEntity(tentacle); diff --git a/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java new file mode 100644 index 00000000..73243fd1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java @@ -0,0 +1,24 @@ +package com.minelittlepony.unicopia.block; + +import com.minelittlepony.unicopia.particle.MagicParticleEffect; + +import net.minecraft.block.BlockState; +import net.minecraft.block.FlowerBlock; +import net.minecraft.client.util.ParticleUtil; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.World; + +class CuringJokeBlock extends FlowerBlock { + public CuringJokeBlock(StatusEffect suspiciousStewEffect, int effectDuration, Settings settings) { + super(suspiciousStewEffect, effectDuration, settings); + } + + @Override + public void randomDisplayTick(BlockState state, World world, BlockPos pos, Random random) { + for (int i = 0; i < 3; i++) { + ParticleUtil.spawnParticle(world, pos, random, new MagicParticleEffect(0x3388EE)); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java index b74d0566..24ebc94d 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java @@ -21,6 +21,7 @@ import com.minelittlepony.unicopia.block.cloud.SoggyCloudBlock; import com.minelittlepony.unicopia.block.cloud.SoggyCloudSlabBlock; import com.minelittlepony.unicopia.block.cloud.SoggyCloudStairsBlock; import com.minelittlepony.unicopia.block.cloud.UnstableCloudBlock; +import com.minelittlepony.unicopia.entity.effect.UEffects; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.cloud.CloudBlockItem; import com.minelittlepony.unicopia.item.group.ItemGroupRegistry; @@ -138,6 +139,7 @@ public interface UBlocks { Block PLUNDER_VINE = register("plunder_vine", new ThornBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY), () -> UBlocks.PLUNDER_VINE_BUD)); Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY), PLUNDER_VINE.getDefaultState())); + Block CURING_JOKE = register("curing_joke", new CuringJokeBlock(UEffects.BUTTER_FINGERS, 7, AbstractBlock.Settings.create().mapColor(MapColor.PALE_PURPLE).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY))); Block CHITIN = register("chitin", new SnowyBlock(Settings.create().mapColor(MapColor.PALE_PURPLE).hardness(5).requiresTool().ticksRandomly().sounds(BlockSoundGroup.CORAL)), ItemGroups.NATURAL); Block SURFACE_CHITIN = register("surface_chitin", new GrowableBlock(Settings.copy(CHITIN), () -> CHITIN), ItemGroups.NATURAL); @@ -240,7 +242,7 @@ public interface UBlocks { StrippableBlockRegistry.register(PALM_LOG, STRIPPED_PALM_LOG); StrippableBlockRegistry.register(ZAP_WOOD, STRIPPED_ZAP_WOOD); StrippableBlockRegistry.register(PALM_WOOD, STRIPPED_PALM_WOOD); - Collections.addAll(TRANSLUCENT_BLOCKS, WEATHER_VANE, CHITIN_SPIKES, PLUNDER_VINE, PLUNDER_VINE_BUD, CLAM_SHELL, SCALLOP_SHELL, TURRET_SHELL); + Collections.addAll(TRANSLUCENT_BLOCKS, WEATHER_VANE, CHITIN_SPIKES, PLUNDER_VINE, PLUNDER_VINE_BUD, CLAM_SHELL, SCALLOP_SHELL, TURRET_SHELL, CURING_JOKE); TintedBlock.REGISTRY.add(PALM_LEAVES); FlammableBlockRegistry.getDefaultInstance().add(GREEN_APPLE_LEAVES, 30, 60); diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 93fc054a..6de73e74 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -98,6 +98,7 @@ public interface URenderers { EntityRendererRegistry.register(UEntities.FRIENDLY_CREEPER, FriendlyCreeperEntityRenderer::new); EntityRendererRegistry.register(UEntities.LOOT_BUG, LootBugEntityRenderer::new); EntityRendererRegistry.register(UEntities.TENTACLE, TentacleEntityRenderer::new); + EntityRendererRegistry.register(UEntities.IGNIMEOUS_BULB, IgnimeousBulbEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.FANCY_BED, CloudBedBlockEntityRenderer::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityModel.java new file mode 100644 index 00000000..9f5e6e7a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityModel.java @@ -0,0 +1,77 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import com.minelittlepony.unicopia.entity.mob.IgnimeousBulbEntity; + +import net.minecraft.client.model.Dilation; +import net.minecraft.client.model.ModelData; +import net.minecraft.client.model.ModelPart; +import net.minecraft.client.model.ModelPartBuilder; +import net.minecraft.client.model.ModelPartData; +import net.minecraft.client.model.ModelTransform; +import net.minecraft.client.model.TexturedModelData; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; + +public class IgnimeousBulbEntityModel extends EntityModel { + + private final ModelPart part; + + private final ModelPart head; + private final ModelPart leaves; + + public IgnimeousBulbEntityModel(ModelPart root) { + super(RenderLayer::getEntityTranslucent); + this.part = root; + head = root.getChild("head"); + leaves = root.getChild("leaves"); + } + + public static TexturedModelData getTexturedModelData() { + ModelData data = new ModelData(); + ModelPartData root = data.getRoot(); + ModelPartData head = root.addChild("head", ModelPartBuilder.create().uv(0, 0).cuboid(-24.0432F, -0.9905F, -48.1305F, 48, 23, 48, Dilation.NONE), ModelTransform.of(0, 7, 24, -0.1308F, 0.0057F, 0.0433F)); + head.addChild("jaw", ModelPartBuilder.create().uv(112, 0).cuboid(-16, -27, 5, 32, 0, 32, Dilation.NONE), ModelTransform.of(1.3731F, -21.6152F, -11.61F, -1.9802F, 0.1003F, 0.0006F)); + head.addChild("head", ModelPartBuilder.create().uv(0, 71).cuboid(-23, -16, -46, 46, 23, 46, Dilation.NONE), ModelTransform.of(-0.0432F, -0.9905F, -0.1305F, -0.1309F, 0, 0)); + + ModelPartData leaves = root.addChild("leaves", ModelPartBuilder.create(), ModelTransform.pivot(0, 27, -1)); + leaves.addChild("leaf_1", ModelPartBuilder.create().uv(112, 0).cuboid(-16, 3, -55, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(2.8316F, -1.0127F, -3.0858F)); + leaves.addChild("leaf_2", ModelPartBuilder.create().uv(112, 0).cuboid(-14, 12, -60, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(2.6063F, -0.075F, -3.1196F)); + leaves.addChild("leaf_3", ModelPartBuilder.create().uv(112, 0).cuboid(-20, 1, -51, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-0.7079F, -1.4622F, 0.4842F)); + leaves.addChild("leaf_4", ModelPartBuilder.create().uv(112, 0).cuboid(-16, 6, -58, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-0.4967F, 0.1003F, 0.0006F)); + leaves.addChild("leaf_5", ModelPartBuilder.create().uv(-64, 140).cuboid(-30, 7, -83, 64, 0, 64, Dilation.NONE), ModelTransform.rotation(2.8708F, -0.1388F, -3.0651F)); + leaves.addChild("leaf_6", ModelPartBuilder.create().uv(-64, 140).cuboid(-30, 7, -83, 64, 0, 64, Dilation.NONE), ModelTransform.rotation(-1.084F, -1.4961F, 0.807F)); + leaves.addChild("leaf_7", ModelPartBuilder.create().uv(-64, 140).cuboid(-30, 3, -83, 64, 0, 64, Dilation.NONE), ModelTransform.rotation(-0.2651F, -0.035F, -0.0332F)); + leaves.addChild("leaf_8", ModelPartBuilder.create().uv(-64, 140).cuboid(-30, 7, -83, 64, 0, 64, Dilation.NONE), ModelTransform.rotation(-1.95F, 1.4932F, -1.6857F)); + leaves.addChild("leaf_9", ModelPartBuilder.create().uv(112, 0).cuboid(-16, 10, -57, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-2.5197F, 1.466F, -2.0202F)); + leaves.addChild("leaf_10", ModelPartBuilder.create().uv(112, 0).cuboid(-8, 3, -61, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(2.9628F, 0.6133F, -2.978F)); + leaves.addChild("leaf_11", ModelPartBuilder.create().uv(112, 0).cuboid(-13, 6, -60, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-0.4768F, 0.8786F, -0.1186F)); + leaves.addChild("leaf_12", ModelPartBuilder.create().uv(112, 0).cuboid(-16, 0, -59, 32, 0, 32, Dilation.NONE), ModelTransform.rotation(-0.2911F, -0.5857F, 0.0605F)); + + return TexturedModelData.of(data, 256, 256); + } + + @Override + public void setAngles(IgnimeousBulbEntity entity, float limbSwing, float limbSwingAmount, float tickDelta, float yaw, float pitch) { + + float age = entity.age + tickDelta; + + head.yScale = 1 - MathHelper.sin(age * 0.05F) * 0.02F; + + float hScale = 1 + MathHelper.cos(age * 0.06F) * 0.02F; + head.xScale = hScale; + head.zScale = hScale; + + head.pitch = MathHelper.sin(age * 0.02F) * 0.02F; + head.yaw = MathHelper.cos(age * 0.02F) * 0.02F; + + leaves.yScale = 1 + MathHelper.sin(age * 0.05F) * 0.12F; + } + + @Override + public void render(MatrixStack matrices, VertexConsumer vertices, int light, int overlay, float red, float green, float blue, float alpha) { + part.render(matrices, vertices, light, overlay); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityRenderer.java new file mode 100644 index 00000000..8315cc12 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityRenderer.java @@ -0,0 +1,39 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.mob.IgnimeousBulbEntity; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; + +public class IgnimeousBulbEntityRenderer extends EntityRenderer { + private static final Identifier IDLE_TEXTURE = Unicopia.id("textures/entity/poison_joke/bulb_idle.png"); + private static final Identifier ANGRY_TEXTURE = Unicopia.id("textures/entity/poison_joke/bulb_angry.png"); + + private final IgnimeousBulbEntityModel model; + + public IgnimeousBulbEntityRenderer(EntityRendererFactory.Context context) { + super(context); + model = new IgnimeousBulbEntityModel(IgnimeousBulbEntityModel.getTexturedModelData().createModel()); + } + + @Override + public void render(IgnimeousBulbEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + matrices.push(); + matrices.scale(-1, -1, 1); + matrices.translate(0, -1.5F, 0); + + model.setAngles(entity, 0, 0, tickDelta, entity.getYaw(tickDelta), entity.getPitch(tickDelta)); + model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); + matrices.pop(); + super.render(entity, yaw, tickDelta, matrices, vertices, light); + } + + @Override + public Identifier getTexture(IgnimeousBulbEntity entity) { + return entity.isAngry() ? ANGRY_TEXTURE : IDLE_TEXTURE; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java index e365457a..05b3da1b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityModel.java @@ -97,6 +97,9 @@ public class TentacleEntityModel extends EntityModel { @Override public void setAngles(TentacleEntity entity, float limbSwing, float limbSwingAmount, float tickDelta, float yaw, float pitch) { + + boolean growing = entity.getGrowth(tickDelta) < 1; + float age = entity.age + tickDelta + (entity.getUuid().getMostSignificantBits() % 100); float idleWaveTimer = entity.getAnimationTimer(tickDelta); @@ -116,9 +119,11 @@ public class TentacleEntityModel extends EntityModel { bendIntentisty += 3F; bone.resetTransform(); - bone.pitch = MathHelper.lerp(attackProgress, idlePitch, bone.pitch + attackCurve); - bone.yaw = MathHelper.lerp(attackProgress, idleYaw, bone.yaw + sweepDirection * attackCurve); - bone.roll = MathHelper.lerp(attackProgress, idleRoll, bone.roll); + if (!growing) { + bone.pitch = MathHelper.lerp(attackProgress, idlePitch, bone.pitch + attackCurve); + bone.yaw = MathHelper.lerp(attackProgress, idleYaw, bone.yaw + sweepDirection * attackCurve); + bone.roll = MathHelper.lerp(attackProgress, idleRoll, bone.roll); + } attackCurve *= 1.04F; } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java index f6ba79b9..7aa694b5 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/TentacleEntityRenderer.java @@ -9,6 +9,7 @@ import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.EntityRendererFactory; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; public class TentacleEntityRenderer extends EntityRenderer { private static final Identifier TEXTURE = Unicopia.id("textures/entity/poison_joke/tentacle.png"); @@ -26,8 +27,9 @@ public class TentacleEntityRenderer extends EntityRenderer { matrices.scale(-1, -1, 1); float scale = entity.getGrowth(tickDelta); + matrices.translate(0, -0.75F + 3F * (1 - scale), 0); + scale = MathHelper.clamp(scale, 0.5F, 1); matrices.scale(scale, scale, scale); - matrices.translate(0, -0.9F, 0); model.setAngles(entity, 0, 0, tickDelta, entity.getYaw(tickDelta), entity.getPitch(tickDelta)); model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnimeousBulbEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnimeousBulbEntity.java new file mode 100644 index 00000000..286f0fad --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnimeousBulbEntity.java @@ -0,0 +1,167 @@ +package com.minelittlepony.unicopia.entity.mob; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.entity.player.Pony; + +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.MovementType; +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.MobEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class IgnimeousBulbEntity extends MobEntity { + private static final TrackedData ANGRY = DataTracker.registerData(IgnimeousBulbEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final List TENTACLE_OFFSETS = List.of( + new BlockPos(-3, 0, -3), new BlockPos(0, 0, -4), new BlockPos(3, 0, -3), + new BlockPos(-4, 0, 0), new BlockPos(4, 0, 0), + new BlockPos(-3, 0, 3), new BlockPos(0, 0, 4), new BlockPos(3, 0, 4) + ); + + @Nullable + private Map tentacles; + + public IgnimeousBulbEntity(EntityType type, World world) { + super(type, world); + } + + public IgnimeousBulbEntity(World world) { + super(UEntities.IGNIMEOUS_BULB, world); + } + + @Override + protected void initDataTracker() { + super.initDataTracker(); + dataTracker.startTracking(ANGRY, false); + } + + public boolean isAngry() { + return dataTracker.get(ANGRY); + } + + public void setAngry(boolean angry) { + dataTracker.set(ANGRY, angry); + } + + @Override + public void tick() { + if (!getWorld().isClient && !isRemoved()) { + var center = new BlockPos.Mutable(); + var tentacles = getTentacles(); + + TENTACLE_OFFSETS.forEach(offset -> { + tentacles.compute(adjustForTerrain(center, offset), this::updateTentacle); + }); + + if (getWorld().random.nextInt(isAngry() ? 12 : 1200) == 0) { + for (TentacleEntity tentacle : tentacles.values()) { + tentacle.addActiveTicks(120); + } + } + LivingEntity target = getAttacker(); + + setAngry(target != null); + + if (isAngry() && getWorld().random.nextInt(30) == 0) { + if (target instanceof PlayerEntity player) { + Pony.of(player).getMagicalReserves().getEnergy().add(6); + } + + tentacles.values() + .stream() + .sorted(Comparator.comparing(a -> a.distanceTo(target))) + .limit(2) + .forEach(tentacle -> { + tentacle.setTarget(target); + }); + } + } + + super.tick(); + } + + private Map getTentacles() { + if (tentacles == null) { + tentacles = getWorld().getEntitiesByClass(TentacleEntity.class, this.getBoundingBox().expand(5, 0, 5), EntityPredicates.VALID_ENTITY) + .stream() + .collect(Collectors.toMap(TentacleEntity::getBlockPos, Function.identity())); + } + return tentacles; + } + + private TentacleEntity updateTentacle(BlockPos pos, @Nullable TentacleEntity tentacle) { + if (tentacle == null || tentacle.isRemoved()) { + tentacle = new TentacleEntity(getWorld(), pos); + tentacle.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, getWorld().random.nextFloat() * 360, 0); + getWorld().spawnEntity(tentacle); + } + return tentacle; + } + + private BlockPos adjustForTerrain(BlockPos.Mutable mutable, BlockPos offset) { + World w = getWorld(); + mutable.set(getBlockPos()); + mutable.move(offset); + while (w.isAir(mutable.down()) && w.isInBuildLimit(mutable)) { + mutable.move(Direction.DOWN); + } + while (!w.isAir(mutable) && w.isInBuildLimit(mutable)) { + mutable.move(Direction.UP); + } + return mutable.toImmutable(); + } + + @Override + public void remove(RemovalReason reason) { + super.remove(reason); + getTentacles().values().forEach(tentacle -> tentacle.remove(reason)); + } + + @Override + public void move(MovementType movementType, Vec3d movement) { } + + @Override + public void addVelocity(double deltaX, double deltaY, double deltaZ) { } + + @Override + @Nullable + protected SoundEvent getHurtSound(DamageSource source) { + return USounds.ENTITY_IGNIMEOUS_BULB_HURT; + } + + @Override + @Nullable + protected SoundEvent getDeathSound() { + return USounds.ENTITY_IGNIMEOUS_BULB_DEATH; + } + + @Override + public void writeCustomDataToNbt(NbtCompound nbt) { + super.writeCustomDataToNbt(nbt); + nbt.putBoolean("angry", isAngry()); + } + + @Override + public void readCustomDataFromNbt(NbtCompound nbt) { + super.readCustomDataFromNbt(nbt); + setAngry(nbt.getBoolean("angry")); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java index cec6b0df..649a033b 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java @@ -4,6 +4,8 @@ import java.util.Comparator; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.util.shape.Sphere; @@ -19,9 +21,10 @@ import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.decoration.AbstractDecorationEntity; import net.minecraft.entity.mob.HostileEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleTypes; +import net.minecraft.registry.tag.BlockTags; import net.minecraft.registry.tag.ItemTags; -import net.minecraft.sound.SoundEvents; import net.minecraft.util.Hand; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.BlockPos; @@ -126,7 +129,7 @@ public class TentacleEntity extends AbstractDecorationEntity { @Override public boolean damage(DamageSource source, float amount) { if (source.getAttacker() instanceof PlayerEntity player) { - if (player.getStackInHand(Hand.MAIN_HAND).isIn(ItemTags.SHOVELS)) { + if (player.getStackInHand(Hand.MAIN_HAND).isIn(ItemTags.AXES)) { kill(); ParticleUtils.spawnParticles(ParticleTypes.EFFECT, this, 10); } @@ -134,15 +137,20 @@ public class TentacleEntity extends AbstractDecorationEntity { setTarget(player); } } - ticksActive += 20; - playSound(SoundEvents.ENTITY_RAVAGER_ROAR, 5, 1); + addActiveTicks(20 + getWorld().random.nextInt(30)); + playSound(USounds.ENTITY_TENTACLE_ROAR, 5, 1); return true; } + public void addActiveTicks(int ticks) { + ticksActive += ticks; + } + @Override public void tick() { prevMotionOffset = getMotionOffset(); - prevGrowth = getGrowth(); + int growth = getGrowth(); + prevGrowth = growth; super.tick(); prevAttackingTicks = attackingTicks; if (isAttacking()) { @@ -164,9 +172,6 @@ public class TentacleEntity extends AbstractDecorationEntity { target = null; } - - - playSound(SoundEvents.BLOCK_POINTED_DRIPSTONE_LAND, 1, 1); } } @@ -175,8 +180,6 @@ public class TentacleEntity extends AbstractDecorationEntity { ParticleUtils.spawnParticles(getWorld(), sphere, ParticleTypes.ASH, 4); if (!getWorld().isClient) { - int growth = getGrowth(); - if (growth >= MAX_GROWTH / 2) { if (age % 50 == 0) { updateTarget(); @@ -189,28 +192,40 @@ public class TentacleEntity extends AbstractDecorationEntity { if (growth < MAX_GROWTH) { setGrowth(growth + 1); + + if (growth == 0) { + playSound(USounds.ENTITY_TENTACLE_DIG, 1, 1); + } } if (getWorld().random.nextInt(110) == 0) { - playSound(SoundEvents.BLOCK_CONDUIT_AMBIENT_SHORT, 1, 0.3F); + playSound(USounds.ENTITY_TENTACLE_AMBIENT, 1, 0.3F); } if (ticksActive > 0) { ticksActive--; setMotionOffset(getMotionOffset() + ticksActive); } + } else { + if (growth < MAX_GROWTH && age % 15 == getWorld().random.nextInt(14)) { + getWorld().addBlockBreakParticles(getBlockPos().down(), getWorld().getBlockState(getBlockPos().down())); + } } } public void setTarget(LivingEntity target) { this.target = target; - playSound(SoundEvents.ENTITY_RAVAGER_ROAR, 5, 1); + playSound(USounds.ENTITY_TENTACLE_ROAR, 5, 1); if (target instanceof PlayerEntity player) { Pony.of(player).getMagicalReserves().getEnergy().add(6); } } + public LivingEntity getTarget() { + return target; + } + private void updateTarget() { if (!canTarget(target)) { target = null; @@ -260,6 +275,16 @@ public class TentacleEntity extends AbstractDecorationEntity { } + + @Override + public void remove(RemovalReason reason) { + super.remove(reason); + if (getWorld().isAir(getBlockPos()) && getWorld().getBlockState(getBlockPos().down()).isIn(BlockTags.DIRT)) { + getWorld().setBlockState(getBlockPos(), UBlocks.CURING_JOKE.getDefaultState()); + } + } + + @Override public boolean canStayAttached() { return getWorld().isTopSolid(getBlockPos().down(), this); @@ -286,4 +311,18 @@ public class TentacleEntity extends AbstractDecorationEntity { @Override public void addVelocity(double deltaX, double deltaY, double deltaZ) { } + + @Override + public void writeCustomDataToNbt(NbtCompound nbt) { + super.writeCustomDataToNbt(nbt); + nbt.putInt("growth", getGrowth()); + nbt.putInt("motion_offset", getMotionOffset()); + } + + @Override + public void readCustomDataFromNbt(NbtCompound nbt) { + super.readCustomDataFromNbt(nbt); + setGrowth(nbt.getInt("growth")); + setMotionOffset(nbt.getInt("motion_offset")); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java index 40cab42d..92a8dab8 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java @@ -67,9 +67,12 @@ public interface UEntities { EntityType LOOT_BUG = register("loot_bug", FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, LootBugEntity::new) .trackRangeChunks(8) .dimensions(EntityDimensions.fixed(0.8F, 0.6F))); - EntityType TENTACLE = register("tentacle", FabricEntityTypeBuilder.create(SpawnGroup.MISC, TentacleEntity::new) + EntityType TENTACLE = register("ignimeous_vine", FabricEntityTypeBuilder.create(SpawnGroup.MISC, TentacleEntity::new) .trackRangeChunks(8) .dimensions(EntityDimensions.fixed(0.8F, 0.8F))); + EntityType IGNIMEOUS_BULB = register("ignimeous_bulb", FabricEntityTypeBuilder.create(SpawnGroup.MISC, IgnimeousBulbEntity::new) + .trackRangeChunks(8) + .dimensions(EntityDimensions.fixed(3, 2))); static EntityType register(String name, FabricEntityTypeBuilder builder) { EntityType type = builder.build(); @@ -84,6 +87,7 @@ public interface UEntities { FabricDefaultAttributeRegistry.register(SOMBRA, SombraEntity.createMobAttributes()); FabricDefaultAttributeRegistry.register(FRIENDLY_CREEPER, FriendlyCreeperEntity.createCreeperAttributes()); FabricDefaultAttributeRegistry.register(LOOT_BUG, LootBugEntity.createSilverfishAttributes()); + FabricDefaultAttributeRegistry.register(IGNIMEOUS_BULB, IgnimeousBulbEntity.createMobAttributes()); if (!Unicopia.getConfig().disableButterflySpawning.get()) { final Predicate butterflySpawnable = BiomeSelectors.foundInOverworld() diff --git a/src/main/java/com/minelittlepony/unicopia/item/CuringJokeItem.java b/src/main/java/com/minelittlepony/unicopia/item/CuringJokeItem.java new file mode 100644 index 00000000..cbd12b9a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/CuringJokeItem.java @@ -0,0 +1,119 @@ +package com.minelittlepony.unicopia.item; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.particle.MagicParticleEffect; +import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.util.InventoryUtil; + +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.InventoryOwner; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public class CuringJokeItem extends BlockItem { + static final List> EFFECTS = List.of( + CuringJokeItem::restoreAir, + CuringJokeItem::restoreHunger, + CuringJokeItem::restoreHealth, + CuringJokeItem::removeEffect, + CuringJokeItem::uncurseItem, + CuringJokeItem::repairItem + ); + + public CuringJokeItem(Block block, Settings settings) { + super(block, settings); + } + + @Override + public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) { + var items = new ArrayList<>(EFFECTS); + while (!items.isEmpty()) { + if (items.remove(world.random.nextInt(items.size())).test(user)) { + ParticleUtils.spawnParticles(new MagicParticleEffect(0x3388EE), user, 25); + world.playSound(null, user.getBlockPos(), USounds.ITEM_CURING_JOKE_CURE, user.getSoundCategory(), 1, 1); + break; + } + } + + return super.finishUsing(stack, world, user); + } + + static boolean restoreAir(LivingEntity user) { + if (user.getAir() < user.getMaxAir()) { + user.setAir(user.getMaxAir()); + return true; + } + return false; + } + + static boolean restoreHunger(LivingEntity user) { + if (user instanceof PlayerEntity player && player.getHungerManager().getFoodLevel() < 20) { + player.getHungerManager().add(20, 0); + return true; + } + return false; + } + + static boolean restoreHealth(LivingEntity user) { + if (user.getHealth() < user.getMaxHealth()) { + user.setHealth(user.getMaxHealth()); + return true; + } + return false; + } + + static boolean removeEffect(LivingEntity user) { + return user.getStatusEffects().stream().filter(effect -> { + return !effect.getEffectType().isBeneficial(); + }).findAny().filter(effect -> { + user.removeStatusEffect(effect.getEffectType()); + return true; + }).isPresent(); + } + + static boolean uncurseItem(LivingEntity user) { + return getInventory(user) + .filter(s -> EnchantmentHelper.get(s).keySet().stream().anyMatch(Enchantment::isCursed)) + .findAny() + .filter(s -> { + var enchantments = EnchantmentHelper.get(s); + return enchantments.keySet().stream().filter(Enchantment::isCursed).findAny().filter(e -> { + enchantments.remove(e); + EnchantmentHelper.set(enchantments, s); + return true; + }).isPresent(); + }).isPresent(); + } + + static boolean repairItem(LivingEntity user) { + return getInventory(user) + .filter(s -> s.getDamage() < s.getMaxDamage()) + .findAny().filter(s -> { + s.setDamage(0); + return true; + }).isPresent(); + } + + static Stream getInventory(LivingEntity entity) { + if (entity instanceof PlayerEntity player) { + return InventoryUtil.stream(player.getInventory()); + } + + if (entity instanceof InventoryOwner owner) { + return InventoryUtil.stream(owner.getInventory()); + } + + return StreamSupport.stream(entity.getItemsEquipped().spliterator(), false); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java b/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java index 24dcf544..a7ff322a 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java @@ -53,6 +53,8 @@ public interface UFoodComponents { FoodComponent CANDY = builder(7, 0.9F).alwaysEdible().build(); FoodComponent SALT_CUBE = builder(0, 2.9F).alwaysEdible().build(); + FoodComponent POISON_JOKE = builder(0, 0F).alwaysEdible().snack().build(); + @Deprecated FoodComponent SHELL = builder(3, 5).build(); @Deprecated diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index e9863be6..40f9eccd 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -88,6 +88,7 @@ public interface UItems { Item ACORN = register("acorn", new Item(new Item.Settings().food(UFoodComponents.ACORN).maxCount(16)), ItemGroups.FOOD_AND_DRINK); Item MANGO = register("mango", new Item(new Item.Settings().food(UFoodComponents.MANGO)), ItemGroups.FOOD_AND_DRINK); Item BANANA = register("banana", new Item(new Item.Settings().food(UFoodComponents.BANANA)), ItemGroups.FOOD_AND_DRINK); + Item CURING_JOKE = register("curing_joke", new CuringJokeItem(UBlocks.CURING_JOKE, new Item.Settings().food(UFoodComponents.POISON_JOKE)), ItemGroups.NATURAL); Item PINEAPPLE = register("pineapple", new PineappleItem(new Item.Settings().food(UFoodComponents.BANANA).maxDamage(3)), ItemGroups.FOOD_AND_DRINK); Item PINEAPPLE_CROWN = register("pineapple_crown", new AliasedBlockItem(UBlocks.PINEAPPLE, new Item.Settings()), ItemGroups.NATURAL); diff --git a/src/main/resources/assets/unicopia/blockstates/curing_joke.json b/src/main/resources/assets/unicopia/blockstates/curing_joke.json new file mode 100644 index 00000000..20ea8e38 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/curing_joke.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "unicopia:block/curing_joke" } + } +} diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 41f63712..d2808a2d 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -246,6 +246,7 @@ "block.unicopia.palm_hanging_sign": "Palm Hanging Sign", "block.unicopia.apple_pie": "Apple Pie", "block.unicopia.weather_vane": "Weather Vane", + "block.unicopia.curing_joke": "Curing Joke", "block.unicopia.mango": "Mango", "block.unicopia.mango_leaves": "Mango Leaves", "block.unicopia.mango_sapling": "Mango Sapling", @@ -322,6 +323,8 @@ "entity.unicopia.sombra.taunt": "That's not going to work on me!", "entity.unicopia.storm_cloud": "Storm Cloud", "entity.unicopia.crystal_shards": "Crystal Shards", + "entity.unicopia.ignimeous_vine": "Ignimeous Vine", + "entity.unicopia.ignimeous_bulb": "Ignimeous Bulb", "player.reachDistance": "Reach Distance", "player.miningSpeed": "Mining Speed", diff --git a/src/main/resources/assets/unicopia/models/block/curing_joke.json b/src/main/resources/assets/unicopia/models/block/curing_joke.json new file mode 100644 index 00000000..d99c056b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/curing_joke.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cross", + "textures": { + "cross": "unicopia:block/curing_joke" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/curing_joke.json b/src/main/resources/assets/unicopia/models/item/curing_joke.json new file mode 100644 index 00000000..65c5811a --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/curing_joke.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/curing_joke" + } +} diff --git a/src/main/resources/assets/unicopia/textures/block/curing_joke.png b/src/main/resources/assets/unicopia/textures/block/curing_joke.png new file mode 100644 index 0000000000000000000000000000000000000000..1a0359ba187d6db5acf64525fc568becc00c7e0a GIT binary patch literal 5132 zcmeHLc~nzZ8h-(-h_#|?>g-;J63u zH^C;(39hvlj^$5y;7=cd1T>oZQD#fJl2GHAn$$sijG!0{XE0JpU<^SrI0e^NRJC{{ zBQ1{|>4g53iY8OK*<@0Byl%Ug3lPL#IlaP|dN94m8)L^OSG-x(&)S2q4&42IIlp;i zx51^8-kH#G@vQ=dHN0sqVp>$gJy!FLRx zKwbIqEsMEVlCS0UfBMPy%Z{vH_+ePef)3WI#Da!zX55}uomrB7d~`(Z%&(#k{}>ax zt6*lz(Y(EH_uH5k61H~ckeaI>pPYGi?C~5F^@iPdhDM)3EoVd3g|F`)Xwrd+5y!jV zE&8Tm{@y{IUpyaT%(-}eEFc*jP=zW#sUy{kj8 z5%mbtH9@r6B%5Un!@HeoR&aAb?RR?Qgdy?6{T`Oj0g{pf4$);)-P*lRr4$9DYNFPH zSv)2%RZK1Rg7L*^R=zlgHwdcX2@&yr1_C&N#47#HT$hjW8&v^Z299O1Mx_j>NI6E8 z4c3v#?FC9oO{p<7*)J9ls)Pt-yjQR@8A&OP5b(*Unkq>iMx!Y#EL0bgYPZ*+!3_q3 z1|u{CfkF+`SLBjdKkD)gkRgH?Nx;W@MUN!9T}l~~<=pv_QKf?O$|nDu9*dVvD|ZI+aGT(gBtfkSk90#=ahHky)XZy~FVZ}LnaoYsy&^Jd&O+D)+s zl%bZz!X&x*eAzv7l2IkE&j@Z_6qvwJOVI|-Kw>Dy2?9#7m=>kAb{w^9DV-L_w02sr zZv|y``6SlG0~rc}t3`;zQaYfg^%%;MK#x*-_{HJ?NA-5f4ps$_Fp zdnH2&5K7Opgr0!5yn$y?3eb9#Ht2LHO#`?FYsc(*DgY($Op4pA9?AhXzK~7}1sQZ<(-)PB30#Wj1j=Mv<67Z=ei%LPrrTN}0gx zgOw;_2F}jFjGPt*1_Oy@LIEz~l?M44hTi!g%c&HCko+|p_`b7aza7T>?dBLaPW3Cw3N=jv5 zQ!p$a48g}103qNf#0pOFQ(2b-z|*6#T$|+LFO&j73nYdU97@1HOq$}oulNY zKvq`9Kw(G8Y)S5TT@e};{nAy7;sf|Uk;~?!L@WJ8&7#pcQR8CD){;8}d&Y08vqLj0 zs^Y@4qpO996U9N>7d{Eq-<+H^?wxlh&+Rkui6Ze#eM*nbwsXm!rBqCrAGdzWqHj6J z?^b+yWoNeK%lCKQIk)=Y!-QMYro8m?+n*NHsGep?moqn;Bv9S0(=k;)$Brvk=XE+( zvaxJ)>VWkP7`{DD@nS8a9Jck7%k?`-wk)uyKRXguoV)PYojq2LF**9DPB;?0@AoIWd^SUkT-;26mH~6ip+533*XM< zkyVqnRE2j)?t93$%64Pm{a4o1bylUFcv`nwNqS%VldWWd_|K@H`kdT1Y-*`>)7qHS zu9xq2&=2kQd4wy!sH3vP`QX)?o2}@o2d4}ltx2e^@3XF$40|`_)nUIsT6B2v9FuC> z?5K_EUaQuJJXksEJeAjA$*Ad7@IylBwIz=xRem2?s7UXbrM~*u=;+DX`i>r3ao)H5 z!6kBK#_hY`?W7lfdQNe@_`s=E3x<9)wD%j^-6cOAiM@5^H&Ll`PRu^M^+tj)C8cSG?{lzqS-V8e8acG~A26*=xg!uvqfM+~EW3)>yrD3y+j-npEoF`uEy1PhQ*S leVi(o&Nw3#rQ7c8uIVq8*Kk)JB=38y?pJl| zRNe328Q6QT-M#wh?x$C;mg$L7QIbYMBt!%N04TCDATtzteS`Qn84>~t00q{F0AHA3`7a;p+5KHV8-n3iz1~zSzMsHuzZ%zO3K>l~)&7{{4af^CttAexv{6DJ`q0LdMR_#>&jW z4NlL>&dJZp#?Q(@#>TG7?0oT396fhd?;}!9YZ)~IVwk0nP>~o- zI6TX@Zwz3>F5m`MdSz!NBXA?ckKxRtw5a=-`+4f=-9p^)Vj>SZy{&3lgZURPBs zYC4KWp^fY(#S+ekEnYkWTrZ1dg_47){sEKxxKwTnNp*@()bXX6O84SJrm0uBmQ@Dk zlfIXD5iSp7>`T@t6v2{=Vj=J_nkgy~akQuWbhv76B)31&S_7gC5L@Mp_H;yH2+fwz zUf{YdZTCy4I}25a5B;38Kd9!};#qwq{{Fb^Oh_YoLC`X%|04G${h6|JaSkXg`pWI( zyfK#|wCw_i@5gSY?omX>M4k(`IenN_*VDr^Q-3g7x;)-paW=ITC58m2zBrhH~V5n)6@FMcq9 zy}6q)nU}qtgDbz65bzH!KUn@9%mO6)qvB>O1k?c=l7yp+IT^G*diKDxl5D*BCC;LbF?41-9{|WEl`Zo(;K3Ker zomkkISy}AuS^hnRtDB?;807B){U1}fYIr-Dv#6Q7I=Z`p#l&JLXSy z{=FdJi-)`OO?nt)`5U>|kpC8ww20Yz@ZY znRwaQewT*Tgvpr8)Pl)^hmYIXn46uC-NN_}l$j~Nl%tEiF}OLc?TszXS)3d!|3v&I zoL@{uRtU(>%=)ho6+2@$3vdDL#2Wv$Q!v1vaByAtC0xvn-5gys93Ab1fd6#6 z|BO}ycPBGrH)D{on>iTtpN?4LAC8#ew_pCN_`l&49nGvQy#K$ne|H`-L9jl!$z`lv z!TEdt3Hqx?shd0h_4e1Ro%NrtL`L?fQ}7#`{#6B6V-It)Kjj2t{S{?uW$a*S4)z{@ zo9jQqt^W_Ez{_gP#=&O6%EZdf#{Rc8aq@C9v9ogWv6^$6bMRW2{wKPtqlKHNv5UEw zC74GrS77t}GenWm{%I(>|J3%hGXLEQV8WPK*_c>4{y`Y`--NOJU165rKI0!X7G(K< zaU%Ff;oqDLINe`iVCMz)g)IMchJSPR+wJ^c{QTV({}*!rL;o+6|BB!L(e*#N{woIl zE9L)L*Z=7HuNe5Rl>cX4|G&|N_^;)ZxdV6>C7RF3MgSebz{+GB9Z*3IHLPbH6MK)*UrHVyzszhZx>xDA* zKC|m&nwmvYMa-Ja*R5ykiF@_PF;=&c^0f2s#jA+t3Xa6hM5>4 zYxru2YlA*d7p*ShIzgl_!tp&-{HQnuo*-l?4pITSH=$StvaArgSWZJ6w$olkrO4np zM5AEUO3~^4q*5W}4xDe`_C@a^KS8Ld6}+lgzGyH!A_!NXC*b@2@O_eYSN0K{I8?OZ zT_b7A^!HZ(quc;xb0rOd+`Fn=%uiGLF4suUDA-~yyXo3MEVKSqsn}rhNP^Kg)#Plf z`M9_o+F>)(Eu|BGI9Cm9x?WH|CiX!GhH5F|vQ%>fWU;_m@R*c(eqCfXCr4!-y{dX< z&IR#=%sLiR0z2(zOu=XXklF@CbA(X?g~Pm=qus#_Cq#{;6kknNb32g}TO-4jeO^_a zd&9cw%P*{csK&U&4`hWRpw7LkXMp|l`Uc8+-R&y7ZCGb9S z0LN47rdeK!w)$HvjYQ-IqHxyd2p8b`N|XC>T+5ec4hlyzKFR*V{5cuty4XGh(sV@C zsZk}eaGQRRs%X)LHx-=%PAX+VI&^^mHi-Z2G$+J7imgEN&qy27t*O!WJ8AaU)wjaM35K&ER-O<)cgL>Gjkg-%PUAAOib9R+req|3dO6fOK$u=j8 zQl7M_d|frGPs1P^Tuig+^+fYjw$zn0W;PY7|Bi3v{>sN<>$>VS@00jB9mG>q_6Wic z{Lw^hP)xcktlP@rTD*=?!Ctl*uT=;%eKatV`r}&MX!r(UL2^RgEyvd(?=^w|*5}07 zQFGxEc=Y^RtXfEQv(Jo&lOvfR>W~6Ts=>(6zN1wP1YmL{kxhh*^ol5sdVcdzIv)3$ z$|;C%b7#ukc|nlTp$$P3EIdbbgGD+&dGi9{wUiY5ca(|<75u1a zhT^8o40v&EOgS_VjQz0?-;&5;{X^RMUF)CQ>J7^aRNxDCwBszyLwGI#(zxF=&y7B( z(8~%9glYm`)K`|v>sK&$=wtEVp1MYvG&-u}HRY?B@79RZ1ACNgAy5!3BW*Auhd#KQ zcN|3k7r7Z)D1qB~3Z2Xc!^wsd@BMie9f}IU(QAWug(0@Z2QmKc$g;0QGPhL?5OD<39Lbx2v9(-b~;5irfS@i%dd(!t5bE zzrGS>u!7`3ONl_Bbj-UV;J^~|%Kl)6Q}PnaRuf6r>EYnrJ3+VoRT%_HnGsEx9waZJ z(MkeW9G{WbvkcV4i@Z)~W*3JWm)x zXSaAC{oa$^bmK|CR}qq(Hf+o5$y@InWWaZ8uZi521Tw=QD22cWo1PbLgwv0lHGHGq zCLvY_xa!bPG;$$B5oHP3B%#bi98i^H#5G38mP0Lb4IF}2isby#?~Yc{JxYO`WJGz` z@q6(pGuYH{dJ!_U&2Q+V2z8Fhq-@9B1xx4(X;V>DB^FBMhIr9&;+?|2SdjJ-roD7s z*>BwkrbGgy%*hH$?I(Uxm#>X)zpmE2*4!;izfc26@3&q}H*e0)#Xw#+W7?Pj`@gG? zME0#u-TdNp<38lIw`;eeyD?gP~Sl`x;b*!FN}#y&3nhfVV!Z z%DsS^W7{k`olI>n|LNfug@zK@Ofd+XYILe%q9ciNGWJ5WO7*Cr>B_R%^V=kWZZ`MM zyaX>yVc#%mU|JQ?uwP-LHXZd_{wQ_wSp0^jd>l$!0kXbv8z6)A;(1n#$paa^>Zxtx zGQUp$oQgamet?k;hG1|f?j*c>;K^^hTHK+j51$sl%L9E)6Ba$KLj zZq|H_3DmF5GAwc?&h2^b8y3Fg@m_21h6{4`OEP@8@BT$FeQAIGLgEvM(wGxTv|~Fo z^T6;la{DT;EV~VMN4;0S44-e?&MjITO|AhXBLE;CA4c9xF4A4 ze)Z~-VQ>2)wsV!Zm1}9N!Vo(i5;~P&z_N3N~=YbprVu}ewq&1%7hHs z&~h=jpkyb`Kb)h3VG5rSAUl#ff9%uZwOfmTlffy);ySXM(1RKg}r?>ODq>Te1}5V^|l9(x~nSwlYO z_1w87U*t;MjpBz9cwh5m^L(-OSYASTp#kx9cHjJ(`r6iJhg=PUAB4Qj&bpLR@Tt<= zs_wH_s6)~xtPYo(Wp!Y)YD&Bf&W~g*U)D@_4WZMtMcjfmfR5>NufjvxrM$2Z{RmY2 zV%CUD`!>29Wl0v(&N{4zDn|KT07+5Heyaak#@WfF3b&L&X-eZwz0_15WvSly;HGC1 zNVYC%2g>Ti!$ebZMnl{1JbVde9&V_m?g+L(gCUY^ZslZx$srw|o@`DAm0QS}kJ-+j z2FPZr$-^mp8Tc%8|9ikv+c;XY)!6TZk@-ypOTb;b*D%jhb7_v(X-rq<)gv(Y|EMeoDYsK^fg^KEb6IG6Ut8uA4z9DJ8z%Ol-Oyigf9BGUcZBFC3ogNWEdh6 zf2n1MmU<{HTK4iuritCJceSNB6ruejg!V@>?;xe^uSYF$w%L%+ zNpUzm8-(hZHAcMI?J8<^*po@*;(9x(*id!LuECL|+Y0(yOVNrv@w+-(uGI518u>`m zGEy7CGXBrLF^x^3=jCZBp3 zG^H~hA5)9k^PKXiXiNePT(!%(-1BG6QoZgVzRhhsk*Bv$wl%Nz6C_?A;{~Z}J@u82y^#Ofr!Q?g`~~QMVAc zLDyP0F=0;URtv#^BaqyWuprYMQ4r4=Ad4wADR-jZVd;7_G!QeWj0-1Bb%BGW(vWbM z04<8vEvjI0@YB%KtWFhD0*UIWHwe|Hifa;JFhb45t9x=dc%S&hP1oT9bfGkjAH<5$ zGrUj-2z70#tm;B56_ISxJy1#g=9^zaeRSu>5$6hc9bVA@k1s_PlK1|h-HuObXn>3A z+b+_DDPG~I6{s`3p`UA>w7;U3x_|m&un9jUzmj8U1LHqNZ666z0d6`I+32{GPA9J9 zDycLIfY5z~yhmMQD{`pHhmtiziQ*86P>2oof-;us!Prd>u_3nDn7y`HJ4M;z*0glD z`pPhwNpD5`S zWh!+JrxOg+M5$B7agji13tsAAE$M|kCO^^ncv%XLmA!_fuQ||Rgql~vw&?7k4sSSi zFNjEF+0c^+LOW|x-`P$F>D@s^Um%e_9(rCq@VtB=9;R7VHXOR*2?+n~Gqf*Wi(XUl zta>I!kI(%@y05PSV%Vv#O< z6FD_%2?ShpAler_++@QUgLI)#uu9uwBNr$bi!MhvPh(QYdNW(9CPmp!Kgqwy?+WX!;P4_dNPn8xSIG9w=m70{5DdF)mzO)Est^vl=4hELu@qyQ|T zoIok89vR8O$Va$Z&!SXSqHH8sxP38VIbHpk9n70zyP^{mk5GoO(T5oRG@wfGR4?nTA4+x`?;TcY_IkU`lP9u&dQ9kxx$!Z=i?kZJ6exA$ZBan_3^ zutoW%b(#ki$P8eq2*P9PC(i9V35&eA34HE0CeGcvy!(&~yrqL!-x94vTnAZA>SP^< z#)>w0E%QgHrll*5aYtZ(?m=LN*%_->#MLG#%21=F;bU|AKH0kHGpS@N{*Ki|oa%FQ zMxJWv3L+{KiQ7+@Fv*po6n%uHjF?ar$uKMgDkW&nBMqE>JY6_z(b`c|PuiN{JZre% z4W$$sU~nAJWaTOGD1SQu(%rgg7(|f^CavD1fjd0LW?QR;n zs0>e(7~1BF!|jW+@Ekydq}eMS_)v4zcz(xn_mkB7!^z{vB)}YVdIuQ;zIYX;wic}#5P-eFeq7KBdJz_iQjKzHF zc=BDS-mB{iHAUvxf5u6tWFD;wBeOE@SkX!iqEveWOu&ydIO(V!!-y&<#in2}3gVqf z56Jhk228lI49feIqB-xgNAM0t}`n@8Sok&f*7 z)d}tW(xaHN-D&|52GmW5opm?2v}!Pk+nr*k@soD!ua@+vH(xkC|()LN%$4X?h!B9^V{P5y`><8QQy1xHWTY> zgY_UIObB6~v21Kpn-FY*H?yJdMhDL2g!>f4V3K5uW;zbldVsJPq0-95c%P((@-)${ zX`}ZG)dd$J0{W?r@@2Z!v>JYNv{n5mFqrYhWLr796rGPlEV8f2$*Ng#sby zXuC;HB$uye)*Fix23|65-)D7?fyc7$4m4O&`0kFUhu_Zc@ksenH&+<&fdlmgjULK@hEV&<**%VAOndT^=7`YeH@V)(R;jq z{!Se|tRtJs#jC+D`*T<1l4LX+M9dQUYp{N_g&yAKK zgHo4?DUgBiarwD);Nh{^6wlG~LJ1;x39;^&hz~kc`!x&GF=~+iyxw}-7ejGtf)_{> zC!0sjiA^^Qi7Cy|5{E<)9UG+PPXW$!I#UD`bdlsEY=SwY+1GT>2G8V_qR`4+mM!lvFf}4WNspS^-(&Gj5&g;@;b{ z(`0G%fZ9f>$s(TR#?O%YzS|u+vOhKHMwG&K-6a|hZ-dvttA648N0AFWVG1%sN*EbE z5su-KyDw-RZv#Z8Uk72qQ@{%kzWA+Tdaqla=Z)$65tiw|XF8W$%${dz^?fN;|7|rZ zMRAV~gbXJK*802@Y_WM&vtI{lX4J#k#M;B4WsMOPJ-VZ*MQBYi2|U%|H)zYUQ=Cbo zi}@K&M5C@~lyTWHYM0fJzvj4D0`c7M%*G;sCEUA z0I5=du^;0x1MZ2=P5#au>~C&CdKAHzK&a?cqfF+X*b7)0;*L2h1km)FCB4)5g6~Z%gJa2{ zR6T%=YTFPJ9-=?mSKji&J~&kTNL~a!vCnSD-GR~@u$UJ1v1xDvZ*ciMl7B`pe<3nb z6Qwkf-qsyo>S4g`2t`eHzP?eMhqj(A+_1VKMSpx zo~R>WirWIekn^l7k|HKh!$kdet^PAW<-yp zf}Hbg3t4itV5H~3=>dEQKWz^F5h1PeNrNl85wWq|$!gmwbZ5MujO{Gi5?gB4s*>CC zAXpo?$_o8~K?}vkUx*{B1$cXVSK#wtP|^s;y!3%%sN_Xw`dWh2FIQ0bmFwX~QW@hR z1t5IuZLZ$bL_wV}#-UG5kd?i}slSsH^vX*Gb8Kj+ap)UzN4wZ zt*^9bE{NcxZ^P+%L%KYNImy^?vUo)$mlphJZ{b~s6x#43oUASdb*jSc^W;ZM@Ui96 zD3G$`9cw~r@18v?{gcuOscjdHEE)pzTp_uyr+!@V^`;()?dibMdY53zOEd!X1K_Yr z0)Rqz6HTX9$1!S*A!wHMZz^Z>ko&coW}W_HPhf4N2G`+eiMd{oyBs*Xz8@R_~}V&iY>JBDIg2AseLqp zEW3@>G$z$sqZ~&dOFLSk`bA zqV*b~jT>?Kp{sD{TXafNYnbRjSU(zjS`z?!?ku`2clYTK%|GYq?)4@vaGRyoFz7)- z)&KV-XwT<tL!g< z?T0&E!a>W-d@V5OKCY9ZXEPdyrEp}cG()K95sy8zkBBW0Vl&&If(0_NjPfCC)Cg%P z<#SdhJF3ER-WJvl2a5kRd1D!y9@{~-5arEC0@?(mU**lQjdJyHe2&U1NI?egBYT=Y z?D}+0lI2(k9LbNEHd2Uz0P#oGKVj*$W)8%!|m2LnDXU0h8^J=`VR07Elf72zjDgF~Xne_Ku%_U4^PJ8$LX- z15W5X-pKPVp4EXzK>^K1#(}fd7n0CTp7JjfyuUz+W|U>}KWquSsLc#>LIACKABv(m z&FkaS#hJQ-zP|B1Pu@t&_Fi~+Mxr4bTjD1FSWx$9c96%@ul}(^PaV8_(YoD_sIML0 zQS1L{(ddu;d=S9*<5x!5uK)CIsI^%#9fNm8?Xim8t?uFE@xpEZvt>l?n^StYUx+!w>IZ>jL&6Te+ZZ`T~m${OTt$KB1$KRNj@*^;i6y~?713}oI?8|CaFjDfSbAgempmJfChbI0nS^V8E+aPwG5^{GA`nkQC1XvViBsvC-MgqJag88R1LItM z^Ul*#*ALVV%zF4 zJlM1n)F}wkXJ;|uJ9*9V3<~icd@%^uK^DVW+!RToH^qWE*H~ZlaR?!Bn=fi- zvpOS071VsU{%+Y!p2iPmQ8!hYRFbk%1>J<-?DDDF&6c2IPc#3s6*J-){AYWjwR@Zf z?s!Z+EX;men9zmbJJ2uDRRle#;IgsnzNUeK^kN;H!UX9g9Jdu-T~jL;(2QG2{`f+3 zbiMs}fqt_b~Y8D?>`?^+1o1!YGb^=+#=A|fLZ+I20 zzLo9`r7sAuZS)fdpX2}QECA7BO7?graSO+DimNY~8T)|?$ESHiA7C3Ae_Us0$k-(g zYv@`>p6Bg$`M?BtVa)T^04|6Y=%C@iV%#^fs%5nlPINfb|AMG1I~D3JjUt=_w)wPA z*}Y{9ViJR1gM@f-R5AY2Bf10v6~xo*b7&36eG?y@u0~7O>h6-_w5E{X9=0QL5;uid zfS7xxY6sUoBHlcMwhcNjR65|8c;Y4K)H7Ll@;k%wkx;X_aRE_WLF?{@9wsh~>gdT_ zl_?-=ndzce!|{Gm)yiD!(=Z!-+0Y{Njz%o~t#gGgf7L)uYk!P*{u&b(p!r4ZDY1K= zRw*jzA?c;d7rL0mcjnUQAg9VSJ4S)GnTTb-qY!={1wJ% z8H2a>gZI80Fv8W@v{~GW6^cv$W*w3>gKmIPE>)=tS8S(P-kDeKs7-ZOuKta+5(|F5 z*&7+8$hbKZtsVQ(kTO`z3LHKYOad>GFc$5NP7&6P^9osgi)`!LFefP&^dudKx}8>l zjK=I*#<6q0nQlr2SAl4wlVKk<2W;O^Bmjft!FJ+aW@RdtN+z=oTE2e!>&IOcFA1BK ziVDP^8UR5}faCqoeP5nBl{EON*3}=z^XMt_#a`HY;o&YBQyTkXi|<5q9a^}v}dL`9g~smd1hkEuH`3;<;U@G`e!L87PX9pSsky|;BX ztmgRugzqDdD70d_wJ~#$)%%Q9hRePsC936uXyzk?)hLM5O70i*b*Ocy4jip_5UX@p zBxuqPCaC-@8lKP`%PLB1L+OLE&}%kyKMvVnX4+zsv9bF$V@;$bS&z&HpBHf}b1`t) zXZ?flOI{?w-#R~TPd|$`qAM19G@XJ66L+m|Y}QRRm|fWQr_8<%2{8&>E^1nNvbuR? z@N)GisS)o@ZIm&tQvjQ*SgKYCXnHC`>0*&CYE%%`(JJfg2}YxOxU@0ceoO%UOCw-| z+7t>moe6GnA9JggTLGDrME@&6f?NYHf#~8LCo*G~tiZMV(j0dmPVV)0>%%hkP2FXclC~ zruX^7UV`WtZ-&#mHSWq@e7H`(*2_rekr3z-rtPWpX#xT-n2bh`$4e(i6jT{8MHPhD zh{G=vN)0Od{oQOK9iLx+I27%(rJK&I5m1~HhRR9hn?$1CJVPYH#Fr}+#$E@p$G0%` zCkmLVXj-3wchO)ahSi~ed&d^YQp)n#I$ptAJzz%uVB%;ijarDjDW0*?Nb%kqD5s<# z8)5>%Xv}6aHMbdJlUoN$)2Pq1ahez!=Csdbm7li2!yU=D%Yg6UP6{*ds=p%Mz5dh=H ze`Y6f9jpALB9=3*1BhYV@=?H1iJi+!%oH-^KoBR6?j%Q=M6lrP9Q|!5_4cRn%I~Z2 z+_5cBui2GW(T~$neNa2LjTtV_S}0bR6&qgD6m%}(CLt~Y<((5Df^z3uigUXndbqCq?8 zd)@V1`7&^=U~8$N6~(L>i7ZH}9d07K^v2Deur4eei^KRpl%ckX8_JA=z7)G@lTOyh zmXrU}IBAdDB%=khnFMt-7>VfIu{sQ5M%xREFpF~fYTmT$|Ns)zj+ z;VxO}`m0fVLNCcsTx^K`_GCWxFlB^cd4_)u!E zK^?h6!U<5qUlq~$4J1ALbHF$cN6M?g_6|G4GBy@Bt%&tDyhn-Q<~o)gs*)9@W6 zwhHGOek&)G0*{aF-xiXL!bp^X!hS~IRiP}@5gF3Df(YPZ#46JpNDrGUtOI|`Z3I8? z_PpE1lq0`T9fQ-O0HNrOvGs#I{bJJF6m`Tj@hR~+t9LQsI=xPZjjC;hO+))H^V+@j z>BKGAB;jAlDiZMXpceMGIfRz?g86F3S0xs?^S^vCqcDdPp_nfXa~b{cVmjoSJ2Wh${9G5SLVN2oJSW;_RpDk!J&2s}{leBF0{Bu_Lk zHoq-(kz1R4k{_1fbp#CUe!e=XY~d`_fhOjXLbh;MNbU3hA6$8ZGWZqW8HQ`7$?djI z#dohmh?Rq{kz>i*2oZc>D3Z*U4QjZh_SUkHajX9qGuAvR1cs?Gsa+>EIy?2G(nMPY zmL~g?)cKnaI@DAJGB_AWj*0UtH`?IIBGS`{;X`J(DhFn6bGz|ZdU?sPz+btqs09F~ zuKTsks9zDvX>UE~fKnp8WpvF5bYopSKGj)O-(B`+L-es9tNp%B?yXdphO7W!8Pi!aqKs1|ulw1X{onSg5SU>9{XNpeH!I9CYrkKo|Yeh** z6V-Nbohw%Jm-UFDEhWP=BP^DRs*iC(zJP77!8LUuWIJ)0+DJJqlrg#*-b;Jn&yR(Rv=V#6Jg*du!T6j4 z>?us|t*7y&gi8OB zFZU$Zp{dV>vx&S`;ld1CTqT|>VH7!3jVob- z*Xp)3I(b`TX)r-2;S()%8y7xi7PG>n(CbD|_PhUA}sRDmb{ohs(mD&}np7^=h#2Oto}!C z(FtkpQzT%@LQ)6H@aeE|Q?M;yiK1N0^is&t4gs%Mfmb!G9@(}BRr##LiN|M1HBA)e zem&IK4W8Wh&`41!>He@NS1O1KbWsE@$Zi!vq}2`gg66k>PMO26KdQ=*&8(AZsv zh)LOWlbTYCP!R&1-w2FGBjYDi71(*#aWvq?Mv2_lyrQ&XA8cZ1Hc1qg)0{J=5hT-0 z?NLu?SngVwQjG1Z(zUu;<&Z-W>NX@%&J|*nW2%ek_0K6RZ6~g!Ef0-hLXE5CD|159 zwX8SF_e9r~=^v1cSi|{G%PMK8rgcHsXAkteyUh#644s}Ya>Zm%c8f^xcDKv6$BGKh z+Ld=%VGZjusrXVt$4A(S&@RY|tj~=4k(c8gU*`F@&-kwA#H*ihp_(*k#g8JG$z;pk z(pk8%nM51hPPO)6Y84vV7d>-xn;Tg=I^)FBNSG{!lQ84&j?9Ue3oDhzTJc^|esby; zkBwSx&Mu0KU}8+HFh1x|5>y~fg4=1x=C}<V|(u6YlTGr+=j%L&}Xh5z7% zw2m7OO3t9!r+eJT5gW)!<6<8iilUa;cc2Ig3OnR!X?X#CX7<#P~_5N((em`Wi!KpU3)Kyf60} z$HR33O@Dk!YDGV?zeNRH-+fwEj4k)B1oxLq1tL85dfCCmGly$dO#KqgWqaZiA6AR* zm2YXrtS*e3N;XCc5E!4X3FAuIJ+0*>q>Rfy z68BR{kj(BTnx7mNh8d`2b$VsamOE)-TCb}%I%~CxX*$=WZeI#5o&Ni-ws&*v$l@~LiQIt>g~aN``hSSgRNym2GOT9&IHq;)k9&Z;z)QPg27>| zj#I5BiUS8!9L`}_kN~#C_RfL+`qx}z5O+U;tB4UHfr+TSXhft;;T;9UXjiVJm!KQ#3R6lvK$-;U)5UK)15A}_MD)I6;txGarD-688L2xe_^ zc?8WZq(6|@!hOmP7+H8~4SdBdde@$ibp+G+hHuS3wfi%t{)X4!$<)o-yB|KqOA82I zC<~xk%jR%}#cyfpYmLLY_c>ooNRBZsLe^$Z+F4pZ317OrV$kej;dL(`pKooJ!av=` zv81w^^pq@#NL5`fo<0A1uLTvqmfQZN-4dcEMUU(DXO zMEz~GPkiC2s)ysl`qqXjv$?G;Ei{#+=5g>yxbWYuUV{^fbm3J?W7vm+i(pLLYJ!6m z>gA#aDF#i#9Av`AACO4Uwzf4NBWqGc3Y$XPF=}?@dw)S&RA7`tUq){g>eOz+)74qg zgr{btbND-eFmr4_*>z7@@^k|&Z~fPk-XdWX>W!bEZ)jME(~!!?0ef5_A9ot3lCZwExNg<>kfvzT2cUgixwJ~NJ${E z)PmiE>(L*->t|G+d$BOwJv}(dpu?{R85>+cWRoM~eM}|S0gvX~A9}iG@XlYMacJcL zg6HZ}N=)vCc1#_u?RPSQ0n=d|3hE^$WO8&)OjA%MhWO6xnZ!Fqk#Ar8dRER5u3QxJ zF#2lymZiEh>8lN3xO(WPi*v8st^^oQ7c%jFW^F=DVDUXZ@$3)OO@!?Uw61U3!53fp zBv$71oVK1g!0E~N8lWls%5+HGbrDPIyT4;L<=?3_Aj4dI`{<70#q}KBtlTZz%*Bm( z7%A^_u9e|>pa`7`ZNcCeU@uea9`e&CnG>VB1Zgwy8jdqV(2k?H(Km4^{B%j{s#ws> zVQCo29(HmFD$WY1Sp%IfoF`{7+)mDnyXhgHx?Ujh`r z_mDV{ce%p55%qXaO~;K~78VD7XdRCK!s;nVd;Ei!=dHBhrqK3}yX*waMe>25j70Sa zYGkUpinYXF(5g8h*ipy6j3f?(Kyu1r580>>hsNiZNA4B$Y;*0QbH^&ChD-BF>vcOQ?>>c0v#k}fZ4BmaygmXce) zINROIUlC6jS+9UX~y` zzSS0738=-*T2p+9qv*+!-`qTeIdR8BmyJyMh40!9e^TAH!5qEz{iMAWZ{MkXBs7po z#;Mebe_hr5vU0rJ8 z3X?Q$>)ZVE>!>fy>qynO0xg?r6&4kyOhTSJd11>~w&Ss`i>ap8rg#ez!c^jNoMnRt z1ltT_!e^s}$l23r(^#vTO{yQzvuiJ3-$Gpy83HP`sojkt0CaI=zs|h&7Ovvn-b^kj zgju&*7}ntL%G8hfe(zS66>xwvaBc5eTUh)Q967hdaT5>#{{0Cpjl%f;@gNeVtAGOw ze=`|~tkV9|fcvmvSz7?|#bXPz^gAVR->cuUAfDz`U_l`7R9)XYe>=ryCbxWP^2#w0 z`_}vJmieXood3<%fzLxWp0>z7qlm=_O-Hbmzwt`9Fh++r@x`Eh^JEtR&3$(3=?cp6 zE^B|t>+!`9G)-jXE??l=*0I3NtMC0hoVW^&~6vDbN%V@}~U?sVA;uRc=`u*hjPIst~HZfiO zlI`iSW&A~{&G3~nv2In7E`9FDNxCh%3$c~SS99gD(Ta;UFL9@eAH;AZ#U^-CAvPcM-d6XHG zdyATG29IGYMH08T{@l_u=CF)uX2gFcD~G4q#5WWaxj?OF;e4)%?NHNdSB1{{MNqDgP;F1KebX9?;;XOPS^PU4u^>j56l-F zFAZ@8bmHtn-}duVzP2d&3%beVQ;}~A*Zw39o31Co@Koe|%V($CB!Mh?;6z)HH;A~G zVmDwHNfa@-8b{y0cqTp!687;vlNgbW^01Q0$8^qC;e(8zDc%+}?tUqr86q1sws|Cl z%3>3ejr&$7HY6b~dxqg#|0!*fSMubOQRDBM&lT>!PSc%`(a0Gm6wCO@$=OoGc|Uzw z=`#-OZL(`^-K<|ZVGY+FHbfWS+o0Ln8ys|X*__ia!EU}i<2AO{`f&O~1GhqOT@d7J zX~;6-Y$VVH!x>^X+f`ysgP7tb-iWA|;Z8l)vvOl!eUt0#y>hn=KKq$PQ{0je)WBDy zSQF{IUCcd1PJtswvcke5i)~{#Vjgp&lw;Xu76DyF!Z&$7=R;L1S)i7bM?WzRaVjp$ zp#pEb%PcXO=xgIlWrBvhpRJqvm5|M>0iiYQmp>c#{u^eNS2Pyc$nPLx_W z-YTD*{5zbg0*TQDp&Y9}R_bP?xD745q=x2AgDlCJkdT3U$^vGXWt9flmT;fLELz85us2+>6j9Qc02tM2T;Gnj-G^`x zcXt7$km@4>OIb=FV_JrjtW=VJEZBW)18ObkR}U1ceUmEBlPnYL?feeh&f3NM5Mt>i zE3zaYss9JyKp(#;EPY->E{%)h$2>a!kjuj_P+e$!VO|UsCw%nYFcj)S%%>j5H58(b z#9U~{Bf$cnU;mP9r;OtRCcWa5i|;XT#&-J^US596&2mA>nJ$I>d`+#2sxdE}OlKQ* zv%8`)Y$izl(6G?ykRVv5;u(Lj9-5K7YKo~ z@wbRYD5)^BV045=>lHXshEud32`HtzzkkjSn2JPbg*z4BX5w z*)Pxdqy-~k+ELAD>qHSkcYG+u zfW*M3`|t5&^G#TF9QQ{630Uof9HALRND`=Z@i=QUDxG6{#%cKm&5pFi9L}pA5+Pm4 zG19dnS}>J0;dW*yfy40<2^%C8KHvWh4>uoR^#CC-NC6FKu0XB?%ty>BO(Xt+>sP9E$ae~*xXsTA(eq?S$KT%E=Wa9cTv(OYDpG0l5oJVRn-nJXef&_bY+Lt*Yl)=Xz`33G>O zX%s0GPQkjHf}q&y8Q3WdlBg_1bMAbMk&Ef)h(z8y|92VKFzdvN`IFm01!l_Gb|4sN ztK=M4$KuYR2`8fNEvP~&Tju4!Fl_mJ|3kN7kEh(USI+E1P`wpMDFos1_S=XFpI-kJ zC+Vx8jTqh1EUuHB7y_u$gHH|8it1;H1T%8T1WRsPu927$u~#fQ&}$)Lz0(9j@6-^; zL2rSmHL>a5(=fDk6xXVoM)YQ=-J-@Bk;3KlD;{jVMX*Mz3jw2RV$qr2K~M)&EYOz& z+Gn5NYiFLFwDbJ%eLmm+4Uh6y>AkUE_C!1wqjF>jTSOO>K$ektKd~L&f(}g*Cf@>TRu4tc}Hv-YJOEtxr|CVK8O^MUJ|j~&0M?T!{3#6>e=0W5*3le)#FzKYRS{spV#w%1B1 znP8D*Tk7$E-?Hq(4@M;R_*3j%#U5_3db zA*MDu`L_2op4hxILxouA7Ap$`EL_LV`@hXV_9a%?kSkp#Ts)#wb2j=ogtp`s(;F9 zd`K#%SU0*wN{GzMOvsU75G7L8fwy3V%7R&-NMY86F$^rati)YFl)6TEH}zEBsz*|c zv|hPhp7FJ_Kj!81L#&;XLqJi8iY=~wn`?9NjTyc7l_V7SwEm23+VLoSi_5EzZ>fo1 z#tm4b%bWR#%vlNESwsnI`ySl3Tmo44GrJm*0*kl%yE1UyF0k%4<+gM*tQ@`Se=Gpj zg@>DO@T;po<7xhoW4}SIx%g*DSaTcmrZX(=HDLoRlTq@md8uMET*;mw6B07D2`MBd z+j~bLevGm8@lI_9BFDKg@s4nQB&<6$*3*!9w-dCC^fYMef?RtA-)lDi!cHFcV><=%|puBbZ0f%;hwSG5` z533&nz|xurf>x&|Z#R!nFMX!A)(sHVQs6;KoZs#mM|ktXgFO~&A2*z&RO|@m+d1?!~5LS3tw14=nax&gh-0-mrIfoQjE`>+bHA{sg! zlF;U(m$a4D*xN!*ky<-LNbK#%CTyuysr^VE3bi$w`3TS!2NY}T1dEtBL)7NrI)9sK zRuF`auDOI=wgRPL&D;l}vmhz5EEAIa?A=-XS=F_5M$zV+NHsVtuPCeGEjbMD^^&F&)6%eeD&YUKC!m?{(q!ZbvpdEeW++OKMDGho;wV>~%U8HQJa=tbs~7<(*#j_iL5M^VmxOlcCR|NF<=xG9 ziFqK(mgk4Rb)crI9)=2(gYtlSlswavQw|7M*e z0c-7zg3<5h?j7|2(p3_3JIh|h&urt{V8XNM7eHkbTsDjF=i z!jH0S!fd}Plp5}a?Zu}1yfYbPt%l-+4Vci8@9~JcOMl|2k zy15Z$y#}>aHmo!%5)#2Gk%Wp7HK1ZND%L6w#&2<&A91<7ggzl5kU}7ut!lzmr6x1g zGD*X03cuMZAo`-(&Krb^T4$5aSo*<%rZ+mAMcgM}?XV)xm0l~m@EF*vM_K!|&gj=R zeEao}_J8w{zw`Mm35MH`w>PB-Vtwsw+8X_JTaxAVA6$!1u@guWyZo5z`S}+#2aodC zIM(YsC83EiGp{psLGi#7LUQ#ryfGqM)tGXtpKy7$PX_EY-~RQhj-jLgQIzHOZn>?J zYq1b>3_(l~g;)I}9^|j1)8|+mz5lT0#}W1Mow5)luy4vm{}u1Yf1jm(Lc>|6O@s3` zT62AY8DGF4}MMg%4Lji&t;h2V?rsToT@ zkmHsh{uGyfSjSumwZ?V-9Dwum6;$6ehur*snwDVNy)#gI1IYaKE=<;QY*?o1f^aIDbsx05=kb(-w-q>W=hR9wbN#B%Tzu5w=jzQ~e-j5iort`KP z0v^P#P}|-m+Pz`|8ideY#%$}c@r|AW5KhZ`T-RUn)-VuKxqC1Bib}9fGJIO)g(t^> z$}6gQs`I{AMj2Os!G5<{nXSc2g%SfQikhy*C-*#ne`{ZXfA-z~%=Zuto{-z?I>t0= zJJLv}V{wK(Y7Rjg(wq6E`b%_i$Eytc~XoOQFl zau_mdi|Ye$R$@1Ayl;2Q9%iPwGGt*K9x>NF7GVrKmUiQYrQ0)Tou$oWB1=H7aD+g11ggCihKSniywqUUn8Hp*-wIj09WV;cQA7ygNw7JvewY7F<28kqdX>N#} zu95%s$N%O1YWYp<0KTkO)UeL0`)YWRoOf;uCGF@a@8oY%TX$W#`9l50tA9;N8&o>U z0#8qW8`I8oJBK!L#~ST-?mH&ob*&77s5qb&(;Xmo>qrzOuSb)JS|ySl02TM}Z#r|j zdjMb&0$z#MM(yDhGCXE;df*Es|C^issVyQ>QVWc(ZCEQ>41>j3{X@F#)Az)(b z*huLTU4dr8(mN7^j|QcaQsk|ZZ+O6`!n4Cq_~qrlCXjjm;UA!)?2j+8{(8KJ3VlVz zd7@b-4jExjT|mR07&9~o40IE^ zR`(<9VNLDXAgD`y;{d&}%tyL-yjhY)DknsnNok~MMM`5{l+c}T7?Uuj2dG|=!j6y= zDxJcXF>Yy1S^6cX`7L@-axz+&?^nuw0r+xV!5rxY&}QV2=om?i+C0iS$BbHaci6Dz z0n!nL5F+oKew)@iL6o1r{BvR&d9wY0W)ne%b>dM;nh4S8+AxKj6SndiBE`WZ@iVd# zAh?pP>2CSY0Pu-wTe$JZSBS{nqTwTfwg zHKwYtq0=}luZZku&6w&QZ8O_)&Y|u(E(bO?V(N5Tkw{}^UM933eW&*vCFfCeC8a_N z8-~1d@k}&Y-IH@BGGHc*VI(UYrz3SzTHjN9r_M(0*LTLFvA7domk#%;x%&>_%XS^x z9_g9T7q;=lk=|~=;5mT`kxY|4%TgJ_04q5x+76?WBojs4-6uHp9N*@`ALR~7UjFecI z`T^^SSQ2yHQ}XDYOdTK=jLIgTQTvRwNDjEjsCUwR4ETKq@MXJ(F!+v+P!%wvs!N$w zoi?g{VUsr$8SmVLZ$0mGO2ib_v75UdVQ!QC?&)`UXY(z7@!~Jp#0Q-1-a%0EkXJo< zLUP0`Vxmr@5d~3Fh=dr4(Fboa209Zd7_}@cdSsIyU@YWNAR0?wd<rZd+XG$ z(2b>4CcUQXOwO78bVT|<2_vmu5wjl?2H6opB$vQu7?|c&K+u(8I44IpY)u-mHaXmo z6WGKs3=}eBNt9tjNdsn)P1zthBLcO}lpM*~?Y2!S+hJs`2EmJ0%EiS)M4Cpbb*6S? zC_9>&r|R5a_XXg~bVZ3YYpb_?BuiQCr30dFDry=a1d_P#oT)nqi{5HiA~_G_oWO(> z+|sHwW8W`09p2*I^WWufUi>d~tBi3=Zxto^NDbEA-nH*3W&(-EftiI!Mh)uXw3FTr zgc3+>39_a3g#|^|FqGrGciw?0Mkfd7;txY+VNYgsFtZ18PAta*-2@~t&j$|6OO#4P zD7|1N?7}(Pgx-x9g=TK!HWUFCFQ9#42q%bivP|SuNHM_bgJ}208TS~#m+d+o-|`Wh zx@g8E5~Ly&=H;4b1xcQT*V;nLg`5V&8gr{i@2Kl$$nFB_tT8h#jz42*6YoF#E=uGp z=ilX{XMf4J-~JCU7HU_b2qpz^&Uu!^b$dk~3RFP5CoUOi4xGg^THQ0vdj@t;D?`jA zQMv?{)*&kcffydq+YQq^xw38EV>F*ksFGr&iy&b`w>_=7UtdZCNN1_n#Ii+~#$tt% zg{94m`NV@cs&SZScH`*eDJr!eiJF-WQtiYn^hHQf(Pg?{Dfb<~m+Sh;^FL!>U$S2= zXiNlAx=c6h+BJ*C744B?cs@h!%DMqwbz&UgCI$-yPWwYw22z9-tZs!zsJrgl#z`<_%ICH}eH1kY%8Z zBRP%q-d&bFHwYbTk+~^-o}mZguwxTO*U=BB?u4vRt)jiJw$_E1cQompy|VF~%^q20E?9DFEb!DEW#Y;1Ll&LMc|>}m_sXv>{))5hTRc7e4n4R;IZL8y zXOPTcdF8IS<&0mw{8xPQ{GVb;+0Pqt&P+D3)j}lG6KkT+1A0E=lZ)^1^x^MuGhbmr zn5S!o@r0$hS;@Q{C?SJIpc022U5|vkVaNmXTqzW)9SN<_qY;96I&z(fz*u&4+`3!V zsI6_tG$Pd|MuZ^ho{ws@E-cze;Xurpc~Rmmohi&jg{d}Dyq^Nx7l1F>^}(Y*Ks$sG zNU``xEjU}RHi%KlVO+(EM%9TN2A4cCmt1NZ{VPKk% zL^34^Eq1I2HrXxNle&L{cBMo}ZupH<_g_rugn6E+y)u*&I+c|2{Ytqn0AH@lOo$<@ zA=p9;E|XD}Hs&+yt?B|&fjn*q!lt{wJNDU;}z-rJs$O$1s zt(h^pW}@m1qwF9A#&pKb>}PySkwq)xu)*fe&3r+Y#2gOyE9D*o_;OuZ@EG%<(gk$o z+I#zYn_dI0I!`~BbAJ5dPdVRwg{2-j9Ud^26H3|=BqIU38lC2L*X!a}Q*umTa8qyC z4rdH$2i;|{ros>h_ot^b#!O-$<64BKm3e^@wv_A%MyKN`LSk+m2%L=P=pavZ2;Fv@4>Flmglo z=4Ewwjsr=JAw?GUbPNfJ&9I}$eFt#g0es1>WjY{bq%KBIo@0LuiGfI$gmstq#t6MP zKEM0{k9J>Q{qmH#PHt$a&LIdjlCp=K^yb?9E(fCQJQX)6O=r%_TTFJuQpYS1tkW>G zH;N3*dT^w@2_>FjagUhM`VoseB)~i=!%#qHDg)gbEiP_Iic!iyvr6q1I)q-SdV``g zI=OpdP-VgvVHgX&X3!arBuOl-Q(|H)1!G{DXAsCaLyIg{sCGc8l(O?g-4}o_ z)iw78=|CUu+;ZK?*oYyJQYPh*k8i%ulimAkBG2k@Yf9@2pWpll)M#p}*B&%Fs@^UO z-50zz_H|E;BM03>PzXNzkCLE;mHHdVp`fj;j<$}r4>AzKj@TnGAS9X;w0mCOpa?HND-!Y)2# zse4bRZ5|BKaog#)&%Z%L*rc6*Rf1>UsG)tqEy61%jit>XjUjJX>crd^`s&!ztz*(L zZNzBwu3$o?QTqa{MwcW)FoK4an!I=f+B}o;DXN{RO)S%axh~AsKw-#R)QplNX)K5+ zArz(tF%2v%lpzsQp~xwlRFH14bn+;qZFX#-DZ98sOvqBGHWNZ7$I+{&7{dU=5D39A zYmC{a5s~43rQ8>QFWD4e^_Ghg=q&WrbvDvb>x}7~AHDdWczX67#2`es`3~aj0Be!6 z?K^yW_$ixw?l#z~<+q4Y)d4)yO4lR3H=4{yFuHnsEC#h7={8ffxihj<6i>?;Lcy%L z#8#bfM4TyQ>!P9}^jyi&efoCe19s(<)9oo@3y_%V!cc_P8liPp=TXRIAjilk8Lc4F z2vM2)#1OV@#x2Ji-~Cf?=b{dXCSn|Ua`KSlvS-K}{xA04t=FtF4pKS3Cyn*-vNQlpX;1z**=K%@H5?=rel0_R@wi{PXk{b2j?A}fsyQ*~8hYXsn<+MT^&bwlfMzO<@XFn`;VbPJ98qI2h$$jVg)D>; zdp6*FL=>jwhBPD!!e$tI)`2*-Obp|e=$Tq72cK|r205dIOH>-NG+esXSRS^FGSFHh zk3H3Qsy8$>HtiluTL>|-l!Y7a{(NG4rd-AMw`tce|smDKRL4h>}nON}@U3-+!G?p8c4s@*Kq3 z#62!XYyGq}FFganwMR@fh&puZ>(DL2+8T0ST3MyRO0)jrN^PhrwUkcU5g{bUJ+R#+ zhPXk*SsKi9#g;~tf#`|3oY0gQIAcEy%=MC8x?n03CXO>O414NSxttDE>B+lunQ-qh z-6{pAG7dY2Fmx0o>=|?<#DJ=>*b&Hm`cNyit=FgkyUjgf+}!Sz+XC>KT>)OU{N88u zEkE z#$q!u0JVTElqL*e57??`48Q`_*=@FLLPQO`7Q%2w(hX9b8E3P(&vaU7Yx1yAnU)EP zGtWZOKuB#do0EWm{~FTTTHzW6gz z3O&8Yd)E-dfO>wl`2~2m`v7pBT>pZlEL=unO6=yLwR`&; zJv|u*Q6Yp#GsCPBU&ZJdIb=Mr24gP7G2*f?q(t+|TxNWM&2Yxl8e>wD4kS%zPLyRP zg)?w4etarD3zYM7d0@|XKd3sNP{BiYWJm*`rJK5a@-N( zux0=z-o5iTc>CgeJU@NHSBH-|mKW4E(OlT)dyL_nxnA~p{@NFq0%|?!W|hp_Y9(&I z^ejJJ!$?-+&bgid*bEz5n^|nZmqgMn%iPF}4DpO*I+CaymR=}MWybx)xV=NI6GI-E zPd7A!&6qjNr|wQ%3qiKjX@*d`&9_9RdgSiz4kkvU&UG^24(3d-U)WVV#)n)BhD z{q7tILe=?I&cW@v1p!|l5Kxb(B(w#*K@3X8X|>U`k*&v+rMzVf10#_WGtwZcQI?sr zI8wtxtut546AtACB9S-F-s8#jKPO1w(Zx47F8wqd`)q%O(AR!WbM;e{ljs$I}XWBAh!5G6GCcEm!9%4q4UI%UR?nQdWNdcbPT19+s>6T9J@!+c3=#t^oY zb|Ro6VJq#d=E^=Vj-gRe$R zfwEi^;syKh9(lOOMI2YwoUSK#<1l~0v0QU8-lb{4OI@=Cgcy77;2US(>glx}d3^I@ zW;<~?eF_9p7zy%{g~&QS%gFPFd-v^gdYreaKr4KB|KH^4^aVL3+(REjGLoXvmVyk9 zm~g0muW;0~Myhn8*X~3#ci!hZVIW~5~KxvJQG@2wf+l;WG zg-Q$q%Q7*BgfJpf7^5&%BkKl@{pK&CEUDb?l-mOEnt>NqnEeKkfJSF>Lu_Uowj7oh zoTqzKpZMVZzr{}<|8HzBey?ZrsS`cpTDaJ~%X0XLsVzL%ywA0Ng-Y$B9CxY}h7ef{ zZsy0FjgPp$dmqdQYuIKt&Fq|kq&>8>Cj<2izsaAmO&zp5)R`ycGul!iLKa~hJ6Xs| zH~*ArrdcDl5rWam373FaLy{0BBOb8kOjGHQWU164;Xv~fo6X2v3vG#L>J}m5&N4T~ zn6OCC63heJI5O8jR6}GS*2>fh!?2+&4Ja(-76iO40I%5!ApwzQ6J8@j8rcmGXnsWU zjxVo10u;abQ5OP5Bh)}1VVRGpC)|Yl!`nQ)`6briJm2A_UiK(yRfaHj4*uyYl*|YB z|A_13)6Vu2?IId6RD{~BTZ^MqHTORRlN z&<@AHeepY(38FogOKOj_Zc`;lw*WUcYDgHusa-OLJy;-$V&20ug9_D8ym9tTqDRaM zCPoynpakpUqM%BpAq1jywxOz_22CnGOB1ZHFz3fz?DUx2B-C1>iMXPp^N5h^`~C9rrfx5PeI`J&mV3=@>(Z zxCcy}W{tFlgESz}U4_o_1+~qb4R29=LDm<>F`P~Ba(UJhf!MIkXIMRuWkZpPn>KYv zq}z2{0A8o{=GpIXb9ja!Tx=iGmL86|)h&={2-~osa1kDzeUBuL zx*#bqx5TMjVXaZEA>AVkw@Pi5A!KGzqAImjM1-IbtB^z>0L|CPa3=z@8MRfSMvMvq zMvoKoT6@T-PF#$4`b41J^o&CmG>)W{DbvI_T(FcADJQ0-P(31ESjwp<{p2^WbfU~X zv3MBw6mJYNP^owusCMFPdyms;fuZ5PK}~Q@h$}DX>^q(F=R$~?W(6}L=R_&qRRjXn8?nZQweG^ZCfpbW5y52uD}A&Tlw-T%SYN;lF=ozpZxez-4ZYR(g`k0IPL3mo z`5B*H{&|n+3L{@U{}`ws%2LV-`YGxT22~yN?nCIU0fH(Wh?D?yYcYvirg=i*4dM}@ zkV2v@4a=)knM!L5)nLpSY``hxc!otGwvpLx$T2|()a5xToDsVTI+ZK(aE@DIo{u0I zfI)J%J}b^RWTyE@iUUCd#TsF`Vz)i##o-BwgsU;6#B8@9;J>cx|F>EIZ33ceF5nqv z6){0W#N8-9cOXzICXGfvg@Y#CyXWsxCyHNls#h#NakIQ&DmUCrUoz%9eC^)v^62~< z9P4v(99HQNUA<>c$Nd{2IM4_o+gZE<|9PBHpyydM}k$tlOKn0&Zxn zw7L+iQ5rOvAta8=Gv?C~tJCUmWX#hGqV8DgHHXs?2%w$0=UNztNUJ?N;c9uxB0}*4 z)&viQD2cM%a50|a%{d&7q!fF;UKt!CYKFRU#$B1jChQo7z>1#kWS zUYCMf!_6q|NW(CjkXC!3h*T&}H3MdfPh3x*5#xqke!$o6{vn?}|I=;)>IL`8%Khtc z;~o$Jvjz7)J20GDW~ffBb5HdZ;c$9EBjQ$Zi4+IC;a-_)fnro5M(<3C^;WRHyU|6ei*tjYsQKfEU+~Q3-^&<^J{qzPSF$T5AFpyc>vmqe`W<+W7`RsVBspP6{f- zF!U~fLL3HKIgoTftM2Xt z97d`)PIkj_nMq-brY)+OxgH21lG)?lRu2Q?SQbnRWib|=nA$`wCsNMDlo;|ItlmPQnYGpSTI8i#7ciR5RMT$#tqBna?|~dxK~zy~9dPrA+1yWb6lmNW zAERN1hRDPHxA^?>r@X!YHmx{O0*lpd3+6_(r9ZgPyN_O9ooX{dA|^te7fb}A(CUJU zQKrUjcNg=SWmzCcY;mr6iZX&2dCY8lU|KF&mILE<=*&YK7zYTRnU|TIH(2R_Aa!JJ zS1iC-x}Yee8^*k)IhZ$eB`+;DQBU1rIEU^gEU6)0@pc;zep>)ux8)79o?+jttVKaz zZ9ub%hJZMTcEV1yhMS-vtS&+UOc1v`9^X_8ScXOtt&y z7h6`xA;G+Y2UHwmLA)2G*$z-5cyQ)26QoZLf*8#dw6M*YX`UG3z`QI3 z+0bmpSfDE7X3xAFI~y=~7yq;hGQl#k8TOo(nZ>TTlP;JAj`OKwDBif5UyyP}SC4~3 zxng9G$3|=IcBkAHfY+=6z?m0q5TK1NNH>yg^C>7<_*H=a~1+{^t32z$#0t zR6BK?&=402H-vaWl7UjE9yM+X?zB}vDa>rb8MVyh?460|m8BI@%2XDzM%;UB89LLd0TpjXv4tUT zsmlR(VN8M3GWD<17}|(fBTD1AoJcX#R)^%J&i%jZOiFvQCQ8Z7c0;V7e(MNyTL50O z_2ly7F74IKyBEKYARtDm3#C?$%O!YalkXEHfy3h$Kcjg?L&B_4ZQ|kC2OU}W3L@R% z*My)@XwWLWyfOgShc9`w{RW>Me!|7@CZ#!uA!|gpAYE!JFS~(-CL>{i5Pb~`0SO6j zM=S(FP!#33T%$QMPlX~gyLgUjp_G|H_x&0X6E-iPjxdmifoZxV5b#gw*}z!T5sL^KBA3M zW^U{W-ZCLHD#j*ej?0W_cj2jYfzN(;gH68ZAfN<91D`zqr|VVW!*v88sCL)iZUm~d zS`qCrUM|Ab{1nW&Jbli7|K`dMa>Tq_fY+W8h{E;stWPWeorUw^Avs5A5JE;J(WnF< zq)06%ay~-{m>AMPHM7)fid0e!pdGlY%*av@Q9MLSDdf%AiAb{T#jrInqv{03aLu%~ zkR)TkLLmgfPLQ^dS}Q}^_At?$Ikqc8ymbY>Eda0CI?PvSbiDR<|4?R9_u}ACZpe9$ zuz)mbnGh02rId-?W=9Se)TQv=y+1%i`1JV?xx0ODbr@c$1+BCDSaAXG2mvKEl#%n{ zKF<$d5a{^;T_e!PfMdD#e;zR6CGJEN$+d z{LQcw3B}h0ppm5>*~TrXu(&}zQ9Tf*)Xhkup<*1DOSW-j&Z+ylZ6 z(}l9kxK$)%B=xx9KB&Bv1ODr}Z?YTmOG`pIsSq-cHbdIrBoO~L}I{eKS^i1B!&(1 zWaJ^U8Me&xLQt5N6C#SdO0WI@m{#&JV-rj4Garcfx;9cn#M}mTtMw2y9z$;NAOD$*&9b6Q+njUk1_jRUrNw5H+ za}3o}Z$dQSdY~==)gz^Pw=p+Ga${Z=ZVpd)G+ zSCo1phr2wx{%FM#K#Vm_xclM>f=EQx&H7jV0HD?b^KxMEBdwP8YeuD_!J!&K6J~%J zSI19?vOxv1o)JU{LnP+`3C27X%qEn9VK}3+`k1404yj@ek$^d5b~ydBbi01r6+j$Sz}6O2 zKwUA6XfG3Yo!S5dq>VsCy4TIpY{0>--g*6(th zuyzGADQ(xJT0+*Hp3Qip{9XKLFVpBSj6X?4Scv-i6-8VcI@^6G~~-SEzX?{b*Ge^a{ndN$7 zjcUI^h))*q@4NMVQm$asUn$pZjA+J3__tI1C4!v)leT&C8 zA2X)&4xUj!oNS6qCA_r362Yspctn!IEQrRH`6uLI$0nVz)C0|WI~)ltHe=Q>S^gzY zzkBaFN~uR4p8p=7zWB%7-F>Y;FOgnFw9nPu|HXOuCf!9sMLcya0I(UhG;c)79G7Q3 z#%x^;hoHDZ=xQYd?@B~nh)d7!+l`r}6?VG|=CaUyA(99V&JooBuB4P{t!E=Htuf?G zZG{**7&ycYp(!VB$Py80m_n#^=o_&Yn#wTC?m=rd=b@6RXj9IUNOp1w{ z`IaLe$7&1*sd738-)u z632Q)kO9}#Z8!O6f)L&bN(`tyL0zjACGz8)Xgdn zwjc5rPyY!iWQtcRPFWRnm4!;7(jYjIOsz*$Uf!Em`o0ycoVYrEO3Z<6zC+48cEbhx z&7IzzXy`cr-I-(UMb>^FI4qAD(|PYItULX_yj}FXKOu(1ZhXku_=s`5U_YF*i+9<@ zJ==WFu;~c8Sm?&3;U%Kd`;HjG8aCSFzcdI@6HbDX*=$q)z0nXeYAuX0v5`CLYugac zXl9J#j+_G9;eyDPv(26%Z5hWs)lMK#+X)#1+k8Ru+;st7(J&BFq?F#l%uEz(s29%i zLpI|*KKbgO^5pU(f^6}YFcHSELkhS$F$o$#TI)n75`j!YP%?eEnO5+$_Uk3s1V>oxec~hzG=+^ZbD4r%w@6f(co-xZGaw9GZQYSPx{O~Y)N|FuM5%(!FD+3SSF$!a0|qc5E&`$va1Mtss}0Vu{e`s;50qQ zNj>U3^pqi0Ixs5XoaAG{MNs?FKK6)(S}mavoT2E-?XdW3d~u zY#3HU6$y!9%+QPn=ilbZ%}0E3`DcvjoVT{$#p1-=_0ZJn#C%RjPHV;(Q=hPsa9>uU zR9kJ+BMryQ$w6oeYKk{#(yxic&S7vW-n@4wbuIAXnDqk&sU2&O?%ecZ{+x^P5$1*G zr%$=Jdxuur3J%|{-&g=d1@S--p;aS>ez!DlL>HPEK}OV-H#T4A$<0UH-@T2>K=DG5 zNNa_rzQS?cC%-jEB_ra97m$egf{-ZMNc1qwiofE^zcGHD&o6((n`gg+`Jo$Ts31CW zy?nvj`|opAz958z#(+v#5AVNT=`T-z$>p^A0<;6AF1%-d0~cd6>^tVci5MbFyCzud zr@kg0pMmW+8n1iDI7C{{_5Xmo;9wMMkqUs>(x&LuRY z_}Y*Orm=rJ{iF{m(0l@iA@BL>@XtGbf^j#0061^$f1i)b|4FkIyuEcZ|5xY$nyt5C zA;*#Cm8f|=@zxC>t&hcU%o-u=nC(c^(9?J&65I#^#ah?0i+3fNM3jiz(n&8~(FRre z39IzGo|u3_ttXO1nnWC@SYui~;r{S#F6XcM2(CfafhIi|6a-1a)!_>;?Q?m$4Y;Bt z4Sat6Bbrxo97thHj$7Pz1lg`VsXz$mFcPjKVOWJiwf`)M5wFgdr;mAh`h;2w#SXnV zwEF5gA#I(Si;!htNDnFW;LQ;CR0^Xl9NR){bP#NtI9)wQ)0RWM0vcKiG46>X zwA3iIa$07_*j;|Uxc(u(^VZ+x=ucScQ_eQ$l(}%K-R(!3b6u`Gbg5}IJ6#Ae5aJ2( zarG@3D9geSMndS1e=dbmD={R3Mc&wdkLC+E%O%r%$(#G{^lU|0sBKzDpB*5rt@Q}B z+Cj=%Dw+i=(l@iMb+WbI9kqU8Ft_fp93oMKxjGUW(wrcHZMw(j#~*MO-vWhFCW@W9 zL5evkY`C63=X`kcc6a})6ae?$#s^iZRf2YDYw(DN*4tsD16Q@-cz29tT2DMWc|`gs zCTK@`-8<8+`kG#&16J28zD_d0==1pCD>mQY{_Yz*zWFH+H{WM!FZk;C3%>E--{V&= z{tR{FeDe;&u)$T(R?+pE{$KyS|CYDz!#i&P_kf4M8QlHvzSW;;5T5_-pYa#Jg8%$~ z`mdPl6S8Jb<*<%EK`dg+f{Efq2$CSI+T!L&6dtDU^Y{PL{{k-H?w@=cT0dr<|2IGH z+2Owgeh8m@0{_n^@ZbNV|B_&dAeo@bCf=o#1GOHxfA%54p0oH9+$wF!WeCN^MgzQYyf+t}gJJCrzlO=U!Ut2mu(K2pE zh#Qt>;KmRm=0dTBx;P;XNTZMcP$C)kJ_`vY^5*ut{PM;BO_Y(h_P@)RFIegXGIN|y z3_7s1g321A9Pg53X+MqSfeT7$4-xu#t(g@NZEh^Ft(kUQ_ zf^>ID$1v2;Atj153#Vc(`J8=n zpZ(b{^hnR%B%IzzLD<0X&0WKb?ggs>BTAFr1^2ZlJis+#c{io*@+$3-rakGHNbYdU z*{72TcjDy~>!hlWOPx|`l78N&l8SBDq7JQUW%Sj1Ti)?)6LN1ON`sUu<+jG3lJq}C zdN+QmqD?SAXvNM8hw2^-IA#}C(l+UC#(Ts=FZhWZo!i=m5XXWM*i8;`F9H@??!kx71$gv4y=)8&B&k10@<(LX zbDo}N;Cb@TSF^G&VW^aFYx==Kmnm`!&-T~Uwx)U>dSCw%(5y(y&yf6dz?;mSVX3X2 zHLpd|#m(hZ*#Lbhpze52lwB(QQjS{6yRy%bTv$I#fy6+8 zt~~J(lkd*z_?#4*(9xvXe^a zG}%DfqiQUE(X9jwDik3!dtU6-~A3Zi%5%-v-n_%+wcc$$OI~j959@K(=5&i0KEdb-b zQfR&F?_wGPv5eN?sA`>TfA?lcBRHG!-0;U@8)6lfN_K;6$3dFi3Dd1UZWWtleAC)d zFIMzFG>rGjy(|c`omDg(%b4{QCTHa`NbTFm-_Tja%c0)-lD%VF-Lb`?O58w4V3Mb&f8Aj=4g zL%XfJiRFCh{*5B;=mT!`2jyid6MXOw?RDWVjf@{;HkB^Zk`~n5-`njeXQVRXrON{Q z+;=S_-774=fguvE+&%NtKF*pC?0Tt1d*D5;5BFYK(`eJZ2ou42La888?$QgUf}OW( zZI*#IwXG{EKGI7|;Sg!yvdy#M=Nt%-3s#Z<747k*wTe$6mFj6}r|*~OTS7OjCc@-< zt|YC_aUhTOT8xlRx8#}i&$hYm?i~KRArf;#g)v{2<}yYYUL;fp2OL{1DWavaO(4F& zQ@lr^(O;wTyA7i)sdc#1(gc6bwbTs9{}?Wg4(@tTGs0@_i4d<*9oHjvom+Xn+z$MK zoP4(WcA@;O$hsf4fRnlYXU8RF^y8v}GR4v-3L-tBkNTewxqBL^8C(xYko=7@U*M;u zx8a>Sq|81{cs$u{V02;ob81Rhi_2FZruQ4JXQiwoth%>dL(erHwWL3!w63~jt!-M^ zYd3QY2JU@ee!5XzF2VOzBmfb+crPkKj&_{QM9tD>&MN>lBX$wdNw(bK`#hSKsp4;es`|5|a;lsysCFefr!J4xxtnHi@DFih z5jcGeiYt%~0=9nt#iq)kO~n08!MYNtp36x;8yOrfiS_v0ocYnsvx4h!Si}`U{AzUd z0j#fnopIySR8iB#!&{SZ4jO&8#Z6y(U&cz9Le@`yc;lT&ho7(I=ZH+k{CQHS1p2EF-K5C+%|lZn(r)Du(mu zVgi=THTt`wftcX%BGPQH@h6|H`UF%xqGPjr)#px0L|1*$qEA#QLJxbZV@9mhoe&-% z2uGkk@lL|6r>>6aTrT#ldCQ%~glwdU-rqsSW7g-pBVCw44app*V{2d*)L++QDz_jjzYduce^dA>nt|B#urQ0Ky-9W5b z)=Dv6VahG}2D)(ad2s`2la^Zo7SrKeDKX3LLEqrF|gp+v~4D1 z;I2~K^DL=kB+EYE!wyzS$Fr4Q5kbbJ+d;<)FI~J!OHQlPacM zOXeTch(IdAF9`FU-GHa4V?OmjNzr80dBOopR2i9L*giEC3@r1g*_-%Z>{G6vZ zqc;nB=@IuesyBs_pmMZhla~<|Z&ZqhT)MKTA>_2D*)qPGDFj0x;H!Q_pJir8r(V`T z{Wbr$YlkIfFvQ`COdQ;px+oX=vQ;XN_R(AYl!Ei+X`~Y7i6glXO=-Yy>ua>lt7y}7 z3yZega*io{i#~-1C3^?o(-pl|B_ok`b#Ql&OA@Z3&4Lr<AR7a6u9czBE-jM=YEFeG*?2RSku>#8Qp2Omh|tBTvO*+RK$7NL3?YVVW3Nz zR?-J^oJM9pdya#Xf*stY3H`D!p}Lx-+uRVw*MwTXox@*y%oqjCXc6jEVYa1^t0f-;3Ae(IW?)0h=)2g5 zv`2Tb**NlECP^Q&>-blhD)0pocz8Gr<#Q09d~8Lv$=5h-lK+=Cu9-3WXHf#%S%VO% zunY|Wn_Ta#6C(aC`Noph1p%i!ae~5%jLQktBw^BCVC`t)gGwWVckHKFo$M=F))bbDv$$FfM!X zwb639VQK5${<)1ub^pZrN}I`}=EFa0g0-ChN1mBJwuyu}8hg&oBFhvgN3B8gDb!+a zo~nIa_#OxDCnVGYv&Qcy<;~@Zr}N#&dP1JsurT4Cd|9`>pAyY!mPw%9&b40FI@BWB z)wJr#o29m8)Z)qe_UPJuCwzs8lnw+DW02yYCNSENRlo0B8pf~4Y~R#8p3hGdoW|-b zO|vG&%aYt{CLqMD9nkf$uhVHVAZi5b4qe7lb=IBoLFe0(Fn$@GZ{A(Yns_UwCU<8u zCs@VrMnac4&1}6}9^_6ZBMcFvl#mkr{DK$y&OqPzDxdeDeWpGlQ4nQ3r8TFdFI>QC zXs)-k*%>XnpC$fv5)F92TYO=nrF#jDA9BlygPU^R0WzxUcCzU_Z~}6)S_V&}V6mY~ zeoDhBBlvu%ujV29dM`L*Gwc=SY`t5R$;2TdmLGV(Y)gMolNCrwYb<_}pzMw9l!a5& zjMF|gH6fwRkyhgQIzP?Tl7G)#^Ly!?OJ2o1J0cxMnSl~>?BrLH#@zJ@AzjK&md%&E ziPc8rKF#E|(=Z)Jf<3W(i=@q~0ff?tU!8^lyY`Qsfj9jg<*CO5p84U#FQYTu$0AIU zqa35|a~FaRO`t@oL`~RRE85n2c{J!v@s^*(fZ!|C(O#C{r`YJ#t1CWr0Zr#s!lCx= zY@ven-&=tNy3+5>#QuQT3$V#_O(X^1GNYVYR#Zl)cSR~67>GJs#dlJzve_k;b zyC8Tb0ci2GOgSqt2<{jM`YQ{`*5#er{vc3FWJT5kjhVn89r5b-HH`7%3lg_>7o+m@ zX)w}R{ePKE&xn3&e%s%j;F@{c-yQ#`%Y~&!S)?M>u%hwq@puUz-uv8&Tc6vqk;Rh) z2*zSWejGM zN^X}J4`Mk$!*@{7f7j^#7;sWFjfrYk}F!L8z)b-pJv0|%RD<;)MbyO z;Nle{=%8npzq}s~%-fTK{1UU=h$6?mfG{v+n9bV)Z^UBL4$dfq(p=KkjIScy-fp;Q z*G6xu3KhL{VvuD*$z*tK@XJ9q`VO|mbT2hbIrBv6$5xZOi1X;yMXc9;>D(8}R#X%a z*InBDj!ccH=h5$zpi7^dc|Ui-NFgZLnzbLI_tG|lfYr^uW^LA+Jl#AQ-t_)$TG4=Q z?w@n4@S{Gldt$m}r}1K+93Dp;UxeGUAS6j<7*NWUEsuKjzgeFJeJ2c~a$TOf`j=}4oh5HnPd3r&efZHD+wW$R z1J`cLD$@z$$ec6won_4MyUcqTp9r?4fCGgFELqtTEF-L$l zrtP6&L#uf1FWzyxlrF6-K10VrIRUC5(6y1UqpX$fU~bu+x-3bN7O+p6LQ1FHuk(d5 zXYZ#&V#t|J;=bJg%Fv#zJI)^cuH=&VUG9D_saUt<>C|y$B*uJb~YTC z*`~w^hTWJmBBsUd&(R8S56AbQ?JsV_rn3`qTE8we*h5luQz9}Fm~v@EwkRCz*i%3K zwv2pojCea+fDKVuu%jq`JqEjv@D67&iH=>H5Crg2uMLaGJ@noZ%?~UHlpv(oM?+3ugYJx8j8l!FIe= zQ7eeRX|WXI>0?Vfm6Ho(6*yHUU3$$+P#a^&rxK;{YuKPm=ffg%vVy;O7L!%1SfJ_K z9Ko)qniq5S#$R|dJ~0`J4H7;UV-nPE$-Kk=8+)=H8o;j;oEA50@7x z`=XfRv^ea>#8`iY1r@ctS$LqluWeit%UBICER)@FuyZT=py;fR&jlY2YlA+Ivg8Kc zC$Z9}C^DfItQpYEPMP3-E}rG6Ly}^g^XLoHo4~GQHytkVS{mQwtbqu2E=ou82Y0CK zKc*vI@%%QN7~lm_yPNtbJBkMeklHJiQed6ff4(=MGR?|xTDna^rQv=l`(_W*KUfUE z%MDDTeE8V9ui$Zt)7Q*jP5t1Q@8sc4 z2zt%sF5Y1eNWa%3Pw{npa2Dqp_YWqkfP0h*1TqGb-t^E=$xTKd|}D4#x&Y3ntz z7svS2p-o&Y_s=M?KFjG#wQX%jt~D;Z8ll>P_C|~Hg$|-Grcj4P@)BLT)6?@fc>YM{ z^TRgL!N%#UYlctF_JZ(rO#G}hIRDZ4OjJ=V!)yU)#oEb}D{92QQ55UzUjc?O5r(GR^z+AW%;h;^OrfqntT6iQ}ovZZl))IZUA(O0~1nd0> zhDh-BK=p43ioeMK|Gyj=^l%UGrMTCwM_PGf7GtA&gQSfX;vt~>Mu}B<*4rc*DP2Ml5jhW%d<_BL z5BfXSadX~{#O;1uSHNxA0G*CjZJu6WtONGETO@XWn9ykyT~th(A}j|$cY7&f-{DiM z${Uld97>1H3pH*$Qve%>W0@9Dq;10UE{p}j>UUD@Kva?XVP~`KCq46f*Vs&$ex6A( zxaKnnRSa`S(@{i!K7^O|KgnqLBN@W=-HgN03?0BBrBrgERMLiibf}H|Odag+AqwEL z6@_M>M8P)Gl<-$#keJ#=@c<&fAx~J-Jr7sp0-es6LexSD;eTS`6OCV>NB*i8mIKTK zV30!d<6cw{Zb2{^tI zXly2@%gqEo5Vdju27Snkq&5JkCh|Xt%!=Vy59^(@7`c&~Cpzj6O&%jSO6R!I09fA! zk~qGlhe#|lEQJuj32Z`;wY5=!MX1jHonXMM(G9%!CR5@zS0A1oqFK?S&Se9 zv@t3jjDd5|*b#ea-rCVO5qfew;D{>9%kd7S#(NIUn-@x!pn`I4w<}WQ&EB>)DG3)5 zXY5Yi=JI05VC_8TZ^_S20HM2`ttSo6SF-mLN7T%*3pN;Iz!C~NTXlJennpxjw2oAH zpP(w3<5MFpzJ4_A7HmR$!HJhqwC5N7c)`a*;Lyz&2_l|wh||?s zIe;g`ZwS2FHC7E(p3Yvj2&0P3x4CMAzv76xxHJMTd-U@~G0?hVNLe^qQIAaQ@r+Mu zw|W2dh@>(Al^}TD5(D{*<}g2VFB484oc*Ixzp~ef8#=t1Di`@)t<-icW#rZnj@hcO zMjr_>jJs}6ySC9BoJtHW7tMM#{eYCiTL0+>bQ8;Q)hD?PbDj*}AW$NQlUrZ20@}CP znuuinvW|bgJi z7h(@xIgbFWH`kvun{9O}wXCg6b6|bq%Cp*g@n;?pSMf*92p1oqy}g#2KMUm9)BJB} zIqoqAB=D=8Bl3|ji@jqA?!AqtBC5Ou%FsSdR*A-*rtaZ2`(Qr+-rP4nHHvyAM*OpO zHwK$BSrkVHlF>L~Y1XR?G#_XK>m}KmWedd@1j44dDY|2(4ccUG6<6@ikx#`n_E1om z#3vKIY)`pYOLD6WvDEteOv<2~+bSb1wh$xL!G?{$*{hHqAD80%Au<<~79VZoUd&#p zLz-Y$7RG)z77d+jO=j5MLl=$-;x|U%Wt1B3+Y{w~MP>6v@|)cZh2zdso{rJ(grKJT z=f!=(&5<6U%#Ew0P1PY`e4UoOBx4r>P;I<^4v4{MCjcN>Ob2^E&?ho}D z|KCiq4h5J?fMt%@!N{xp1rD*nrN1{8O1OR<(gv$&{)31AVU=Ej?>ZfHH#-Zhln+XW z=WM0lo{0Z9ofMI+OgR5$o2I@TnnmA`|HA{vVGl@Wug10+^sTIyXvO|h?EViXu)_!s oEAYQx60TTk)&D;}Yor4>K(#DH=d@{+6L9-zYrIg0s@g{XAN{)au>b%7 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_idle.png b/src/main/resources/assets/unicopia/textures/entity/poison_joke/bulb_idle.png new file mode 100644 index 0000000000000000000000000000000000000000..7fe663ff3245675c5f5c5a5a7b9afd3de490c734 GIT binary patch literal 43186 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/curing_joke.png b/src/main/resources/assets/unicopia/textures/item/curing_joke.png new file mode 100644 index 0000000000000000000000000000000000000000..1fb7402ad18607c5b1687d902f308a9c8ca269bb GIT binary patch literal 4857 zcmeHLYfuzd7VZH=#Pvm@>tkKok@)WE$Mno}w+s&%go%tn7zF~l_Dpw=9hnEy4Ko@= z#l?WAiLWvk)I?2mHLFpKiDFb1)Kn!JQzmNV0Y+16e8iAw7PXdSZ}$MgZmPDdUH>uE zoqPJ;bH01dcg{V~RAx?3>lZyd8iJsH)^tl2mBM$rL7(?t%`ghSzAZ3P%HKvr~=FUA`BN`ei9KnHqmIy|VS0@*((~ z;IWtQ-l2~LVqtPZM(xq7OW*oZV5$e!9+`RI`e022UJgjqE|O^8}HWy|F*MlOrn4GkM~zgpYqFGE6!#t z+)8Ck8ZtTe#@tzi;i`Cb_nsRYP|iGy_7+{7V7Pd6>=Jg|{{54rEf>eF%WaH_tH{hu z88-NG#i#q$J4V^A_laH7`17=Hvp$>|Ir>c4p7U^BX-ndLbN*h;aO0Wsn55RKYl~KC z%ymh%cc|twYc9lV%04-{vFRT(;TH*TL(8Zl97cC_XXhW1aJP8Fuh4(PP&BP`@!PnLZQiUtA!T|lq%`duha z(=>_^C_x}V1CbVZWHyL+q|pjQ2*bilPQU1rMXv`|FjI^jK1 zI}3mhG|2i;T!*1)gl2c0nNV`M7X(8Dc_;@snm%PP(C!bovd*snwAvkBJy|36` zsD{HiQNECO162u}ig!CwxpAEq3I#6F?NhA)+1)f{QFugFH{X;UHJq-F0P{}VZrUBO ztI9yjW@9W~XR+d*)ne8v=QEtwDRPV|8c2$E(0UBP92|#`EM`C`gMcG~fixO$%pg!E zQx_0@;!&T+ zrEVyMGs&4&vzE|dk5)1ZSy=!EW^IP(DGol`u#0X!TV@qDaT7-BF(XOgMk9gYp|DlF zS-f8Ym8f8dYK1g}GD1q}As7s+6mCYB0oqI#8J zSiKYs>kNe;u_ZjG`U$W?Th4i`$Hjy0(O#|{>*8;e0zq+l3@022A?Qhjq)aqI@i>oA ztR55+3GO{<=#DOV1v$X_`D7R15pV^{Q~eDEk5dyhzPol{9_}=n`X6ZzDRk+hfSS-6=+B_tqn-V}oipI?ll-)o+mk#3hIY?kg7#-PkEajf+`oGZ?{pgL6_kch20^og6H_g@x-X$X))6y(Z6Z8XgziG~F@Ml(YTVxl{$WsSwItiAkKSE>cA9<0(9=t< zH~rXDePsE$P5k!vV@^5dMLjIT8dle}4Dby?;(AGgFFSnK63_2(M<8YJE51~c0JhI+FEnne605`yH1ap^O4m&^?ZHf_n*Y{ z4}`yx?+;%+<@=R6Y1=BUWJ^od<@@=f`WdFve~LNnPk+5@F8cq%PSRo#2ZabGOY|9a;;#9#VfF{&f2WITx#`-Z3$u%suw3qPBZUz3wD0S!gd94iy|()TjB< zx6Q}nxKr=E9TyuNSTj4ANnKi;9lvAQH-_;!2@{sqwf^MTJTksip54p6J?uMLubWf5 uvT}Pt-ppMO(8#3T0}UT%H~eZjp^2?|qx9m@og+a;A#3V%%f2ad%l-w$uAk%p literal 0 HcmV?d00001 From 19831a635f94773c194b5befe9d494a515906e89 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 3 Jan 2024 22:59:58 +0100 Subject: [PATCH 09/98] Added translations and fixed the loot table for some of the doors --- src/main/resources/assets/unicopia/lang/en_us.json | 4 ++++ ...k_stable_door.json => dark_oak_stable_door.json} | 4 ++-- .../data/unicopia/recipes/blocks/cloud_door.json | 13 +++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) rename src/main/resources/data/unicopia/loot_tables/blocks/{darK_oak_stable_door.json => dark_oak_stable_door.json} (82%) create mode 100644 src/main/resources/data/unicopia/recipes/blocks/cloud_door.json diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index d2808a2d..fe458ff4 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -308,6 +308,10 @@ "block.unicopia.cloud_bed": "Cloud Bed", "block.unicopia.cloud_chest": "Cloudsdale Chest", "block.unicopia.cloud_chest.double": "Large Cloudsdale Chest", + "block.unicopia.cloud_door": "Cloud Door", + "block.unicopia.crystal_door": "Crystal Door", + "block.unicopia.stable_door": "Stable Door", + "block.unicopia.dark_oak_stable_door": "Wooden Stable Door", "block.unicopia.oats": "Oats", "block.unicopia.oats_stem": "Oats", diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/darK_oak_stable_door.json b/src/main/resources/data/unicopia/loot_tables/blocks/dark_oak_stable_door.json similarity index 82% rename from src/main/resources/data/unicopia/loot_tables/blocks/darK_oak_stable_door.json rename to src/main/resources/data/unicopia/loot_tables/blocks/dark_oak_stable_door.json index f758c3e2..eb337eb8 100644 --- a/src/main/resources/data/unicopia/loot_tables/blocks/darK_oak_stable_door.json +++ b/src/main/resources/data/unicopia/loot_tables/blocks/dark_oak_stable_door.json @@ -13,14 +13,14 @@ "type": "minecraft:item", "conditions": [ { - "block": "unicopia:darK_oak_stable_door", + "block": "unicopia:dark_oak_stable_door", "condition": "minecraft:block_state_property", "properties": { "half": "lower" } } ], - "name": "unicopia:darK_oak_stable_door" + "name": "unicopia:dark_oak_stable_door" } ], "rolls": 1.0 diff --git a/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json b/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json new file mode 100644 index 00000000..59d66aba --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json @@ -0,0 +1,13 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + "##", + "##" + ], + "key": { + "#": [ + { "item": "unicopia:cloud_block" } + ] + }, + "result": { "item": "unicopia:cloud_door", "count": 1 } +} From 5196c9bf17ba71bfa87dc53fa83176fcda690e18 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 3 Jan 2024 23:01:04 +0100 Subject: [PATCH 10/98] Made curing joke obtainable by earth ponies --- .../ability/EarthPonyGrowAbility.java | 8 ++--- .../unicopia/block/CuringJokeBlock.java | 31 ++++++++++++++++++- .../unicopia/block/UBlocks.java | 2 +- .../entity/mob/IgnimeousBulbEntity.java | 17 ++++++++-- .../loot_tables/blocks/curing_joke.json | 20 ++++++++++++ 5 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/curing_joke.json diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index 6d982e43..523e2fc4 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -6,7 +6,6 @@ import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.block.UBlocks; -import com.minelittlepony.unicopia.entity.mob.TentacleEntity; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.util.TraceHelper; @@ -85,14 +84,11 @@ public class EarthPonyGrowAbility implements Ability { w.setBlockState(pos.down(), Blocks.DIRT.getDefaultState()); } w.setBlockState(pos, UBlocks.PLUNDER_VINE_BUD.getDefaultState()); - } else if (w.random.nextInt(5000) == 0 || w.getBlockState(pos).isOf(UBlocks.CURING_JOKE)) { + } else if (w.random.nextInt(5000) == 0) { if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) { FarmlandBlock.setToDirt(null, state, w, pos.down()); } - w.breakBlock(pos, false); - TentacleEntity tentacle = new TentacleEntity(w, pos); - tentacle.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); - w.spawnEntity(tentacle); + UBlocks.CURING_JOKE.grow(w, state, pos); } return 1; } diff --git a/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java index 73243fd1..5f37ff82 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java @@ -1,16 +1,20 @@ package com.minelittlepony.unicopia.block; +import com.minelittlepony.unicopia.ability.EarthPonyGrowAbility.Growable; +import com.minelittlepony.unicopia.entity.mob.IgnimeousBulbEntity; +import com.minelittlepony.unicopia.entity.mob.TentacleEntity; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import net.minecraft.block.BlockState; import net.minecraft.block.FlowerBlock; import net.minecraft.client.util.ParticleUtil; +import net.minecraft.entity.Dismounting; import net.minecraft.entity.effect.StatusEffect; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.random.Random; import net.minecraft.world.World; -class CuringJokeBlock extends FlowerBlock { +public class CuringJokeBlock extends FlowerBlock implements Growable { public CuringJokeBlock(StatusEffect suspiciousStewEffect, int effectDuration, Settings settings) { super(suspiciousStewEffect, effectDuration, settings); } @@ -21,4 +25,29 @@ class CuringJokeBlock extends FlowerBlock { ParticleUtil.spawnParticle(world, pos, random, new MagicParticleEffect(0x3388EE)); } } + + @Override + public boolean grow(World world, BlockState state, BlockPos pos) { + var otherFlowers = BlockPos.streamOutwards(pos, 16, 16, 16) + .filter(p -> world.getBlockState(p).isOf(this)) + .map(BlockPos::toImmutable) + .toList(); + + if (otherFlowers.size() >= 8) { + IgnimeousBulbEntity bulb = new IgnimeousBulbEntity(world); + bulb.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); + + if (Dismounting.canPlaceEntityAt(world, bulb, bulb.getBoundingBox())) { + otherFlowers.forEach(p -> world.removeBlock(p, false)); + world.spawnEntity(bulb); + return true; + } + } + + world.removeBlock(pos, false); + TentacleEntity tentacle = new TentacleEntity(world, pos); + tentacle.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); + world.spawnEntity(tentacle); + return true; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java index 24ebc94d..cd8284da 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java @@ -139,7 +139,7 @@ public interface UBlocks { Block PLUNDER_VINE = register("plunder_vine", new ThornBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY), () -> UBlocks.PLUNDER_VINE_BUD)); Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY), PLUNDER_VINE.getDefaultState())); - Block CURING_JOKE = register("curing_joke", new CuringJokeBlock(UEffects.BUTTER_FINGERS, 7, AbstractBlock.Settings.create().mapColor(MapColor.PALE_PURPLE).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY))); + CuringJokeBlock CURING_JOKE = register("curing_joke", new CuringJokeBlock(UEffects.BUTTER_FINGERS, 7, AbstractBlock.Settings.create().mapColor(MapColor.PALE_PURPLE).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY))); Block CHITIN = register("chitin", new SnowyBlock(Settings.create().mapColor(MapColor.PALE_PURPLE).hardness(5).requiresTool().ticksRandomly().sounds(BlockSoundGroup.CORAL)), ItemGroups.NATURAL); Block SURFACE_CHITIN = register("surface_chitin", new GrowableBlock(Settings.copy(CHITIN), () -> CHITIN), ItemGroups.NATURAL); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnimeousBulbEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnimeousBulbEntity.java index 286f0fad..a13fbdf1 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnimeousBulbEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnimeousBulbEntity.java @@ -11,6 +11,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.entity.player.Pony; +import net.minecraft.block.BlockState; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.MovementType; @@ -120,15 +121,27 @@ public class IgnimeousBulbEntity extends MobEntity { World w = getWorld(); mutable.set(getBlockPos()); mutable.move(offset); - while (w.isAir(mutable.down()) && w.isInBuildLimit(mutable)) { + while (isSpace(w, mutable.down()) && w.isInBuildLimit(mutable)) { mutable.move(Direction.DOWN); } - while (!w.isAir(mutable) && w.isInBuildLimit(mutable)) { + while (!isPosValid(w, mutable) && w.isInBuildLimit(mutable)) { mutable.move(Direction.UP); } + if (w.getBlockState(mutable).isReplaceable()) { + w.breakBlock(mutable, true); + } return mutable.toImmutable(); } + private boolean isPosValid(World w, BlockPos pos) { + return w.isTopSolid(pos.down(), this) && isSpace(w, pos); + } + + private boolean isSpace(World w, BlockPos pos) { + BlockState state = w.getBlockState(pos); + return state.isAir() || state.isReplaceable(); + } + @Override public void remove(RemovalReason reason) { super.remove(reason); diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/curing_joke.json b/src/main/resources/data/unicopia/loot_tables/blocks/curing_joke.json new file mode 100644 index 00000000..7a4cba95 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/curing_joke.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1.0, + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "unicopia:curing_joke" + } + ], + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ] + } + ] +} \ No newline at end of file From 2d87fd5099508984d6643ddf0a0b577aba978388 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 3 Jan 2024 23:01:45 +0100 Subject: [PATCH 11/98] Exclude grass blocks from earth ponies' ability so they aren't always swamped in tall grass every time --- .../minelittlepony/unicopia/ability/EarthPonyGrowAbility.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index 523e2fc4..c7e6493b 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -78,6 +78,10 @@ public class EarthPonyGrowAbility implements Ability { return growable.grow(w, state, pos) ? 1 : 0; } + if (w.getBlockState(pos).isOf(Blocks.GRASS_BLOCK)) { + return 0; + } + if (BoneMealItem.useOnFertilizable(stack, w, pos)) { if (w.random.nextInt(350) == 0) { if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) { From 383cefa9159849c52a0f633e2f936b587b19ed7d Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 3 Jan 2024 23:11:00 +0100 Subject: [PATCH 12/98] Fix damage message when dying to a tentacle --- .../com/minelittlepony/unicopia/entity/mob/TentacleEntity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java index 649a033b..d20eb3a7 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java @@ -15,6 +15,7 @@ import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.MovementType; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.damage.DamageTypes; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; @@ -156,7 +157,7 @@ public class TentacleEntity extends AbstractDecorationEntity { if (isAttacking()) { if (--attackingTicks == 12) { if (target != null) { - target.damage(getDamageSources().generic(), 15); + target.damage(getDamageSources().create(DamageTypes.MOB_ATTACK, this), 15); Vec3d diff = target.getPos().subtract(getPos()); target.takeKnockback(1, diff.x, diff.z); From d4a3a274e2c13e21d9e9f6154df5f0b9d093c2dd Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 3 Jan 2024 23:19:17 +0100 Subject: [PATCH 13/98] Fixed cloud door recipe --- src/main/resources/data/unicopia/recipes/blocks/cloud_door.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json b/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json index 59d66aba..7d41de39 100644 --- a/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json +++ b/src/main/resources/data/unicopia/recipes/blocks/cloud_door.json @@ -6,7 +6,7 @@ ], "key": { "#": [ - { "item": "unicopia:cloud_block" } + { "item": "unicopia:dense_cloud" } ] }, "result": { "item": "unicopia:cloud_door", "count": 1 } From 0372592a793b33e26fa5ba589a51b6b68ff991cc Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 3 Jan 2024 23:19:29 +0100 Subject: [PATCH 14/98] Fixed errors loading changeling diets --- .../resources/data/unicopia/diets/races/changeling.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/data/unicopia/diets/races/changeling.json b/src/main/resources/data/unicopia/diets/races/changeling.json index e6255344..8b108468 100644 --- a/src/main/resources/data/unicopia/diets/races/changeling.json +++ b/src/main/resources/data/unicopia/diets/races/changeling.json @@ -46,7 +46,7 @@ "amplifier": 1 }, { - "type": "unicopia:multiply_hunger", + "type": "unicopia:lose_hunger", "multiplier": 0.5 } ] @@ -62,7 +62,7 @@ "effects": [ { "name": "Love Consumption", - "type": "unicopia:clear_love_sickness" + "type": "unicopia:cure_love_sickness" } ] } @@ -81,7 +81,7 @@ }, { "name": "unicopia.affliction.love_sickness", - "type": "unicopia:multiply_hunger", + "type": "unicopia:lose_hunger", "multiplier": 0.5 } ] @@ -122,7 +122,7 @@ "amplifier": 1 }, { - "type": "unicopia:multiply_hunger", + "type": "unicopia:lose_hunger", "multiplier": 0.5 } ] From 073d411e9010d9d81849736ecf99ef5464c1c1ae Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 3 Jan 2024 23:19:43 +0100 Subject: [PATCH 15/98] Fixed errors logged relating to the recipe book --- .../unicopia/item/cloud/CloudShapingRecipe.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java b/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java index c88a5abd..4f12f7db 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java @@ -15,6 +15,11 @@ public class CloudShapingRecipe extends StonecuttingRecipe { super(id, group, input, output); } + @Override + public boolean isIgnoredInRecipeBook() { + return true; + } + @Override public RecipeType getType() { return URecipes.CLOUD_SHAPING; From e572c2c1ea8e6108f023b1accf10988f6a3378bc Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 4 Jan 2024 22:41:27 +0100 Subject: [PATCH 16/98] Added gold root --- .../com/minelittlepony/unicopia/UTags.java | 1 + .../ability/EarthPonyGrowAbility.java | 38 +++++++++++-- .../unicopia/block/UBlocks.java | 6 +++ .../enchantment/ConsumptionEnchantment.java | 3 +- .../unicopia/blockstates/gold_root.json | 12 +++++ .../resources/assets/unicopia/lang/en_us.json | 1 + .../models/block/gold_root_stage0.json | 6 +++ .../models/block/gold_root_stage1.json | 6 +++ .../models/block/gold_root_stage2.json | 6 +++ .../models/block/gold_root_stage3.json | 6 +++ .../textures/block/gold_root_stage0.png | Bin 0 -> 4282 bytes .../textures/block/gold_root_stage1.png | Bin 0 -> 4375 bytes .../textures/block/gold_root_stage2.png | Bin 0 -> 4452 bytes .../textures/block/gold_root_stage3.png | Bin 0 -> 4522 bytes .../tags/blocks/maintains_farmland.json | 3 +- .../loot_tables/blocks/gold_root.json | 51 ++++++++++++++++++ .../blocks/unaffected_by_grow_ability.json | 6 +++ 17 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 src/main/resources/assets/unicopia/blockstates/gold_root.json create mode 100644 src/main/resources/assets/unicopia/models/block/gold_root_stage0.json create mode 100644 src/main/resources/assets/unicopia/models/block/gold_root_stage1.json create mode 100644 src/main/resources/assets/unicopia/models/block/gold_root_stage2.json create mode 100644 src/main/resources/assets/unicopia/models/block/gold_root_stage3.json create mode 100644 src/main/resources/assets/unicopia/textures/block/gold_root_stage0.png create mode 100644 src/main/resources/assets/unicopia/textures/block/gold_root_stage1.png create mode 100644 src/main/resources/assets/unicopia/textures/block/gold_root_stage2.png create mode 100644 src/main/resources/assets/unicopia/textures/block/gold_root_stage3.png create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json create mode 100644 src/main/resources/data/unicopia/tags/blocks/unaffected_by_grow_ability.json diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java index 91b4fcae..9d4a460b 100644 --- a/src/main/java/com/minelittlepony/unicopia/UTags.java +++ b/src/main/java/com/minelittlepony/unicopia/UTags.java @@ -42,6 +42,7 @@ public interface UTags { TagKey CRYSTAL_HEART_BASE = block("crystal_heart_base"); TagKey CRYSTAL_HEART_ORNAMENT = block("crystal_heart_ornament"); + TagKey UNAFFECTED_BY_GROW_ABILITY = block("unaffected_by_grow_ability"); TagKey POLEARM_MINEABLE = block("mineable/polearm"); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index c7e6493b..b69ebae4 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -1,22 +1,31 @@ package com.minelittlepony.unicopia.ability; import java.util.Optional; +import java.util.function.DoubleSupplier; +import java.util.function.Supplier; import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.MagicParticleEffect; +import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.util.TraceHelper; +import com.minelittlepony.unicopia.util.VecHelper; + import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; +import net.minecraft.block.CarrotsBlock; import net.minecraft.block.FarmlandBlock; import net.minecraft.item.BoneMealItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; +import net.minecraft.particle.ParticleTypes; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; /** @@ -61,7 +70,7 @@ public class EarthPonyGrowAbility implements Ability { for (BlockPos pos : BlockPos.iterate( data.pos().add(-2, -2, -2), data.pos().add( 2, 2, 2))) { - count += applySingle(player.asWorld(), player.asWorld().getBlockState(pos), pos); + count += applySingle(player, player.asWorld(), player.asWorld().getBlockState(pos), pos); } if (count > 0) { @@ -70,7 +79,7 @@ public class EarthPonyGrowAbility implements Ability { return true; } - protected int applySingle(World w, BlockState state, BlockPos pos) { + protected int applySingle(Pony player, World w, BlockState state, BlockPos pos) { ItemStack stack = new ItemStack(Items.BONE_MEAL); @@ -78,14 +87,35 @@ public class EarthPonyGrowAbility implements Ability { return growable.grow(w, state, pos) ? 1 : 0; } - if (w.getBlockState(pos).isOf(Blocks.GRASS_BLOCK)) { + if (state.isOf(Blocks.CARROTS)) { + if (state.get(CarrotsBlock.AGE) == CarrotsBlock.MAX_AGE) { + boolean transform = w.random.nextInt(3) == 0; + DoubleSupplier vecComponentFactory = () -> w.random.nextTriangular(0, 0.5); + Supplier posSupplier = () -> pos.toCenterPos().add(VecHelper.supply(vecComponentFactory)); + + for (int i = 0; i < 25; i++) { + ParticleUtils.spawnParticle(w, new MagicParticleEffect(0xFFFF00), posSupplier.get(), Vec3d.ZERO); + if (transform) { + ParticleUtils.spawnParticle(w, ParticleTypes.CLOUD, posSupplier.get(), Vec3d.ZERO); + } + } + + if (transform) { + w.setBlockState(pos, UBlocks.GOLD_ROOT.getDefaultState().with(CarrotsBlock.AGE, CarrotsBlock.MAX_AGE)); + } + + return 5; + } + } + + if (w.getBlockState(pos).isIn(UTags.UNAFFECTED_BY_GROW_ABILITY)) { return 0; } if (BoneMealItem.useOnFertilizable(stack, w, pos)) { if (w.random.nextInt(350) == 0) { if (w.getBlockState(pos.down()).isOf(Blocks.FARMLAND)) { - w.setBlockState(pos.down(), Blocks.DIRT.getDefaultState()); + FarmlandBlock.setToDirt(null, state, w, pos.down()); } w.setBlockState(pos, UBlocks.PLUNDER_VINE_BUD.getDefaultState()); } else if (w.random.nextInt(5000) == 0) { diff --git a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java index cd8284da..ca6726e9 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java @@ -140,6 +140,12 @@ public interface UBlocks { Block PLUNDER_VINE = register("plunder_vine", new ThornBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).ticksRandomly().sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY), () -> UBlocks.PLUNDER_VINE_BUD)); Block PLUNDER_VINE_BUD = register("plunder_vine_bud", new ThornBudBlock(Settings.create().mapColor(MapColor.DARK_CRIMSON).hardness(1).nonOpaque().ticksRandomly().sounds(BlockSoundGroup.GRASS).pistonBehavior(PistonBehavior.DESTROY), PLUNDER_VINE.getDefaultState())); CuringJokeBlock CURING_JOKE = register("curing_joke", new CuringJokeBlock(UEffects.BUTTER_FINGERS, 7, AbstractBlock.Settings.create().mapColor(MapColor.PALE_PURPLE).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY))); + Block GOLD_ROOT = register("gold_root", new CarrotsBlock(AbstractBlock.Settings.create().mapColor(MapColor.GOLD).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP).pistonBehavior(PistonBehavior.DESTROY)) { + @Override + protected ItemConvertible getSeedsItem() { + return Items.GOLDEN_CARROT; + } + }); Block CHITIN = register("chitin", new SnowyBlock(Settings.create().mapColor(MapColor.PALE_PURPLE).hardness(5).requiresTool().ticksRandomly().sounds(BlockSoundGroup.CORAL)), ItemGroups.NATURAL); Block SURFACE_CHITIN = register("surface_chitin", new GrowableBlock(Settings.copy(CHITIN), () -> CHITIN), ItemGroups.NATURAL); diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/ConsumptionEnchantment.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/ConsumptionEnchantment.java index 547710c0..9976f03b 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/enchantment/ConsumptionEnchantment.java +++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/ConsumptionEnchantment.java @@ -19,7 +19,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; public class ConsumptionEnchantment extends SimpleEnchantment { @@ -44,7 +43,7 @@ public class ConsumptionEnchantment extends SimpleEnchantment { Block.getDroppedStacks(state, world, pos, blockEntity, entity, tool).forEach(s -> { world.playSound(null, pos, USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.BLOCKS, 0.05F, (float)world.random.nextTriangular(0.6F, 0.2F)); - ExperienceOrbEntity.spawn(world, Vec3d.ofCenter(pos).add(VecHelper.supply(vecComponentFactory)), s.getCount()); + ExperienceOrbEntity.spawn(world, pos.toCenterPos().add(VecHelper.supply(vecComponentFactory)), s.getCount()); UCriteria.USE_CONSUMPTION.trigger(entity); }); state.onStacksDropped(world, pos, tool, true); diff --git a/src/main/resources/assets/unicopia/blockstates/gold_root.json b/src/main/resources/assets/unicopia/blockstates/gold_root.json new file mode 100644 index 00000000..6791a03a --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/gold_root.json @@ -0,0 +1,12 @@ +{ + "variants": { + "age=0": { "model": "unicopia:block/gold_root_stage0" }, + "age=1": { "model": "unicopia:block/gold_root_stage0" }, + "age=2": { "model": "unicopia:block/gold_root_stage1" }, + "age=3": { "model": "unicopia:block/gold_root_stage1" }, + "age=4": { "model": "unicopia:block/gold_root_stage2" }, + "age=5": { "model": "unicopia:block/gold_root_stage2" }, + "age=6": { "model": "unicopia:block/gold_root_stage2" }, + "age=7": { "model": "unicopia:block/gold_root_stage3" } + } +} diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index fe458ff4..3dc63351 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -247,6 +247,7 @@ "block.unicopia.apple_pie": "Apple Pie", "block.unicopia.weather_vane": "Weather Vane", "block.unicopia.curing_joke": "Curing Joke", + "block.unicopia.gold_root": "Gold Root", "block.unicopia.mango": "Mango", "block.unicopia.mango_leaves": "Mango Leaves", "block.unicopia.mango_sapling": "Mango Sapling", diff --git a/src/main/resources/assets/unicopia/models/block/gold_root_stage0.json b/src/main/resources/assets/unicopia/models/block/gold_root_stage0.json new file mode 100644 index 00000000..8f0b22cd --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/gold_root_stage0.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/crop", + "textures": { + "crop": "unicopia:block/gold_root_stage0" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/gold_root_stage1.json b/src/main/resources/assets/unicopia/models/block/gold_root_stage1.json new file mode 100644 index 00000000..5315c2b8 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/gold_root_stage1.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/crop", + "textures": { + "crop": "unicopia:block/gold_root_stage1" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/gold_root_stage2.json b/src/main/resources/assets/unicopia/models/block/gold_root_stage2.json new file mode 100644 index 00000000..cf262db5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/gold_root_stage2.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/crop", + "textures": { + "crop": "unicopia:block/gold_root_stage2" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/gold_root_stage3.json b/src/main/resources/assets/unicopia/models/block/gold_root_stage3.json new file mode 100644 index 00000000..790fd135 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/gold_root_stage3.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/crop", + "textures": { + "crop": "unicopia:block/gold_root_stage3" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage0.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage0.png new file mode 100644 index 0000000000000000000000000000000000000000..54ec7e1cc8401d49115084fbf5a2d787c9863620 GIT binary patch literal 4282 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage2.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage2.png new file mode 100644 index 0000000000000000000000000000000000000000..af5f7f914d60e393c3bf1bad1ac7b0507b594d23 GIT binary patch literal 4452 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/data/minecraft/tags/blocks/maintains_farmland.json b/src/main/resources/data/minecraft/tags/blocks/maintains_farmland.json index b8408162..0e142e00 100644 --- a/src/main/resources/data/minecraft/tags/blocks/maintains_farmland.json +++ b/src/main/resources/data/minecraft/tags/blocks/maintains_farmland.json @@ -7,6 +7,7 @@ "unicopia:oats_stem", "unicopia:green_apple_sprout", "unicopia:sweet_apple_sprout", - "unicopia:sour_apple_sprout" + "unicopia:sour_apple_sprout", + "unicopia:gold_root" ] } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json b/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json new file mode 100644 index 00000000..6bfe9e77 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json @@ -0,0 +1,51 @@ +{ + "type": "minecraft:block", + "functions": [ + { + "function": "minecraft:explosion_decay" + } + ], + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:golden_carrot" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "age": "7" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:golden_carrot" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "minecraft:blocks/carrots" +} diff --git a/src/main/resources/data/unicopia/tags/blocks/unaffected_by_grow_ability.json b/src/main/resources/data/unicopia/tags/blocks/unaffected_by_grow_ability.json new file mode 100644 index 00000000..06c918c6 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/blocks/unaffected_by_grow_ability.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "minecraft:grass_block" + ] +} From 0e40c10c437e873eb8e41020593978caa2babd2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A8=AA=E5=88=80=E5=A4=A9=E7=AC=91=EF=BC=88Knife=20smile?= =?UTF-8?q?=EF=BC=89?= <131762804+Cryghast@users.noreply.github.com> Date: Sat, 6 Jan 2024 01:36:19 +0800 Subject: [PATCH 17/98] update zhcn json (#232) * update zhcn json * ignimeous --- src/main/resources/assets/unicopia/lang/zh_cn.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index bdbed780..3df54786 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -114,6 +114,7 @@ "item.unicopia.pebbles": "石籽", "item.unicopia.rock": "石块", "item.unicopia.weird_rock": "怪石块", + "item.unicopia.tom": "Tom", "item.unicopia.rock_stew": "炖石头", "item.unicopia.rock_candy": "石头糖", "item.unicopia.salt_cube": "盐块", @@ -245,6 +246,8 @@ "block.unicopia.palm_hanging_sign": "悬挂式棕榈木告示牌", "block.unicopia.apple_pie": "苹果派", "block.unicopia.weather_vane": "风向标", + "block.unicopia.curing_joke": "疗玩笑", + "block.unicopia.gold_root": "黄金根", "block.unicopia.mango": "芒果", "block.unicopia.mango_leaves": "芒果树叶", "block.unicopia.mango_sapling": "芒果树苗", @@ -306,6 +309,10 @@ "block.unicopia.cloud_bed": "云床", "block.unicopia.cloud_chest": "云中箱", "block.unicopia.cloud_chest.double": "大云中箱", + "block.unicopia.cloud_door": "云中门", + "block.unicopia.crystal_door": "水晶门", + "block.unicopia.stable_door": "马厩门", + "block.unicopia.dark_oak_stable_door": "木马厩门", "block.unicopia.oats": "燕麦", "block.unicopia.oats_stem": "燕麦", @@ -321,6 +328,8 @@ "entity.unicopia.sombra.taunt": "用在我身上不好使!", "entity.unicopia.storm_cloud": "暴风云", "entity.unicopia.crystal_shards": "水晶尖刺", + "entity.unicopia.ignimeous_vine": "无耻藤", + "entity.unicopia.ignimeous_bulb": "无耻球茎", "player.reachDistance": "可触距离", "player.miningSpeed": "挖掘速度", From 1c751aca0e3a5ba1cc9043574b588c0ac125f0e4 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 5 Jan 2024 18:41:20 +0100 Subject: [PATCH 18/98] Rename ignominious bulb and vine --- .../unicopia/block/CuringJokeBlock.java | 4 ++-- .../minelittlepony/unicopia/client/URenderers.java | 2 +- ...yModel.java => IgnominiousBulbEntityModel.java} | 8 ++++---- ...rer.java => IgnominiousBulbEntityRenderer.java} | 14 +++++++------- ...sBulbEntity.java => IgnominiousBulbEntity.java} | 10 +++++----- .../unicopia/entity/mob/UEntities.java | 6 +++--- src/main/resources/assets/unicopia/lang/en_us.json | 4 ++-- src/main/resources/assets/unicopia/lang/zh_cn.json | 4 ++-- 8 files changed, 26 insertions(+), 26 deletions(-) rename src/main/java/com/minelittlepony/unicopia/client/render/entity/{IgnimeousBulbEntityModel.java => IgnominiousBulbEntityModel.java} (92%) rename src/main/java/com/minelittlepony/unicopia/client/render/entity/{IgnimeousBulbEntityRenderer.java => IgnominiousBulbEntityRenderer.java} (66%) rename src/main/java/com/minelittlepony/unicopia/entity/mob/{IgnimeousBulbEntity.java => IgnominiousBulbEntity.java} (94%) diff --git a/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java index 5f37ff82..8118b1d9 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java @@ -1,7 +1,7 @@ package com.minelittlepony.unicopia.block; import com.minelittlepony.unicopia.ability.EarthPonyGrowAbility.Growable; -import com.minelittlepony.unicopia.entity.mob.IgnimeousBulbEntity; +import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity; import com.minelittlepony.unicopia.entity.mob.TentacleEntity; import com.minelittlepony.unicopia.particle.MagicParticleEffect; @@ -34,7 +34,7 @@ public class CuringJokeBlock extends FlowerBlock implements Growable { .toList(); if (otherFlowers.size() >= 8) { - IgnimeousBulbEntity bulb = new IgnimeousBulbEntity(world); + IgnominiousBulbEntity bulb = new IgnominiousBulbEntity(world); bulb.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); if (Dismounting.canPlaceEntityAt(world, bulb, bulb.getBoundingBox())) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 6de73e74..98046073 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -98,7 +98,7 @@ public interface URenderers { EntityRendererRegistry.register(UEntities.FRIENDLY_CREEPER, FriendlyCreeperEntityRenderer::new); EntityRendererRegistry.register(UEntities.LOOT_BUG, LootBugEntityRenderer::new); EntityRendererRegistry.register(UEntities.TENTACLE, TentacleEntityRenderer::new); - EntityRendererRegistry.register(UEntities.IGNIMEOUS_BULB, IgnimeousBulbEntityRenderer::new); + EntityRendererRegistry.register(UEntities.IGNOMINIOUS_BULB, IgnominiousBulbEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.FANCY_BED, CloudBedBlockEntityRenderer::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java similarity index 92% rename from src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityModel.java rename to src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java index 9f5e6e7a..186d2359 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java @@ -1,6 +1,6 @@ package com.minelittlepony.unicopia.client.render.entity; -import com.minelittlepony.unicopia.entity.mob.IgnimeousBulbEntity; +import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity; import net.minecraft.client.model.Dilation; import net.minecraft.client.model.ModelData; @@ -15,14 +15,14 @@ import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.MathHelper; -public class IgnimeousBulbEntityModel extends EntityModel { +public class IgnominiousBulbEntityModel extends EntityModel { private final ModelPart part; private final ModelPart head; private final ModelPart leaves; - public IgnimeousBulbEntityModel(ModelPart root) { + public IgnominiousBulbEntityModel(ModelPart root) { super(RenderLayer::getEntityTranslucent); this.part = root; head = root.getChild("head"); @@ -54,7 +54,7 @@ public class IgnimeousBulbEntityModel extends EntityModel { } @Override - public void setAngles(IgnimeousBulbEntity entity, float limbSwing, float limbSwingAmount, float tickDelta, float yaw, float pitch) { + public void setAngles(IgnominiousBulbEntity entity, float limbSwing, float limbSwingAmount, float tickDelta, float yaw, float pitch) { float age = entity.age + tickDelta; diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityRenderer.java similarity index 66% rename from src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityRenderer.java rename to src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityRenderer.java index 8315cc12..4bcb9484 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnimeousBulbEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityRenderer.java @@ -1,7 +1,7 @@ package com.minelittlepony.unicopia.client.render.entity; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.entity.mob.IgnimeousBulbEntity; +import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity; import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRenderer; @@ -9,19 +9,19 @@ import net.minecraft.client.render.entity.EntityRendererFactory; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; -public class IgnimeousBulbEntityRenderer extends EntityRenderer { +public class IgnominiousBulbEntityRenderer extends EntityRenderer { private static final Identifier IDLE_TEXTURE = Unicopia.id("textures/entity/poison_joke/bulb_idle.png"); private static final Identifier ANGRY_TEXTURE = Unicopia.id("textures/entity/poison_joke/bulb_angry.png"); - private final IgnimeousBulbEntityModel model; + private final IgnominiousBulbEntityModel model; - public IgnimeousBulbEntityRenderer(EntityRendererFactory.Context context) { + public IgnominiousBulbEntityRenderer(EntityRendererFactory.Context context) { super(context); - model = new IgnimeousBulbEntityModel(IgnimeousBulbEntityModel.getTexturedModelData().createModel()); + model = new IgnominiousBulbEntityModel(IgnominiousBulbEntityModel.getTexturedModelData().createModel()); } @Override - public void render(IgnimeousBulbEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + public void render(IgnominiousBulbEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { matrices.push(); matrices.scale(-1, -1, 1); matrices.translate(0, -1.5F, 0); @@ -33,7 +33,7 @@ public class IgnimeousBulbEntityRenderer extends EntityRenderer ANGRY = DataTracker.registerData(IgnimeousBulbEntity.class, TrackedDataHandlerRegistry.BOOLEAN); +public class IgnominiousBulbEntity extends MobEntity { + private static final TrackedData ANGRY = DataTracker.registerData(IgnominiousBulbEntity.class, TrackedDataHandlerRegistry.BOOLEAN); private static final List TENTACLE_OFFSETS = List.of( new BlockPos(-3, 0, -3), new BlockPos(0, 0, -4), new BlockPos(3, 0, -3), new BlockPos(-4, 0, 0), new BlockPos(4, 0, 0), @@ -40,12 +40,12 @@ public class IgnimeousBulbEntity extends MobEntity { @Nullable private Map tentacles; - public IgnimeousBulbEntity(EntityType type, World world) { + public IgnominiousBulbEntity(EntityType type, World world) { super(type, world); } - public IgnimeousBulbEntity(World world) { - super(UEntities.IGNIMEOUS_BULB, world); + public IgnominiousBulbEntity(World world) { + super(UEntities.IGNOMINIOUS_BULB, world); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java index 92a8dab8..49904028 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java @@ -67,10 +67,10 @@ public interface UEntities { EntityType LOOT_BUG = register("loot_bug", FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, LootBugEntity::new) .trackRangeChunks(8) .dimensions(EntityDimensions.fixed(0.8F, 0.6F))); - EntityType TENTACLE = register("ignimeous_vine", FabricEntityTypeBuilder.create(SpawnGroup.MISC, TentacleEntity::new) + EntityType TENTACLE = register("ignominious_vine", FabricEntityTypeBuilder.create(SpawnGroup.MISC, TentacleEntity::new) .trackRangeChunks(8) .dimensions(EntityDimensions.fixed(0.8F, 0.8F))); - EntityType IGNIMEOUS_BULB = register("ignimeous_bulb", FabricEntityTypeBuilder.create(SpawnGroup.MISC, IgnimeousBulbEntity::new) + EntityType IGNOMINIOUS_BULB = register("ignominious_bulb", FabricEntityTypeBuilder.create(SpawnGroup.MISC, IgnominiousBulbEntity::new) .trackRangeChunks(8) .dimensions(EntityDimensions.fixed(3, 2))); @@ -87,7 +87,7 @@ public interface UEntities { FabricDefaultAttributeRegistry.register(SOMBRA, SombraEntity.createMobAttributes()); FabricDefaultAttributeRegistry.register(FRIENDLY_CREEPER, FriendlyCreeperEntity.createCreeperAttributes()); FabricDefaultAttributeRegistry.register(LOOT_BUG, LootBugEntity.createSilverfishAttributes()); - FabricDefaultAttributeRegistry.register(IGNIMEOUS_BULB, IgnimeousBulbEntity.createMobAttributes()); + FabricDefaultAttributeRegistry.register(IGNOMINIOUS_BULB, IgnominiousBulbEntity.createMobAttributes()); if (!Unicopia.getConfig().disableButterflySpawning.get()) { final Predicate butterflySpawnable = BiomeSelectors.foundInOverworld() diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 3dc63351..87ec41e1 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -328,8 +328,8 @@ "entity.unicopia.sombra.taunt": "That's not going to work on me!", "entity.unicopia.storm_cloud": "Storm Cloud", "entity.unicopia.crystal_shards": "Crystal Shards", - "entity.unicopia.ignimeous_vine": "Ignimeous Vine", - "entity.unicopia.ignimeous_bulb": "Ignimeous Bulb", + "entity.unicopia.ignominious_vine": "Ignominious Vine", + "entity.unicopia.ignominious_bulb": "Ignominious Bulb", "player.reachDistance": "Reach Distance", "player.miningSpeed": "Mining Speed", diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index 3df54786..0e5446b8 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -328,8 +328,8 @@ "entity.unicopia.sombra.taunt": "用在我身上不好使!", "entity.unicopia.storm_cloud": "暴风云", "entity.unicopia.crystal_shards": "水晶尖刺", - "entity.unicopia.ignimeous_vine": "无耻藤", - "entity.unicopia.ignimeous_bulb": "无耻球茎", + "entity.unicopia.ignominious_vine": "无耻藤", + "entity.unicopia.ignominious_bulb": "无耻球茎", "player.reachDistance": "可触距离", "player.miningSpeed": "挖掘速度", From ddd217c53d64abcfc1694b841d2058b0e0a5a2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lumi=C3=A8re=20=C3=89lev=C3=A9?= <88174309+PoneyClairDeLune@users.noreply.github.com> Date: Sun, 14 Jan 2024 19:51:46 +0000 Subject: [PATCH 19/98] Dehydration compat --- .../hydration_items/unicopia_food.json | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/resources/data/dehydration/hydration_items/unicopia_food.json diff --git a/src/main/resources/data/dehydration/hydration_items/unicopia_food.json b/src/main/resources/data/dehydration/hydration_items/unicopia_food.json new file mode 100644 index 00000000..119164ea --- /dev/null +++ b/src/main/resources/data/dehydration/hydration_items/unicopia_food.json @@ -0,0 +1,24 @@ +{ + "1": { + "replace": false, + "items": [ + "unicopia:banana", + "unicopia:rotten_apple" + ] + }, + "2": { + "replace": false, + "items": [ + "unicopia:green_apple", + "unicopia:sweet_apple", + "unicopia:sour_apple", + "unicopia:mango" + ] + }, + "3": { + "replace": false, + "items": [ + "unicopia:pineapple" + ] + } +} From facbbe2d269c03ac51c7f19e3f02c29162adad5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lumi=C3=A8re=20=C3=89lev=C3=A9?= <88174309+PoneyClairDeLune@users.noreply.github.com> Date: Sun, 14 Jan 2024 20:06:19 +0000 Subject: [PATCH 20/98] Drink, soup and etc. --- .../hydration_items/unicopia_food.json | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/resources/data/dehydration/hydration_items/unicopia_food.json b/src/main/resources/data/dehydration/hydration_items/unicopia_food.json index 119164ea..e9347cb6 100644 --- a/src/main/resources/data/dehydration/hydration_items/unicopia_food.json +++ b/src/main/resources/data/dehydration/hydration_items/unicopia_food.json @@ -12,13 +12,29 @@ "unicopia:green_apple", "unicopia:sweet_apple", "unicopia:sour_apple", - "unicopia:mango" + "unicopia:mango", + "unicopia:cider", + "unicopia:love_bottle", + "unicopia:love_mug" ] }, "3": { "replace": false, "items": [ - "unicopia:pineapple" + "unicopia:pineapple", + "unicopia:rock_stew" + ] + }, + "4": { + "replace": false, + "items": [ + "unicopia:love_bucket" + ] + }, + "12": { + "replace": false, + "items": [ + "unicopia:juice" ] } } From 0ca19ba07c499a56b81c5eda69395360a85b71d5 Mon Sep 17 00:00:00 2001 From: LingVarr Date: Mon, 15 Jan 2024 23:38:22 +1100 Subject: [PATCH 21/98] Update ru_ru.json --- src/main/resources/assets/unicopia/lang/ru_ru.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/resources/assets/unicopia/lang/ru_ru.json b/src/main/resources/assets/unicopia/lang/ru_ru.json index b151cf02..1e626a2f 100644 --- a/src/main/resources/assets/unicopia/lang/ru_ru.json +++ b/src/main/resources/assets/unicopia/lang/ru_ru.json @@ -246,6 +246,8 @@ "block.unicopia.palm_hanging_sign": "Пальмовая подвесная табличка", "block.unicopia.apple_pie": "Яблочный пирог", "block.unicopia.weather_vane": "Флюгер", + "block.unicopia.curing_joke": "Лечащая шутка", + "block.unicopia.gold_root": "Золотой корень", "block.unicopia.mango": "Манго", "block.unicopia.mango_leaves": "Листья мангового дерева", "block.unicopia.mango_sapling": "Саженец мангового дерева", @@ -307,6 +309,10 @@ "block.unicopia.cloud_bed": "Кровать из облака", "block.unicopia.cloud_chest": "Клаудсдейльский сундук", "block.unicopia.cloud_chest.double": "Большой клаудсдейльский сундук", + "block.unicopia.cloud_door": "Облачная дверь", + "block.unicopia.crystal_door": "Кристальная дверь", + "block.unicopia.stable_door": "Дверь конюшни", + "block.unicopia.dark_oak_stable_door": "Деревянная дверь конюшни", "block.unicopia.oats": "Овёс", "block.unicopia.oats_stem": "Овёс", @@ -322,6 +328,8 @@ "entity.unicopia.sombra.taunt": "На меня это не подействует!", "entity.unicopia.storm_cloud": "Штормовое облако", "entity.unicopia.crystal_shards": "Кристальные осколки", + "entity.unicopia.ignominious_vine": "Позорная лоза", + "entity.unicopia.ignominious_bulb": "Позорная лампочка", "player.reachDistance": "Расстояние до цели", "player.miningSpeed": "Скорость добычи", @@ -832,7 +840,7 @@ "enchantment.unicopia.heavy": "Тяжесть", "enchantment.unicopia.herds": "Стада", "enchantment.unicopia.want_it_need_it": "Хочу это, нужно это", - "enchantment.unicopia.poisoned_joke": "Отравленная шутка", + "enchantment.unicopia.poisoned_joke": "Ядовитая шутка", "enchantment.unicopia.stressed": "Напряжённость", "enchantment.unicopia.heart_bound": "Связанный сердцем", "enchantment.unicopia.consumption": "Потребление", From 144f00c2073aa10f85d3119ea5b2bba137154ddd Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 15 Jan 2024 20:25:07 +0000 Subject: [PATCH 22/98] The Ignominious Bulb now grows from a sprout when first summoned and will no longer lose its arms --- .../unicopia/block/CuringJokeBlock.java | 22 +- .../entity/IgnominiousBulbEntityModel.java | 10 +- .../entity/IgnominiousBulbEntityRenderer.java | 2 +- .../spell/SpellEffectsRenderDispatcher.java | 3 +- .../entity/mob/IgnominiousBulbEntity.java | 202 +++++++++++++++++- .../unicopia/entity/mob/TentacleEntity.java | 14 +- 6 files changed, 224 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java index 8118b1d9..61658a29 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/CuringJokeBlock.java @@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.block; import com.minelittlepony.unicopia.ability.EarthPonyGrowAbility.Growable; import com.minelittlepony.unicopia.entity.mob.IgnominiousBulbEntity; -import com.minelittlepony.unicopia.entity.mob.TentacleEntity; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import net.minecraft.block.BlockState; @@ -33,21 +32,16 @@ public class CuringJokeBlock extends FlowerBlock implements Growable { .map(BlockPos::toImmutable) .toList(); - if (otherFlowers.size() >= 8) { - IgnominiousBulbEntity bulb = new IgnominiousBulbEntity(world); - bulb.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); + IgnominiousBulbEntity bulb = new IgnominiousBulbEntity(world); + bulb.setBaby(true); + bulb.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); - if (Dismounting.canPlaceEntityAt(world, bulb, bulb.getBoundingBox())) { - otherFlowers.forEach(p -> world.removeBlock(p, false)); - world.spawnEntity(bulb); - return true; - } + if (Dismounting.canPlaceEntityAt(world, bulb, bulb.getBoundingBox())) { + otherFlowers.forEach(p -> world.breakBlock(p, false)); + world.spawnEntity(bulb); + return true; } - world.removeBlock(pos, false); - TentacleEntity tentacle = new TentacleEntity(world, pos); - tentacle.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 0, 0); - world.spawnEntity(tentacle); - return true; + return false; } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java index 186d2359..b8bb4c20 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/IgnominiousBulbEntityModel.java @@ -57,6 +57,13 @@ public class IgnominiousBulbEntityModel extends EntityModel ANGRY = DataTracker.registerData(IgnominiousBulbEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final TrackedData AGE = DataTracker.registerData(IgnominiousBulbEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final int BABY_AGE = PassiveEntity.BABY_AGE; + private static final int HAPPY_TICKS = 40; private static final List TENTACLE_OFFSETS = List.of( new BlockPos(-3, 0, -3), new BlockPos(0, 0, -4), new BlockPos(3, 0, -3), new BlockPos(-4, 0, 0), new BlockPos(4, 0, 0), @@ -40,6 +60,10 @@ public class IgnominiousBulbEntity extends MobEntity { @Nullable private Map tentacles; + private int prevAge; + private int happyTicks; + private int angryTicks; + public IgnominiousBulbEntity(EntityType type, World world) { super(type, world); } @@ -52,6 +76,29 @@ public class IgnominiousBulbEntity extends MobEntity { protected void initDataTracker() { super.initDataTracker(); dataTracker.startTracking(ANGRY, false); + dataTracker.startTracking(AGE, 0); + } + + @Override + public boolean isBaby() { + return getAge() < 0; + } + + @Override + public void setBaby(boolean baby) { + setAge(BABY_AGE); + } + + protected int getAge() { + return dataTracker.get(AGE); + } + + protected void setAge(int age) { + dataTracker.set(AGE, age); + } + + public float getScale(float tickDelta) { + return Math.max(0.2F, 1 - (MathHelper.clamp(MathHelper.lerp(tickDelta, prevAge, getAge()), BABY_AGE, 0F) / BABY_AGE)); } public boolean isAngry() { @@ -59,7 +106,56 @@ public class IgnominiousBulbEntity extends MobEntity { } public void setAngry(boolean angry) { - dataTracker.set(ANGRY, angry); + if (angry != isAngry()) { + dataTracker.set(ANGRY, angry); + } + } + + public void setAngryFor(int angryTicks) { + this.angryTicks = angryTicks; + } + + @Override + protected ActionResult interactMob(PlayerEntity player, Hand hand) { + + ItemStack stack = player.getStackInHand(hand); + if (isBaby() && stack.isOf(Items.BONE_MEAL)) { + if (!player.isCreative()) { + stack.decrement(1); + } + growUp(10); + if (!getWorld().isClient) { + getWorld().syncWorldEvent(WorldEvents.BONE_MEAL_USED, getBlockPos(), 0); + } + return ActionResult.SUCCESS; + } + + return ActionResult.PASS; + } + + @Override + protected void onPlayerSpawnedChild(PlayerEntity player, MobEntity child) { + Vec3d center = getPos(); + Supplier offset = VecHelper.supplier(() -> (getWorld().random.nextBoolean() ? 1 : -1) * 3); + setPosition(center.add(offset.get().multiply(1, 0, 1))); + child.setPosition(center.add(offset.get().multiply(1, 0, 1))); + } + + @Override + public void setPosition(double x, double y, double z) { + Vec3d change = new Vec3d(x, y, z).subtract(getPos()); + super.setPosition(x, y, z); + getTentacles().values().forEach(tentacle -> { + tentacle.setPosition(tentacle.getPos().add(change)); + }); + } + + public void growUp(int age) { + int currentAge = getAge(); + if (currentAge < 0) { + setAge((age * 20) + currentAge); + happyTicks = HAPPY_TICKS; + } } @Override @@ -68,20 +164,27 @@ public class IgnominiousBulbEntity extends MobEntity { var center = new BlockPos.Mutable(); var tentacles = getTentacles(); - TENTACLE_OFFSETS.forEach(offset -> { - tentacles.compute(adjustForTerrain(center, offset), this::updateTentacle); - }); + if (!isBaby() && !firstUpdate) { + TENTACLE_OFFSETS.forEach(offset -> { + tentacles.compute(adjustForTerrain(center, offset), this::updateTentacle); + }); + } - if (getWorld().random.nextInt(isAngry() ? 12 : 1200) == 0) { - for (TentacleEntity tentacle : tentacles.values()) { + for (TentacleEntity tentacle : tentacles.values()) { + if (getWorld().random.nextInt(isAngry() ? 12 : 1200) == 0) { tentacle.addActiveTicks(120); } } LivingEntity target = getAttacker(); - setAngry(target != null); + if (angryTicks > 0) { + angryTicks--; + } - if (isAngry() && getWorld().random.nextInt(30) == 0) { + setAngry(!isBaby() && (angryTicks > 0 || target != null)); + + float healthPercentage = getHealth() / getMaxHealth(); + if (isAngry() && target != null && getWorld().random.nextInt(1 + (int)(healthPercentage * 30)) == 0) { if (target instanceof PlayerEntity player) { Pony.of(player).getMagicalReserves().getEnergy().add(6); } @@ -94,16 +197,41 @@ public class IgnominiousBulbEntity extends MobEntity { tentacle.setTarget(target); }); } + + if (target != null) { + lookAtEntity(target, 10, 10); + } } super.tick(); } + @Override + public void tickMovement() { + super.tickMovement(); + prevAge = getAge(); + + if (getWorld().isClient) { + if (happyTicks > 0 && --happyTicks % 4 == 0) { + getWorld().addParticle(ParticleTypes.HAPPY_VILLAGER, getParticleX(1), getRandomBodyY() + 0.5, getParticleZ(1), 0, 0, 0); + } + } else { + if (prevAge < 0) { + setAge(prevAge + 1); + } else if (prevAge > 0) { + setAge(prevAge - 1); + } + } + } + private Map getTentacles() { if (tentacles == null) { tentacles = getWorld().getEntitiesByClass(TentacleEntity.class, this.getBoundingBox().expand(5, 0, 5), EntityPredicates.VALID_ENTITY) .stream() - .collect(Collectors.toMap(TentacleEntity::getBlockPos, Function.identity())); + .collect(Collectors.toMap(TentacleEntity::getBlockPos, tentacle -> { + tentacle.setBulb(this); + return tentacle; + })); } return tentacles; } @@ -112,7 +240,11 @@ public class IgnominiousBulbEntity extends MobEntity { if (tentacle == null || tentacle.isRemoved()) { tentacle = new TentacleEntity(getWorld(), pos); tentacle.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, getWorld().random.nextFloat() * 360, 0); - getWorld().spawnEntity(tentacle); + tentacle.setBulb(this); + + if (getWorld().isTopSolid(pos.down(), this)) { + getWorld().spawnEntity(tentacle); + } } return tentacle; } @@ -157,6 +289,9 @@ public class IgnominiousBulbEntity extends MobEntity { @Override @Nullable protected SoundEvent getHurtSound(DamageSource source) { + if (isBaby()) { + return SoundEvents.ENTITY_HORSE_BREATHE; + } return USounds.ENTITY_IGNIMEOUS_BULB_HURT; } @@ -166,15 +301,60 @@ public class IgnominiousBulbEntity extends MobEntity { return USounds.ENTITY_IGNIMEOUS_BULB_DEATH; } + @Override + @Nullable + protected SoundEvent getAmbientSound() { + if (!isBaby() && getWorld().random.nextInt(2) == 0) { + return SoundEvents.ENTITY_CAMEL_AMBIENT; + } + return SoundEvents.ITEM_BONE_MEAL_USE; + } + @Override public void writeCustomDataToNbt(NbtCompound nbt) { super.writeCustomDataToNbt(nbt); nbt.putBoolean("angry", isAngry()); + nbt.putInt("age", getAge()); + NbtList tentacles = new NbtList(); + getTentacles().forEach((pos, tentacle) -> { + var compound = new NbtCompound(); + compound.put("pos", NbtSerialisable.BLOCK_POS.write(pos)); + compound.putUuid("uuid", tentacle.getUuid()); + tentacles.add(compound); + }); + nbt.put("tentacles", tentacles); } @Override public void readCustomDataFromNbt(NbtCompound nbt) { super.readCustomDataFromNbt(nbt); setAngry(nbt.getBoolean("angry")); + setAge(nbt.getInt("age")); + if (!getWorld().isClient) { + if (nbt.contains("tentacles", NbtElement.LIST_TYPE)) { + var tentacles = new HashMap(); + nbt.getList("tentacles", NbtElement.COMPOUND_TYPE).forEach(tag -> { + var compound = (NbtCompound)tag; + if (((ServerWorld)getWorld()).getEntity(compound.getUuid("uuid")) instanceof TentacleEntity tentacle) { + tentacle.setBulb(this); + tentacles.put(NbtSerialisable.BLOCK_POS.read(compound.getCompound("pos")), tentacle); + } + }); + this.tentacles = tentacles; + } + } + } + + @Override + public void onTrackedDataSet(TrackedData data) { + if (AGE.equals(data)) { + calculateDimensions(); + } + super.onTrackedDataSet(data); + } + + @Override + public EntityDimensions getDimensions(EntityPose pose) { + return EntityDimensions.changing(3, 2).scaled(getScale(1)); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java index d20eb3a7..16d4a459 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/TentacleEntity.java @@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.util.shape.Sphere; +import net.minecraft.command.argument.EntityAnchorArgumentType.EntityAnchor; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; @@ -55,6 +56,9 @@ public class TentacleEntity extends AbstractDecorationEntity { private LivingEntity target; private final Comparator targetSorting = Comparator.comparing(this::distanceTo); + @Nullable + private IgnominiousBulbEntity bulb; + public TentacleEntity(EntityType type, World world) { super(type, world); } @@ -70,6 +74,10 @@ public class TentacleEntity extends AbstractDecorationEntity { dataTracker.startTracking(MOTION_OFFSET, 0); } + public void setBulb(IgnominiousBulbEntity bulb) { + this.bulb = bulb; + } + public void attack(BlockPos pos) { var offset = pos.toCenterPos().subtract(getBlockPos().toCenterPos()); @@ -82,6 +90,10 @@ public class TentacleEntity extends AbstractDecorationEntity { setYaw(MathHelper.wrapDegrees((float)(MathHelper.atan2(dZ, dX) * MathHelper.DEGREES_PER_RADIAN) - 90)); getWorld().sendEntityStatus(this, ATTACK_STATUS); attackingTicks = 30; + if (bulb != null) { + bulb.setAngryFor(10); + bulb.lookAt(EntityAnchor.FEET, pos.toCenterPos()); + } } public float getAttackProgress(float tickDelta) { @@ -144,7 +156,7 @@ public class TentacleEntity extends AbstractDecorationEntity { } public void addActiveTicks(int ticks) { - ticksActive += ticks; + ticksActive = Math.min(200, ticksActive + ticks); } @Override From 117c68c8c6fa148fd8474df604e0d966cac92b6d Mon Sep 17 00:00:00 2001 From: Cryghast Date: Tue, 16 Jan 2024 08:18:57 +0800 Subject: [PATCH 23/98] update zh.cn.json --- .../resources/assets/unicopia/lang/zh_cn.json | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index 0e5446b8..d03c9fd3 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -151,9 +151,9 @@ "item.unicopia.clam_shell": "蛤蜊壳", "item.unicopia.scallop_shell": "扇贝壳", "item.unicopia.turret_shell": "锥螺壳", - "item.unicopia.shelly": "壳儿", + "item.unicopia.shelly": "贝利", - "item.unicopia.horse_shoe.accuracy": "精准度%d%%", + "item.unicopia.horse_shoe.accuracy": "精准度%:d%%", "item.unicopia.horse_shoe.speed": "速度:%d", "item.unicopia.iron_horse_shoe": "铁蹄铁", "item.unicopia.golden_horse_shoe": "金蹄铁", @@ -189,30 +189,30 @@ "item.unicopia.music_disc_funk.desc": "funk, just funk", "item.unicopia.cloud_lump": "云团", - "item.unicopia.light_gray_bed_sheets": "淡灰色床单", - "item.unicopia.gray_bed_sheets": "灰色床单", - "item.unicopia.black_bed_sheets": "黑色床单", - "item.unicopia.brown_bed_sheets": "棕色床单", - "item.unicopia.red_bed_sheets": "红色床单", - "item.unicopia.orange_bed_sheets": "橙色床单", - "item.unicopia.yellow_bed_sheets": "黄色床单", - "item.unicopia.lime_bed_sheets": "黄绿色床单", - "item.unicopia.green_bed_sheets": "绿色床单", - "item.unicopia.cyan_bed_sheets": "青色床单", - "item.unicopia.light_blue_bed_sheets": "淡蓝色床单", - "item.unicopia.blue_bed_sheets": "蓝色床单", - "item.unicopia.purple_bed_sheets": "紫色床单", - "item.unicopia.magenta_bed_sheets": "品红色床单", - "item.unicopia.pink_bed_sheets": "粉色床单", - "item.unicopia.apple_bed_sheets": "苹果纹饰床单", - "item.unicopia.barred_bed_sheets": "条纹床单", - "item.unicopia.checkered_bed_sheets": "棋盘格床单", - "item.unicopia.kelp_bed_sheets": "海藻纹饰床单", - "item.unicopia.rainbow_bed_sheets": "彩虹纹饰床单", - "item.unicopia.rainbow_bpw_bed_sheets": "BPW 彩虹纹饰床单", - "item.unicopia.rainbow_bpy_bed_sheets": "BPY 彩虹纹饰床单", - "item.unicopia.rainbow_pbg_bed_sheets": "PGB 彩虹纹饰床单", - "item.unicopia.rainbow_pwr_bed_sheets": "PWR 彩虹纹饰床单", + "item.unicopia.light_gray_bed_sheets": "淡灰色被单", + "item.unicopia.gray_bed_sheets": "灰色被单", + "item.unicopia.black_bed_sheets": "黑色被单", + "item.unicopia.brown_bed_sheets": "棕色被单", + "item.unicopia.red_bed_sheets": "红色被单", + "item.unicopia.orange_bed_sheets": "橙色被单", + "item.unicopia.yellow_bed_sheets": "黄色被单", + "item.unicopia.lime_bed_sheets": "黄绿色被单", + "item.unicopia.green_bed_sheets": "绿色被单", + "item.unicopia.cyan_bed_sheets": "青色被单", + "item.unicopia.light_blue_bed_sheets": "淡蓝色被单", + "item.unicopia.blue_bed_sheets": "蓝色被单", + "item.unicopia.purple_bed_sheets": "紫色被单", + "item.unicopia.magenta_bed_sheets": "品红色被单", + "item.unicopia.pink_bed_sheets": "粉色被单", + "item.unicopia.apple_bed_sheets": "苹果纹饰被单", + "item.unicopia.barred_bed_sheets": "条纹被单", + "item.unicopia.checkered_bed_sheets": "棋盘格被单", + "item.unicopia.kelp_bed_sheets": "海藻纹饰被单", + "item.unicopia.rainbow_bed_sheets": "彩虹纹饰被单", + "item.unicopia.rainbow_bpw_bed_sheets": "跨性 彩虹纹饰被单", + "item.unicopia.rainbow_bpy_bed_sheets": "泛性 彩虹纹饰被单", + "item.unicopia.rainbow_pbg_bed_sheets": "无性 彩虹纹饰被单", + "item.unicopia.rainbow_pwr_bed_sheets": "女性 彩虹纹饰被单", "block.unicopia.rocks": "一些石块", "block.unicopia.plunder_vine": "掠夺之藤", @@ -483,9 +483,9 @@ "trait.unicopia.poison.name": "毒性", "trait.unicopia.poison.description": "一枚绝命毒镖能轻松放倒一头巨兽。", - "unicopia.diet.information": "饮食信息:", + "unicopia.diet.information": "信饮食息:", "unicopia.diet.side_effects": "副作用:", - "unicopia.diet.not_edible": "物品不可食用", + "unicopia.diet.not_edible": "吃不了", "unicopia.diet.base_multiplier": "基础倍率: %s%%", "unicopia.diet.hunger.detailed": "获取饥饿值: %s of %s (%s%%)", "unicopia.diet.saturation.detailed": "获取饱和度: %s (%s%%)", @@ -625,7 +625,7 @@ "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.earth": " - 拥有强大的击,踢和踏力,可用于防御或向四周出击", "gui.unicopia.tribe_selection.confirm.goods.5.unicopia.earth": " - 唯一能和苦力怕拥抱而不会被炸死的种族", "gui.unicopia.tribe_selection.confirm.goods.6.unicopia.earth": " - 可以吃石头", - "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.earth": " - 可爱软乎的小马耳朵", + "gui.unicopia.tribe_selection.confirm.goods.7.unicopia.earth": " - 可爱软乎的小马耳朵", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.unicorn": " - 瞬移和魔法咒语", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.unicorn": " - 研究和制作增强自身能力的魔法神器", @@ -636,7 +636,7 @@ "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.pegasus": " - 使用储存的魔力在短时间内冲刺或蓄力做出彩虹音爆", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.pegasus": " - 移动速度更快,摔落伤害更低", "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.pegasus": " - 可以直接和云与云类物品互动", - "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.pegasus": " - 可以吃蔬菜和某些鱼", + "gui.unicopia.tribe_selection.confirm.goods.5.unicopia.pegasus": " - 可以吃蔬菜和某些鱼", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.bat": " - 能够飞行,能通过训练提高耐力", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.bat": " - 夜间看得更清楚", @@ -646,7 +646,7 @@ "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.changeling": " - 能够在原地飞行和悬停", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.changeling": " - 几乎能变形成任何生物或任何物体", "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.changeling": " - 通过爬墙可越过任何任何物体", - "gui.unicopia.tribe_selection.confirm.goods.3.unicopia.changeling": " - 是肉食性的。可以吃任何不会让它们生病的东西", + "gui.unicopia.tribe_selection.confirm.goods.4.unicopia.changeling": " - 是肉食性的。可以吃任何不会让它们生病的东西", "gui.unicopia.tribe_selection.confirm.goods.1.unicopia.kirin": " - 免疫一切火焰伤害", "gui.unicopia.tribe_selection.confirm.goods.2.unicopia.kirin": " - 受到伤害后可以发怒", From f56855246275915aabceb7b12f599746dca99573 Mon Sep 17 00:00:00 2001 From: Cryghast Date: Tue, 16 Jan 2024 08:20:59 +0800 Subject: [PATCH 24/98] update zh_cn.json --- src/main/resources/assets/unicopia/lang/zh_cn.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index d03c9fd3..b8525bd7 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -483,7 +483,7 @@ "trait.unicopia.poison.name": "毒性", "trait.unicopia.poison.description": "一枚绝命毒镖能轻松放倒一头巨兽。", - "unicopia.diet.information": "信饮食息:", + "unicopia.diet.information": "食物情况:", "unicopia.diet.side_effects": "副作用:", "unicopia.diet.not_edible": "吃不了", "unicopia.diet.base_multiplier": "基础倍率: %s%%", From 4e9966055f2e313e46162a992033c32fb5e2a8fe Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 17 Jan 2024 18:47:22 +0000 Subject: [PATCH 25/98] Fixed flowering zap leaves not recognisde as leaves --- src/main/resources/data/minecraft/tags/blocks/leaves.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/data/minecraft/tags/blocks/leaves.json b/src/main/resources/data/minecraft/tags/blocks/leaves.json index 852058e8..8221c009 100644 --- a/src/main/resources/data/minecraft/tags/blocks/leaves.json +++ b/src/main/resources/data/minecraft/tags/blocks/leaves.json @@ -3,6 +3,7 @@ "values": [ "unicopia:palm_leaves", "unicopia:zap_leaves", + "unicopia:flowering_zap_leaves", "unicopia:green_apple_leaves", "unicopia:sweet_apple_leaves", "unicopia:sour_apple_leaves", From 5867e987f3c27dbeb730b32967a50485660902a8 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 17 Jan 2024 18:50:07 +0000 Subject: [PATCH 26/98] Fix leaves not mining with hoes. Closes #235 --- .../data/minecraft/tags/blocks/mineable/hoe.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json diff --git a/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json b/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json new file mode 100644 index 00000000..8221c009 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json @@ -0,0 +1,12 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_leaves", + "unicopia:zap_leaves", + "unicopia:flowering_zap_leaves", + "unicopia:green_apple_leaves", + "unicopia:sweet_apple_leaves", + "unicopia:sour_apple_leaves", + "unicopia:mango_leaves" + ] +} From f3ab86ed74236bd58b9eab12894891c08e88c859 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 17 Jan 2024 19:13:17 +0000 Subject: [PATCH 27/98] Fix palm log/stripped log and wood recipes --- .../data/unicopia/recipes/palm_planks.json | 4 +++- .../data/unicopia/recipes/palm_wood.json | 3 +++ .../unicopia/recipes/stripped_palm_wood.json | 18 ++++++++++++++++++ .../data/unicopia/tags/blocks/palm_logs.json | 9 +++++++++ .../data/unicopia/tags/items/palm_logs.json | 9 +++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/data/unicopia/recipes/stripped_palm_wood.json create mode 100644 src/main/resources/data/unicopia/tags/blocks/palm_logs.json create mode 100644 src/main/resources/data/unicopia/tags/items/palm_logs.json diff --git a/src/main/resources/data/unicopia/recipes/palm_planks.json b/src/main/resources/data/unicopia/recipes/palm_planks.json index c8a7dea1..2e94f502 100644 --- a/src/main/resources/data/unicopia/recipes/palm_planks.json +++ b/src/main/resources/data/unicopia/recipes/palm_planks.json @@ -1,7 +1,9 @@ { "type": "minecraft:crafting_shapeless", + "category": "building", + "group": "planks", "ingredients": [ - { "item": "unicopia:palm_log" } + { "tag": "unicopia:palm_logs" } ], "result": { "item": "unicopia:palm_planks", diff --git a/src/main/resources/data/unicopia/recipes/palm_wood.json b/src/main/resources/data/unicopia/recipes/palm_wood.json index 0c28e123..2d4c9008 100644 --- a/src/main/resources/data/unicopia/recipes/palm_wood.json +++ b/src/main/resources/data/unicopia/recipes/palm_wood.json @@ -1,5 +1,7 @@ { "type": "minecraft:crafting_shaped", + "category": "building", + "group": "bark", "pattern": [ "##", "##" @@ -10,6 +12,7 @@ } }, "result": { + "count": 3, "item": "unicopia:palm_wood" } } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/stripped_palm_wood.json b/src/main/resources/data/unicopia/recipes/stripped_palm_wood.json new file mode 100644 index 00000000..1b4f818e --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/stripped_palm_wood.json @@ -0,0 +1,18 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "building", + "group": "bark", + "pattern": [ + "##", + "##" + ], + "key": { + "#": { + "item": "unicopia:stripped_palm_log" + } + }, + "result": { + "count": 3, + "item": "unicopia:stripped_palm_wood" + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/tags/blocks/palm_logs.json b/src/main/resources/data/unicopia/tags/blocks/palm_logs.json new file mode 100644 index 00000000..38397972 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/blocks/palm_logs.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_log", + "unicopia:palm_wood", + "unicopia:stripped_palm_log", + "unicopia:stripped_palm_wood" + ] +} diff --git a/src/main/resources/data/unicopia/tags/items/palm_logs.json b/src/main/resources/data/unicopia/tags/items/palm_logs.json new file mode 100644 index 00000000..38397972 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/palm_logs.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_log", + "unicopia:palm_wood", + "unicopia:stripped_palm_log", + "unicopia:stripped_palm_wood" + ] +} From 54381cf67348b5ca131e1473e1443aa93a902b89 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 17 Jan 2024 19:18:56 +0000 Subject: [PATCH 28/98] Fruit trees now consider the light level instead of time of day when trying to grow. Fixes #237 --- .../com/minelittlepony/unicopia/block/FruitBearingBlock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java b/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java index ee2eb14d..8faa118a 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java @@ -70,7 +70,7 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka return; } - if (world.isDay()) { + if (world.getBaseLightLevel(pos, 0) > 8) { BlockSoundGroup group = getSoundGroup(state); int steps = FertilizableUtil.getGrowthSteps(world, pos, state, random); while (steps-- > 0) { From 5ac68a3df9ff16b8f96c4d03b3a49376dd0ffadf Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 17 Jan 2024 19:28:27 +0000 Subject: [PATCH 29/98] Fixed race change advancement criterion. Fixes #230 --- .../unicopia/entity/player/Pony.java | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 6a12cabe..666ca9c2 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -239,8 +239,7 @@ public class Pony extends Living implements Copyable, Update gravity.updateFlightState(); entity.sendAbilitiesUpdate(); - - UCriteria.PLAYER_CHANGE_RACE.trigger(entity); + recalculateCompositeRace(); } public void setSuppressedRace(Race race) { @@ -377,20 +376,7 @@ public class Pony extends Living implements Copyable, Update @Override public boolean beforeUpdate() { if (compositeRace.includes(Race.UNSET) || entity.age % 2 == 0) { - Race intrinsicRace = getSpecies(); - Race suppressedRace = getSuppressedRace(); - compositeRace = getSpellSlot() - .get(SpellPredicate.IS_MIMIC, true) - .map(AbstractDisguiseSpell::getDisguise) - .map(EntityAppearance::getAppearance) - .flatMap(Pony::of) - .map(Pony::getSpecies) - .orElse(intrinsicRace).composite( - AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN - : AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN - : null, - AmuletSelectors.PEARL_NECKLACE.test(entity) ? suppressedRace.or(Race.SEAPONY) : null - ); + recalculateCompositeRace(); } if (ticksInvulnerable > 0) { @@ -482,6 +468,24 @@ public class Pony extends Living implements Copyable, Update return super.beforeUpdate(); } + private void recalculateCompositeRace() { + Race intrinsicRace = getSpecies(); + Race suppressedRace = getSuppressedRace(); + compositeRace = getSpellSlot() + .get(SpellPredicate.IS_MIMIC, true) + .map(AbstractDisguiseSpell::getDisguise) + .map(EntityAppearance::getAppearance) + .flatMap(Pony::of) + .map(Pony::getSpecies) + .orElse(intrinsicRace).composite( + AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN + : AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN + : null, + AmuletSelectors.PEARL_NECKLACE.test(entity) ? suppressedRace.or(Race.SEAPONY) : null + ); + UCriteria.PLAYER_CHANGE_RACE.trigger(entity); + } + @Override public Optional chooseClimbingPos() { if (getObservedSpecies() == Race.CHANGELING && getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).isEmpty()) { From 0c268277909d47f5cdbbfd8fbd5df0677ada0577 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 17 Jan 2024 19:48:35 +0000 Subject: [PATCH 30/98] Add advancement for killing mobs with a horseshoe --- .../unicopia/entity/damage/UDamageTypes.java | 1 + .../unicopia/item/HorseShoeItem.java | 2 ++ .../PhysicsBodyProjectileEntity.java | 21 ++++++++++- .../resources/assets/unicopia/lang/en_us.json | 8 ++++- .../unicopia/earth/dead_ringer.json | 36 +++++++++++++++++++ .../unicopia/earth/sticks_and_stones.json | 12 +++---- .../tags/damage_type/from_horseshoes.json | 6 ++++ 7 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json create mode 100644 src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json diff --git a/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java b/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java index 737f7775..b28faeea 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java @@ -30,6 +30,7 @@ public interface UDamageTypes { RegistryKey SUNLIGHT = register("sunlight"); RegistryKey PETRIFIED = register("petrified"); RegistryKey ROCK = register("rock"); + RegistryKey HORSESHOE = register("horseshoe"); private static RegistryKey register(String name) { var key = RegistryKey.of(RegistryKeys.DAMAGE_TYPE, Unicopia.id(name)); diff --git a/src/main/java/com/minelittlepony/unicopia/item/HorseShoeItem.java b/src/main/java/com/minelittlepony/unicopia/item/HorseShoeItem.java index a7899e8d..a9ed6129 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/HorseShoeItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/HorseShoeItem.java @@ -9,6 +9,7 @@ import com.google.common.collect.Multimap; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.projectile.PhysicsBodyProjectileEntity; @@ -75,6 +76,7 @@ public class HorseShoeItem extends HeavyProjectileItem { @Override protected PhysicsBodyProjectileEntity createProjectile(ItemStack stack, World world, @Nullable PlayerEntity player) { PhysicsBodyProjectileEntity projectile = super.createProjectile(stack, world, player); + projectile.setDamageType(UDamageTypes.HORSESHOE); float degradation = (stack.getDamage() / (float)stack.getMaxDamage()); diff --git a/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java b/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java index 13d49ce9..531f670e 100644 --- a/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.projectile; +import java.util.Optional; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; @@ -19,6 +21,7 @@ import net.minecraft.entity.LivingEntity; import net.minecraft.entity.MovementType; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSources; +import net.minecraft.entity.damage.DamageType; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; import net.minecraft.entity.data.TrackedDataHandlerRegistry; @@ -27,9 +30,13 @@ import net.minecraft.entity.projectile.PersistentProjectileEntity; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.BlockTags; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; +import net.minecraft.util.Identifier; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.math.BlockPos; @@ -46,6 +53,8 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl private int inWaterTime; + private RegistryKey damageType = UDamageTypes.ROCK; + public PhysicsBodyProjectileEntity(EntityType type, World world) { super(type, world); } @@ -74,6 +83,10 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl return getDataTracker().get(ITEM); } + public void setDamageType(RegistryKey damageType) { + this.damageType = damageType; + } + @Override protected ItemStack asItemStack() { return getStack(); @@ -149,7 +162,7 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl return new DamageSources(getWorld().getRegistryManager()) { @Override public DamageSource arrow(PersistentProjectileEntity source, @Nullable Entity attacker) { - return create(UDamageTypes.ROCK, source, attacker); + return create(damageType, source, attacker); } }; } @@ -262,11 +275,17 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl if (!stack.isEmpty()) { nbt.put("Item", stack.writeNbt(new NbtCompound())); } + nbt.putString("damageType", damageType.getValue().toString()); } @Override public void readCustomDataFromNbt(NbtCompound nbt) { super.readCustomDataFromNbt(nbt); setStack(ItemStack.fromNbt(nbt.getCompound("Item"))); + if (nbt.contains("damageType", NbtElement.STRING_TYPE)) { + Optional.ofNullable(Identifier.tryParse(nbt.getString("damageType"))).ifPresent(id -> { + setDamageType(RegistryKey.of(RegistryKeys.DAMAGE_TYPE, id)); + }); + } } } diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 87ec41e1..97196487 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -993,6 +993,10 @@ "death.attack.unicopia.rock.self": "%1$s was pummeled", "death.attack.unicopia.rock.item": "%1$s was pummelled by %2$s using %3$s", "death.attack.unicopia.rock.player": "%1$s was pummelled by %2$s", + "death.attack.unicopia.horseshoe": "%1$s went ding", + "death.attack.unicopia.horseshoe.self": "%1$s dinged himself", + "death.attack.unicopia.horseshoe.item": "%1$s was dinged by %2$s using %3$s", + "death.attack.unicopia.horseshoe.player": "%1$s was dinged by %2$s", "death.fell.accident.ladder.pegasus": "%1$s forgot they could fly and fell off a ladder", "death.fell.accident.vines.pegasus": "%1$s forgot they could fly and fell off some vines", @@ -1158,7 +1162,9 @@ "advancements.unicopia.earth_route.title": "Path of the Pony", "advancements.unicopia.earth_route.description": "Join the Apple Clan", "advancements.unicopia.sticks_and_stones.title": "Sticks and Stones", - "advancements.unicopia.sticks_and_stones.description": "Kill an mob by throwing rocks at it", + "advancements.unicopia.sticks_and_stones.description": "Kill a mob by throwing rocks at it", + "advancements.unicopia.dead_ringer.title": "Dead Ringer", + "advancements.unicopia.dead_ringer.description": "Kill a mob with a horseshoe", "advancements.unicopia.born_on_a_rock_farm.title": "Born on a Rock Farm", "advancements.unicopia.born_on_a_rock_farm.description": "Successfully farm your first rock", "advancements.unicopia.thats_unusual.title": "That's Unusual", diff --git a/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json b/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json new file mode 100644 index 00000000..881e8c8d --- /dev/null +++ b/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json @@ -0,0 +1,36 @@ +{ + "parent": "unicopia:unicopia/earth/born_on_a_rock_farm", + "display": { + "icon": { + "item": "unicopia:rock" + }, + "title": { + "translate": "advancements.unicopia.sticks_and_stones.title" + }, + "description": { + "translate": "advancements.unicopia.sticks_and_stones.description" + }, + "frame": "task", + "show_toast": true, + "announce_to_chat": true, + "hidden": true + }, + "criteria": { + "killed_entity_with_rock": { + "trigger": "minecraft:player_killed_entity", + "conditions": { + "killing_blow": { + "tags": [ + { + "id": "unicopia:from_rocks", + "expected": true + } + ] + } + } + } + }, + "requirements": [ + [ "killed_entity_with_rock" ] + ] +} diff --git a/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json b/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json index 881e8c8d..591e4616 100644 --- a/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json +++ b/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json @@ -2,13 +2,13 @@ "parent": "unicopia:unicopia/earth/born_on_a_rock_farm", "display": { "icon": { - "item": "unicopia:rock" + "item": "unicopia:horseshoe" }, "title": { - "translate": "advancements.unicopia.sticks_and_stones.title" + "translate": "advancements.unicopia.dead_ringer.title" }, "description": { - "translate": "advancements.unicopia.sticks_and_stones.description" + "translate": "advancements.unicopia.dead_ringer.description" }, "frame": "task", "show_toast": true, @@ -16,13 +16,13 @@ "hidden": true }, "criteria": { - "killed_entity_with_rock": { + "killed_entity_with_horseshoe": { "trigger": "minecraft:player_killed_entity", "conditions": { "killing_blow": { "tags": [ { - "id": "unicopia:from_rocks", + "id": "unicopia:from_horseshoes", "expected": true } ] @@ -31,6 +31,6 @@ } }, "requirements": [ - [ "killed_entity_with_rock" ] + [ "killed_entity_with_horseshoe" ] ] } diff --git a/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json b/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json new file mode 100644 index 00000000..276d2073 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:horseshoe" + ] +} From dde64153ddcf1eff4af33a5fd89e6d98a5244389 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 18 Jan 2024 16:24:02 +0000 Subject: [PATCH 31/98] Fixed bulb duplicating arms when reloading a world --- .../entity/mob/IgnominiousBulbEntity.java | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnominiousBulbEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnominiousBulbEntity.java index 659e43e3..224a2b0f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnominiousBulbEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/IgnominiousBulbEntity.java @@ -10,11 +10,13 @@ import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.VecHelper; import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityDimensions; import net.minecraft.entity.EntityPose; import net.minecraft.entity.EntityType; @@ -34,7 +36,6 @@ import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtList; import net.minecraft.particle.ParticleTypes; import net.minecraft.predicate.entity.EntityPredicates; -import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvents; import net.minecraft.util.ActionResult; @@ -58,7 +59,7 @@ public class IgnominiousBulbEntity extends MobEntity { ); @Nullable - private Map tentacles; + private Map> tentacles; private int prevAge; private int happyTicks; @@ -146,7 +147,9 @@ public class IgnominiousBulbEntity extends MobEntity { Vec3d change = new Vec3d(x, y, z).subtract(getPos()); super.setPosition(x, y, z); getTentacles().values().forEach(tentacle -> { - tentacle.setPosition(tentacle.getPos().add(change)); + tentacle.ifPresent(getWorld(), t -> { + t.setPosition(t.getPos().add(change)); + }); }); } @@ -170,9 +173,11 @@ public class IgnominiousBulbEntity extends MobEntity { }); } - for (TentacleEntity tentacle : tentacles.values()) { + for (EntityReference tentacle : tentacles.values()) { if (getWorld().random.nextInt(isAngry() ? 12 : 1200) == 0) { - tentacle.addActiveTicks(120); + tentacle.ifPresent(getWorld(), t -> { + t.addActiveTicks(120); + }); } } LivingEntity target = getAttacker(); @@ -191,6 +196,7 @@ public class IgnominiousBulbEntity extends MobEntity { tentacles.values() .stream() + .flatMap(tentacle -> tentacle.getOrEmpty(getWorld()).stream()) .sorted(Comparator.comparing(a -> a.distanceTo(target))) .limit(2) .forEach(tentacle -> { @@ -224,27 +230,29 @@ public class IgnominiousBulbEntity extends MobEntity { } } - private Map getTentacles() { + private Map> getTentacles() { if (tentacles == null) { - tentacles = getWorld().getEntitiesByClass(TentacleEntity.class, this.getBoundingBox().expand(5, 0, 5), EntityPredicates.VALID_ENTITY) + tentacles = getWorld().getEntitiesByClass(TentacleEntity.class, getBoundingBox().expand(5, 0, 5), EntityPredicates.VALID_ENTITY) .stream() .collect(Collectors.toMap(TentacleEntity::getBlockPos, tentacle -> { tentacle.setBulb(this); - return tentacle; + return new EntityReference<>(tentacle); })); } return tentacles; } - private TentacleEntity updateTentacle(BlockPos pos, @Nullable TentacleEntity tentacle) { - if (tentacle == null || tentacle.isRemoved()) { - tentacle = new TentacleEntity(getWorld(), pos); - tentacle.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, getWorld().random.nextFloat() * 360, 0); - tentacle.setBulb(this); + private EntityReference updateTentacle(BlockPos pos, @Nullable EntityReference tentacle) { + if (tentacle == null || tentacle.getOrEmpty(getWorld()).filter(Entity::isAlive).isEmpty()) { + var created = new TentacleEntity(getWorld(), pos); + created.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, getWorld().random.nextFloat() * 360, 0); + created.setBulb(this); if (getWorld().isTopSolid(pos.down(), this)) { - getWorld().spawnEntity(tentacle); + getWorld().spawnEntity(created); } + + return new EntityReference<>(created); } return tentacle; } @@ -277,7 +285,9 @@ public class IgnominiousBulbEntity extends MobEntity { @Override public void remove(RemovalReason reason) { super.remove(reason); - getTentacles().values().forEach(tentacle -> tentacle.remove(reason)); + getTentacles().values().forEach(tentacle -> { + tentacle.ifPresent(getWorld(), t -> t.remove(reason)); + }); } @Override @@ -319,7 +329,7 @@ public class IgnominiousBulbEntity extends MobEntity { getTentacles().forEach((pos, tentacle) -> { var compound = new NbtCompound(); compound.put("pos", NbtSerialisable.BLOCK_POS.write(pos)); - compound.putUuid("uuid", tentacle.getUuid()); + compound.put("target", tentacle.toNBT()); tentacles.add(compound); }); nbt.put("tentacles", tentacles); @@ -332,13 +342,10 @@ public class IgnominiousBulbEntity extends MobEntity { setAge(nbt.getInt("age")); if (!getWorld().isClient) { if (nbt.contains("tentacles", NbtElement.LIST_TYPE)) { - var tentacles = new HashMap(); + var tentacles = new HashMap>(); nbt.getList("tentacles", NbtElement.COMPOUND_TYPE).forEach(tag -> { var compound = (NbtCompound)tag; - if (((ServerWorld)getWorld()).getEntity(compound.getUuid("uuid")) instanceof TentacleEntity tentacle) { - tentacle.setBulb(this); - tentacles.put(NbtSerialisable.BLOCK_POS.read(compound.getCompound("pos")), tentacle); - } + tentacles.put(NbtSerialisable.BLOCK_POS.read(compound.getCompound("pos")), new EntityReference<>(compound.getCompound("target"))); }); this.tentacles = tentacles; } From c7a161e114d99936c742519fb3e83dc35a6def4b Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 18 Jan 2024 16:28:45 +0000 Subject: [PATCH 32/98] Update goldroot textures --- .../textures/block/gold_root_stage0.png | Bin 4282 -> 4499 bytes .../textures/block/gold_root_stage1.png | Bin 4375 -> 4562 bytes .../textures/block/gold_root_stage2.png | Bin 4452 -> 4629 bytes .../textures/block/gold_root_stage3.png | Bin 4522 -> 4690 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage0.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage0.png index 54ec7e1cc8401d49115084fbf5a2d787c9863620..a24793ebdef572e0199358bbafae4f0bfb4a2e6a 100644 GIT binary patch delta 801 zcmdm`I9Yi@Gb8iF7DEqnCI$u(1_lNg?e6ItpkSnDXrO0o#=u}?U}R!tU}$A%pw^m;wM}a?#g=GanB@>0-dvoa~?fp<8|NL%o#d|x4 z71L*?l^$jA-F`MiNiS{F>ZAvvc05T8i9Dyz7(G+b?Ckqm>VG$+JaFFCo$t@T3N997 zu2Cwi?~z>QyT{RHbEfp1@14$TG*%yZeneO;cEXQ|ue*-t&*9eNwlPjw7X0~$V$#M7 z$GoIBFIp++FYF%TySzs6@y1h?-J8SKZF*+=le^inHg)FfEp8JJC{0svY-!T+(m3ok zrEIPUx29Ep`G@`;b)7L>JG~N~L^!ZYrCjI#$G$&3`)U1hwY^IcWM0%2&gnN)*uZU) z)%UsfjYY5Uf&V-)FLmpe@|~%h6qlVjQF%eV^o|{QnWy9mb~3lj=eap0_N2e}iRX>q zjejlEF(~2wvaYf?d*0(Oj2@TQOfo-kP|+Z6!JDsq@84z4zrEk~?9aapsq>2#O6Dvu zWnkco&J2ktiSYHY+B}P?iAlxMEIBpRASG4T%rMbX*Tga@QP(mh(Nx#Kz|uS^&Dc0K z(bRCV1M6}gQ&US*V*_(DLvu6B$)8yZZ450!V3DkCU|?lnpv0x1piq%pVC9>il9^WN zl30>ztAt1r$f`FlVB=$)e3~sr#lqOc*wi@5RM*79$U@h|IMGly(ZDiAH__ZMH909I z%_7m*aI!bM7ayv1n-{ZZbBMXN&+r2#D7GYTcNd0l3=0@`o%ttMn+Z! z237{9FhcbFqPM8V?x+U~&?j5iEE^}`;zrZZf zWzh>Q7D#WK>8G|NCol9>tt*3K#|+nq#IRhyX~7@XuCIQzY*+V3o&WpnSI3`@=Hc)- zQePNmocoCP(M#Xm$13MXq^PcOMDb*nL$m zO*L43o>E`dF2|ov*OGfbi>2=kN`G7ZN!;zzu~SCZGkX;}U3DDWSVT*w1UB|cyk*lA zyHqmo!$y?Dj;K@qz|)hf+pkGveCoE9?dnSasz zX~V&2g+0&CuC496%&y?P+3PL?d;5WiiJPw1-n)CoBYN~E>qIsfOqCu*up@rFG z2iE00CME_(1_tIPCZ<4v&#Z+u1|}h}NY)0$xq%Xwf`URtZh@6=eoAIqrAuN-s;v?t zNg%7F~s6@a)Jcw;slWkVj6PWe&&B@`)|+8%-noX`rrSq_S}ET!N*yWPvmcJ&bbB88^{;`hHZZ8!v+neV_owCAQYwGG6^r0aahR@fgq@*qm#PHw{Jpk78yGxY L{an^LB{Ts5XlEjC diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage1.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage1.png index ad9dbea77645034c63832a15b462fd4771a6701f..f5830f6417ab6a538cb0d2a3197a4cfcb2d3098f 100644 GIT binary patch delta 865 zcmbQPbV+$aGb8iF7DEqnCI$u(1_lNg?e6ItpkSnDXrO0o#=u}?U}R!tU}$A%pS%XNi7R%@87 zPuwqdU#UqV;_e5xK6r3G>FU9wk>Tv&jskxe3(E?EN+t@u_vX?~+WVnG{`uYFiuZO7 zE2hs(D?Q5KyZvm4l3v=T)kzOT?Rb(H5_wLaF?yz=+1dBC)cN;b%c6uc|iEv<*O1aMek9~i7_S5?1YI~O?$h@d4oYQZpuz}ko ztM7B|8;f4y1OItqUh38_|}14&vSE1>`8y`6VDsJ z8~<9SV^G5VWnE=)_Pobm7(FhpnPh(8prS$Cf;V6J-oMM7e|x{}*`I$IQs);fl+0OR z%D})Eof#5Q65;D(wRsj(6O*#3Ns>Xbxv{CPv00Ldu8E1Ifo_sTqKU4Bfl-RNX`+F- zf#GC())l;_rk1A029_2UCKl#KlfSSQ*ce)bz#>`Oz`)ADK#5C1L7^hIz{)p2B{Qwk zC9x#cRtb?LkX3J9z{bZo`7~RMN}`#uNwTp?s;+@)3ebK-6OjFBX1XZ`$tj7ciI!$* zrlym<*}eEst=qhqJ)1*p-?f(HEMPLz#*yVoXXNaLC~i4wg}VXYpp>ij&Yr2 zImq89yleUYmlh>2{7p~AWq&nnwtH8;IOaBUg~`pQKC^dl{hG0LrNunnyD{gSpG<2C zG;>*&%6dg$#tMn+Z! z237`^K-G*3n8A8L`as}euZG^lzY6u2yMna%(gX@m1k8Q2NU6ztt&^ZuOVq+p_Q})# zIZmFYa`b3mfv&}m%@L&qh1aeg3>9FhcbFqPM8V?x+U~&?j5iEE^}`;zrZZf zWzh>Q7D#WK>8G|NCol9>tt*3K#|+nq#IRhyX~7@XuCIQzY*+V3o&WpnSI3`@=Hc)- zQePNmocoCP(M#Xm$13MXq^PcOMDb*nL$m zO*L43o>E`dF2|ov*OGfbi>2=kN`G7ZN!;zzu~SCZGkX;}U3DDWSVT*w1UB|cyk*lA zyHqmo!$y?Dj;K@qz|)hf+pkGveCoE9?dnSasz zX~V&2g+0&CuC496%&y?P+3PL?d;5WiiJPw1-n)CaTvANS&>f0@iKs8G)$%E0;jL&$WX z^kz>N#}JFtZ~G0o4mk+8-tTH{-J$W)MdhhjV+4!NB<6}F{s|&YtxjFK%^h*hmv8L4P*(Sop;>Q%FMAwk-lW&eHjJAMJ**l6Y?pu%y{D_6%Q~lo FCII02O~n8J diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage2.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage2.png index af5f7f914d60e393c3bf1bad1ac7b0507b594d23..b08acd77536b602e3b4cbc975ba24140ddf3dd8b 100644 GIT binary patch delta 933 zcmaE&G*x9nGb8iF7DEqnCI$u(1_lNg?e6ItpkSnDXrO0o#=u}?U}R!tU}$A%pa{7M2wRl}r?R@6Dx~wD&`W{PVlT74PjF zR!pCnR(h1dcl+58CB3vwtCJpx+VLbYB=Vd-WAsc#v$OAOssG)O^1yjlcfLRWD!5pT zxkjn5zDIJI?;b~+&6(13zIQsW(O7-t`4M5Y*a<%-zV14nKZje7+r~I$S@7p0ib)$U z9P^UiylADMzp#6V@A4YO#~V*oc5e<_x9OSfPwr;R+SHk^x42C_pfpXvv874NOXINH zl(M-Z+?rPXWE|Mq^{vp@ebq|Pr|D4Das zl!1XSIx{4qB*NFnYV$0nCMFdVgOo&bb29^7vt(lnT@yvCRGQ%h50LqiKoOA9m8$zNCtYz!?zV6m)iU|?lnpv0x1piq%pVC9>il9^WN zl30>ztAt1s$f`FlVB=$)e3~sr#ndvzJkc~USr=%XnXZYcNuq9&d77neszItzqH%JH zaiV$BWN&saK2+;AFJ{l?5c^VduM(J|*pj^6T^PPGEMVAm=C@?QWEBC0dLaQB`LDA7i`)-9TD7`Lqwj)rK>J(G4IREGW_5MB z`Yhiv#i4*>-_5!9pa1*V{rR{$qvn@if#O;h@k^(^ytq_7*>c_HT^}>AvQKL(J6JQh zbDjQ?$A8k4@|vxtIWB9wrg_~yHz)s(iMmXz()k4(7p#{37qh4~WwUU3(U|w-o8aCB zeTQ-{9J6zhY&rM3Krm!ZdUoOS#P$f<&hg3F lbM?GM=#t>C9)E&)3)nRRvy6@&HtMn+Z! z23Cf~K-G*3n8A8L`anR_iMwy&UxoV1T|ruWX##~O0_MJ1q}1fS)=5ySC2CTYstNz#nSf%rN6EIB<}X<*eRpynY{{~t~!owETW}T0vmfJ-m+PH#m3jXA zMq!k?!w1P7Mppl_glGIz*?aNgBKHFa%nAzcUc6%apom?fYL(`)M_!ImP74|K%)e;< zwBca1!k%Ym*Vgu3W>;|D>~)ucz5PJM#7)<0@7=v_yS={p*5}`h9py#q*QfSRW?q*TmQ`RX54fz(Ut7IW;xK&@v?@)hKnc z1M6~L6B7d?14CmI12Yo?^T}UW3v3KbLSV714T^LFB`yU8g^Jt)E8qN-%(P0E#FA87 zB}AG)R=s%v8z1B5i5v+WV&~mYRRL24TavfC3&TBz_Y6Po#ZCV*nO#t!o<)>__rcr= zeV{bw>Eak-ar*59+pNP50WX$JXB*5_QdZGU+w&hy|H_LBvRs9up@Fr(&Am@K`o?@l> luiGu&J3hC4vD}oQhVi80%&6$isW(7*$kWx&Wt~$(697AZa9scZ diff --git a/src/main/resources/assets/unicopia/textures/block/gold_root_stage3.png b/src/main/resources/assets/unicopia/textures/block/gold_root_stage3.png index 970f559fb30f6304eaa298dac50e7988c8b7f7f0..816a7d1cce37664ebaed3fba87ae1d738c1d68ea 100644 GIT binary patch delta 995 zcmZ3bd`V?OGb8iF7DEqnCI$u(1_lNg?e6ItpkSnDXrO0o#=u}?U}R!tU}$A%pA)Uto#UxoVMrAnf%+9HJ~0_MJ1q|~>S%XNi7R%@87 zPuwqdU#UqV;_e5xK6r3G>FU9wk>Tv&jskxe3(E?EN+t@u_vX?~+WVnG{`uYFiuZO7 zE2hs(D?Q5KyZvm4l3v=T)kzOT?Rb(H5_wLaF?yz=+1dBC)cN;b%c6uc|iEv<*O1aMek9~i7_S5?1YI~O?$h@d4oYQZpuz}ko ztM7B|8;f4y1OItqUh38_|}14&vSE1>`8y`6VDsJ z8~<9SV^G5VWnE=)_Pobm7(FhpnPh(8prS$Cf;V6J-oMM7e|x{}*`I$IQs);fl+0OR z%D})Eof#5Q65;D(wRsj(6O)Refmu>gilw2hfmy1Fu8Fycfv!bLih*vbnQ5|dqNQQ7 zxrxza2iE1hrlyvr#)c-Q7AEGVW|P0L7T6eCgur51+rYrez(9#hK|!G+x4_CbKP5A* z(j~DZ)m90SCXiKcUcko3IQcYNjEae2szGv^se!JMaZ;kLNlId(u0^7uk*=AEv8hQ) znuUo`n(<_Bb}v3u>ozZD&*l*G6)3p`Oi^q}-tI08-xwA!>^k#XvS6}`fI_{HfQ-_; zCMzXiy5shAaSX9IeRqOwUbBNhtG$lpq}T@@-ItyUY2Ml8{h0d+)5nExqzhQe1E(DG z<%(y}%sb%ZbX%b{DM;t=riR*v3vc$N-n)Om{@1sSEEdW~Rtg3we|K$DIl6M9=fT-Z zv)tO-p2+NsS9U&8xNhad1_OojOpV9ucUt?iWhF4KIc-oTTXoJVy?3T!j+Z)9LC?d( zKctp_v%A&U_2ST$Lw04~_msbXdnEkEO^ycNmocxey}i2Ye`$2!ef9DMcVF_pylvRF zs&GeE9>l7w=li z%d$&X<0xb2Jb8u*^W*pJfAI8dvY3FXoc&(wck(lSP7QQ=&Y};>ik_~1F6*2UngA|! Bj=ul^ delta 994 zcmcblvPyYEGb7W)7DEpY1_lO3AcoQIp1uJJMtX(@dd6l93`PbtMn+Z! z23Cej9?h4Z4OA{zO5is}7BBdtpwN8RsEl~?Y*(Xo` z=Qw$q%F&~N1-ceLHb;~e6kfY}FjRo0-eHdH9Ff>1M}ALo>Xy*Axp%zsyUe}a`~tH? zmqjnMSRlP^rk~o9oV?IiwXO_`9Wz`b62o%+rUidkyT1C>vR&ODb^h&$OkhQ&GbAk+?f z>{7|R4`X2Sw}>RjV|YJ@Rsla$3l+XZ}U= zrws?A74|$kySBFPGP{EFX0N*p?Cl34CT_Z3d++Xb+wJw$w?6-7>?kiRCh?1e$06 z2?wS*H%}MG5R21yCm7~81&FlT_hicUL?mBcsCepxLsN4n^V@gtU)dLZWWR8*Wnrd_ z2Ya;d;Y$JSeHNYWB#(6VM(KJ*b$y8<&x~% z9dktu)7d6}b-Mcks3uJ@Gl6-+53%|B-(U5ZY?yd*%E82)7lrOSZQqu)+mL(W?ViXJ z3(Oat^6rV`IB@*%=4%!YH{8jXH0Os{q~K&WGxN_34Qo1He%YOM?-zT~?l-SDAKAVl z2Gc<0}k7-^J{?%y=1z)W~mT2j}e0$96!tGw^+l;$PH@x3- zc#l)|!K9^}@Af^YUVl%T^N5!)&+=9E)r%T;?z0tYzpVUkt^9_Z!|k Date: Thu, 18 Jan 2024 19:52:59 +0000 Subject: [PATCH 33/98] Added golden oak trees --- .../ability/EarthPonyGrowAbility.java | 134 ++++++++++++++++-- .../unicopia/block/EnchantedFruitBlock.java | 24 ++++ .../unicopia/block/FruitBearingBlock.java | 14 +- .../unicopia/block/GoldenOakLeavesBlock.java | 26 ++++ .../unicopia/block/UBlocks.java | 14 +- .../unicopia/block/UWoodTypes.java | 1 + .../minelittlepony/unicopia/item/UItems.java | 1 + .../unicopia/server/world/UTreeGen.java | 12 ++ .../unicopia/blockstates/golden_apple.json | 7 + .../blockstates/golden_oak_leaves.json | 7 + .../unicopia/blockstates/golden_oak_log.json | 16 +++ .../blockstates/golden_oak_sapling.json | 7 + .../blockstates/golden_oak_sprout.json | 28 ++++ .../resources/assets/unicopia/lang/en_us.json | 5 + .../unicopia/models/block/golden_apple.json | 6 + .../models/block/golden_oak_leaves.json | 6 + .../unicopia/models/block/golden_oak_log.json | 7 + .../block/golden_oak_log_horizontal.json | 7 + .../models/block/golden_oak_sapling.json | 6 + .../models/item/golden_oak_leaves.json | 3 + .../unicopia/models/item/golden_oak_log.json | 3 + .../models/item/golden_oak_sapling.json | 6 + .../models/item/golden_oak_seeds.json | 6 + .../textures/block/golden_oak_leaves.png | Bin 0 -> 6220 bytes .../textures/block/golden_oak_log.png | Bin 0 -> 6881 bytes .../textures/block/golden_oak_log_top.png | Bin 0 -> 6689 bytes .../textures/item/golden_oak_sapling.png | Bin 0 -> 6670 bytes .../textures/item/golden_oak_seeds.png | Bin 0 -> 6361 bytes .../minecraft/tags/blocks/dragon_immune.json | 4 +- .../data/minecraft/tags/blocks/leaves.json | 1 + .../data/minecraft/tags/blocks/logs.json | 1 + .../minecraft/tags/blocks/logs_that_burn.json | 1 + .../tags/blocks/maintains_farmland.json | 1 + .../minecraft/tags/blocks/mineable/hoe.json | 1 + .../minecraft/tags/blocks/piglin_loved.json | 8 ++ .../minecraft/tags/items/piglin_loved.json | 9 ++ .../loot_tables/blocks/golden_apple.json | 49 +++++++ .../loot_tables/blocks/golden_oak_leaves.json | 116 +++++++++++++++ .../loot_tables/blocks/golden_oak_log.json | 20 +++ .../blocks/golden_oak_sapling.json | 20 +++ .../data/unicopia/recipes/gold_nugget.json | 9 ++ .../tags/items/loot_bug_high_value_drops.json | 1 + 42 files changed, 569 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java create mode 100644 src/main/resources/assets/unicopia/blockstates/golden_apple.json create mode 100644 src/main/resources/assets/unicopia/blockstates/golden_oak_leaves.json create mode 100644 src/main/resources/assets/unicopia/blockstates/golden_oak_log.json create mode 100644 src/main/resources/assets/unicopia/blockstates/golden_oak_sapling.json create mode 100644 src/main/resources/assets/unicopia/blockstates/golden_oak_sprout.json create mode 100644 src/main/resources/assets/unicopia/models/block/golden_apple.json create mode 100644 src/main/resources/assets/unicopia/models/block/golden_oak_leaves.json create mode 100644 src/main/resources/assets/unicopia/models/block/golden_oak_log.json create mode 100644 src/main/resources/assets/unicopia/models/block/golden_oak_log_horizontal.json create mode 100644 src/main/resources/assets/unicopia/models/block/golden_oak_sapling.json create mode 100644 src/main/resources/assets/unicopia/models/item/golden_oak_leaves.json create mode 100644 src/main/resources/assets/unicopia/models/item/golden_oak_log.json create mode 100644 src/main/resources/assets/unicopia/models/item/golden_oak_sapling.json create mode 100644 src/main/resources/assets/unicopia/models/item/golden_oak_seeds.json create mode 100644 src/main/resources/assets/unicopia/textures/block/golden_oak_leaves.png create mode 100644 src/main/resources/assets/unicopia/textures/block/golden_oak_log.png create mode 100644 src/main/resources/assets/unicopia/textures/block/golden_oak_log_top.png create mode 100644 src/main/resources/assets/unicopia/textures/item/golden_oak_sapling.png create mode 100644 src/main/resources/assets/unicopia/textures/item/golden_oak_seeds.png create mode 100644 src/main/resources/data/minecraft/tags/blocks/piglin_loved.json create mode 100644 src/main/resources/data/minecraft/tags/items/piglin_loved.json create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/golden_apple.json create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_leaves.json create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_log.json create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/golden_oak_sapling.json create mode 100644 src/main/resources/data/unicopia/recipes/gold_nugget.json diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index b69ebae4..f02ee066 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -1,10 +1,13 @@ package com.minelittlepony.unicopia.ability; +import java.util.HashSet; +import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.function.DoubleSupplier; import java.util.function.Supplier; - import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Pos; @@ -12,9 +15,12 @@ import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.server.world.BlockDestructionManager; +import com.minelittlepony.unicopia.server.world.UTreeGen; import com.minelittlepony.unicopia.util.TraceHelper; import com.minelittlepony.unicopia.util.VecHelper; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.block.CarrotsBlock; @@ -23,10 +29,13 @@ import net.minecraft.item.BoneMealItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.particle.ParticleTypes; +import net.minecraft.state.property.Property; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.random.Random; import net.minecraft.world.World; +import net.minecraft.world.WorldEvents; /** * Earth Pony ability to grow crops @@ -67,10 +76,14 @@ public class EarthPonyGrowAbility implements Ability { public boolean apply(Pony player, Pos data) { int count = 0; - for (BlockPos pos : BlockPos.iterate( - data.pos().add(-2, -2, -2), - data.pos().add( 2, 2, 2))) { - count += applySingle(player, player.asWorld(), player.asWorld().getBlockState(pos), pos); + if (!applyDirectly(player, data.pos())) { + for (BlockPos pos : BlockPos.iterate( + data.pos().add(-2, -2, -2), + data.pos().add( 2, 2, 2))) { + count += applySingle(player, player.asWorld(), player.asWorld().getBlockState(pos), pos); + } + } else { + count = 1; } if (count > 0) { @@ -90,16 +103,7 @@ public class EarthPonyGrowAbility implements Ability { if (state.isOf(Blocks.CARROTS)) { if (state.get(CarrotsBlock.AGE) == CarrotsBlock.MAX_AGE) { boolean transform = w.random.nextInt(3) == 0; - DoubleSupplier vecComponentFactory = () -> w.random.nextTriangular(0, 0.5); - Supplier posSupplier = () -> pos.toCenterPos().add(VecHelper.supply(vecComponentFactory)); - - for (int i = 0; i < 25; i++) { - ParticleUtils.spawnParticle(w, new MagicParticleEffect(0xFFFF00), posSupplier.get(), Vec3d.ZERO); - if (transform) { - ParticleUtils.spawnParticle(w, ParticleTypes.CLOUD, posSupplier.get(), Vec3d.ZERO); - } - } - + spawnConversionParticles(w, pos, transform); if (transform) { w.setBlockState(pos, UBlocks.GOLD_ROOT.getDefaultState().with(CarrotsBlock.AGE, CarrotsBlock.MAX_AGE)); } @@ -134,6 +138,55 @@ public class EarthPonyGrowAbility implements Ability { return 0; } + private boolean applyDirectly(Pony player, BlockPos pos) { + return TransmutationRecipe.RECIPES.stream() + .filter(recipe -> recipe.matches(player.asWorld(), pos)) + .map(recipe -> recipe.checkPattern(player.asWorld(), pos)) + .filter(result -> result.matchedLocations().size() + 1 >= TransmutationRecipe.MINIMUM_INPUT) + .filter(result -> { + boolean transform = result.shoudTransform(player.asWorld().random); + + player.playSound(USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, 1); + + result.matchedLocations().forEach(cell -> { + spawnConversionParticles(player.asWorld(), cell.up(), false); + BlockDestructionManager manager = BlockDestructionManager.of(player.asWorld()); + if (transform) { + if (manager.damageBlock(cell, 8) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) { + player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState()); + player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell))); + } + } else { + if (manager.damageBlock(cell, 4) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) { + player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState()); + player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell))); + } + } + }); + + spawnConversionParticles(player.asWorld(), pos, transform); + if (transform) { + player.asWorld().setBlockState(pos, result.recipe().getResult(player.asWorld(), pos)); + } + + return true; + }) + .findFirst() + .isPresent(); + } + + private static void spawnConversionParticles(World w, BlockPos pos, boolean success) { + DoubleSupplier vecComponentFactory = () -> w.random.nextTriangular(0, 0.5); + Supplier posSupplier = () -> pos.toCenterPos().add(VecHelper.supply(vecComponentFactory)); + + for (int i = 0; i < 25; i++) { + ParticleUtils.spawnParticle(w, new MagicParticleEffect(0xFFFF00), posSupplier.get(), Vec3d.ZERO); + if (success) { + ParticleUtils.spawnParticle(w, ParticleTypes.CLOUD, posSupplier.get(), Vec3d.ZERO); + } + } + } + @Override public void warmUp(Pony player, AbilitySlot slot) { player.getMagicalReserves().getExertion().addPercent(30); @@ -148,6 +201,57 @@ public class EarthPonyGrowAbility implements Ability { } + private static record TransmutationRecipe(Block input, BlockState output, BlockState material) { + static final List RECIPES = List.of( + new TransmutationRecipe(Blocks.OAK_SAPLING, UTreeGen.GOLDEN_APPLE_TREE.sapling().get().getDefaultState(), Blocks.RAW_GOLD_BLOCK.getDefaultState()), + new TransmutationRecipe(Blocks.CARROTS, UBlocks.GOLD_ROOT.getDefaultState(), Blocks.RAW_GOLD_BLOCK.getDefaultState()), + new TransmutationRecipe(Blocks.CORNFLOWER, UBlocks.CURING_JOKE.getDefaultState(), Blocks.LAPIS_BLOCK.getDefaultState()), + new TransmutationRecipe(Blocks.WITHER_ROSE, UBlocks.PLUNDER_VINE_BUD.getDefaultState(), Blocks.NETHERRACK.getDefaultState()) + ); + static final int RADIUS = 3; + static final int SIDE_LENGTH = (2 * RADIUS) + 1; + static final int AREA = (SIDE_LENGTH * SIDE_LENGTH) - 1; + static final int MINIMUM_INPUT = 9; + + public boolean matches(World world, BlockPos pos) { + return world.getBlockState(pos).isOf(input); + } + + public Result checkPattern(World world, BlockPos pos) { + BlockPos center = pos.down(); + Set matches = new HashSet<>(); + for (BlockPos cell : BlockPos.iterateInSquare(center, RADIUS, Direction.EAST, Direction.NORTH)) { + if (cell.equals(center)) { + continue; + } + if (!world.getBlockState(cell).equals(material)) { + break; + } + matches.add(cell.toImmutable()); + } + return new Result(this, matches); + } + + public BlockState getResult(World world, BlockPos pos) { + BlockState input = world.getBlockState(pos); + BlockState output = this.output; + for (var property : input.getProperties()) { + output = copyProperty(input, output, property); + } + return output; + } + + private > BlockState copyProperty(BlockState from, BlockState to, Property property) { + return to.withIfExists(property, from.get(property)); + } + + record Result (TransmutationRecipe recipe, Set matchedLocations) { + public boolean shoudTransform(Random random) { + return random.nextInt(TransmutationRecipe.AREA) < matchedLocations().size(); + } + } + } + public interface Growable { boolean grow(World world, BlockState state, BlockPos pos); } diff --git a/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java b/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java new file mode 100644 index 00000000..c6af9166 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java @@ -0,0 +1,24 @@ +package com.minelittlepony.unicopia.block; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; + +public class EnchantedFruitBlock extends FruitBlock { + static final BooleanProperty ENCHANTED = BooleanProperty.of("enchanted"); + + public EnchantedFruitBlock(Settings settings, Direction attachmentFace, Block stem, VoxelShape shape) { + super(settings, attachmentFace, stem, shape); + setDefaultState(getDefaultState().with(ENCHANTED, false)); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder); + builder.add(ENCHANTED); + } + +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java b/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java index 8faa118a..b9741cef 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/FruitBearingBlock.java @@ -62,6 +62,14 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka return true; } + protected boolean shouldAdvance(Random random) { + return true; + } + + protected BlockState getPlacedFruitState(Random random) { + return fruit.get().getDefaultState(); + } + @Override public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { super.randomTick(state, world, pos, random); @@ -74,6 +82,10 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka BlockSoundGroup group = getSoundGroup(state); int steps = FertilizableUtil.getGrowthSteps(world, pos, state, random); while (steps-- > 0) { + if (!shouldAdvance(random)) { + continue; + } + if (state.get(STAGE) == Stage.FRUITING) { state = state.cycle(AGE); if (state.get(AGE) > 20) { @@ -89,7 +101,7 @@ public class FruitBearingBlock extends LeavesBlock implements TintedBlock, Bucka if (stage == Stage.FRUITING && isPositionValidForFruit(state, pos)) { if (world.isAir(fruitPosition)) { - world.setBlockState(fruitPosition, fruit.get().getDefaultState(), Block.NOTIFY_ALL); + world.setBlockState(fruitPosition, getPlacedFruitState(random), Block.NOTIFY_ALL); } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java b/src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java new file mode 100644 index 00000000..1ae9ed61 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/GoldenOakLeavesBlock.java @@ -0,0 +1,26 @@ +package com.minelittlepony.unicopia.block; + +import java.util.function.Supplier; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.random.Random; + +public class GoldenOakLeavesBlock extends FruitBearingBlock { + + public GoldenOakLeavesBlock(Settings settings, int overlay, Supplier fruit, + Supplier rottenFruitSupplier) { + super(settings, overlay, fruit, rottenFruitSupplier); + } + + @Override + protected boolean shouldAdvance(Random random) { + return random.nextInt(1000) == 0; + } + + @Override + protected BlockState getPlacedFruitState(Random random) { + return super.getPlacedFruitState(random).with(EnchantedFruitBlock.ENCHANTED, random.nextInt(1000) == 0); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java index ca6726e9..7fc8d666 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java @@ -79,7 +79,7 @@ public interface UBlocks { Block PALM_STAIRS = register("palm_stairs", new StairsBlock(PALM_PLANKS.getDefaultState(), Settings.copy(PALM_PLANKS).pistonBehavior(PistonBehavior.NORMAL)), ItemGroups.BUILDING_BLOCKS); Block PALM_SLAB = register("palm_slab", new SlabBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).strength(2, 3).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.NORMAL)), ItemGroups.BUILDING_BLOCKS); Block PALM_FENCE = register("palm_fence", new FenceBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).strength(2, 3).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.NORMAL)), ItemGroups.BUILDING_BLOCKS); - Block PALM_FENCE_GATE = register("palm_fence_gate", new FenceGateBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).strength(2, 3).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.NORMAL), WoodType.OAK), ItemGroups.BUILDING_BLOCKS); + Block PALM_FENCE_GATE = register("palm_fence_gate", new FenceGateBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).strength(2, 3).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.NORMAL), UWoodTypes.PALM), ItemGroups.BUILDING_BLOCKS); Block PALM_DOOR = register("palm_door", new DoorBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).instrument(Instrument.BASS).strength(3.0f).nonOpaque().burnable().pistonBehavior(PistonBehavior.DESTROY), UWoodTypes.PALM.setType()), ItemGroups.FUNCTIONAL); Block PALM_TRAPDOOR = register("palm_trapdoor", new TrapdoorBlock(Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).instrument(Instrument.BASS).strength(3).nonOpaque().allowsSpawning(BlockConstructionUtils::never).burnable(), UWoodTypes.PALM.setType()), ItemGroups.FUNCTIONAL); Block PALM_PRESSURE_PLATE = register("palm_pressure_plate", new PressurePlateBlock(PressurePlateBlock.ActivationRule.EVERYTHING, Settings.create().mapColor(PALM_PLANKS.getDefaultMapColor()).noCollision().strength(0.5f).sounds(BlockSoundGroup.WOOD).pistonBehavior(PistonBehavior.DESTROY), BlockSetType.OAK), ItemGroups.BUILDING_BLOCKS); @@ -127,6 +127,16 @@ public interface UBlocks { Block SOUR_APPLE = register("sour_apple", new FruitBlock(Settings.create().mapColor(MapColor.GREEN), Direction.DOWN, SOUR_APPLE_LEAVES, FruitBlock.DEFAULT_SHAPE)); Block SOUR_APPLE_SPROUT = register("sour_apple_sprout", new SproutBlock(0xE5FFCC88, () -> UItems.SOUR_APPLE_SEEDS, () -> UTreeGen.SOUR_APPLE_TREE.sapling().map(Block::getDefaultState).get())); + Block GOLDEN_OAK_LEAVES = register("golden_oak_leaves", new GoldenOakLeavesBlock(FabricBlockSettings.copy(Blocks.OAK_LEAVES), + MapColor.GOLD.color, + () -> UBlocks.GOLDEN_APPLE, + () -> Items.GOLDEN_APPLE.getDefaultStack() + ), ItemGroups.NATURAL); + Block GOLDEN_APPLE = register("golden_apple", new EnchantedFruitBlock(Settings.create().mapColor(MapColor.GOLD), Direction.DOWN, GOLDEN_OAK_LEAVES, FruitBlock.DEFAULT_SHAPE)); + Block GOLDEN_OAK_SPROUT = register("golden_oak_sprout", new SproutBlock(0xE5FFCC88, () -> UItems.GOLDEN_OAK_SEEDS, () -> UTreeGen.GOLDEN_APPLE_TREE.sapling().map(Block::getDefaultState).get())); + + Block GOLDEN_OAK_LOG = register("golden_oak_log", BlockConstructionUtils.createLogBlock(MapColor.OFF_WHITE, MapColor.GOLD), ItemGroups.BUILDING_BLOCKS); + Block APPLE_PIE = register("apple_pie", new PieBlock(Settings.create().solid().mapColor(MapColor.ORANGE).strength(0.5F).sounds(BlockSoundGroup.WOOL).pistonBehavior(PistonBehavior.DESTROY), () -> UItems.APPLE_PIE_SLICE, () -> UItems.APPLE_PIE, @@ -254,10 +264,12 @@ public interface UBlocks { FlammableBlockRegistry.getDefaultInstance().add(GREEN_APPLE_LEAVES, 30, 60); FlammableBlockRegistry.getDefaultInstance().add(SWEET_APPLE_LEAVES, 30, 60); FlammableBlockRegistry.getDefaultInstance().add(SOUR_APPLE_LEAVES, 30, 60); + FlammableBlockRegistry.getDefaultInstance().add(GOLDEN_OAK_LEAVES, 60, 120); FlammableBlockRegistry.getDefaultInstance().add(MANGO_LEAVES, 30, 60); FlammableBlockRegistry.getDefaultInstance().add(PALM_LEAVES, 30, 60); FlammableBlockRegistry.getDefaultInstance().add(PALM_LOG, 5, 5); FlammableBlockRegistry.getDefaultInstance().add(PALM_WOOD, 5, 5); + FlammableBlockRegistry.getDefaultInstance().add(GOLDEN_OAK_LOG, 15, 15); FlammableBlockRegistry.getDefaultInstance().add(STRIPPED_PALM_LOG, 5, 5); FlammableBlockRegistry.getDefaultInstance().add(STRIPPED_PALM_WOOD, 5, 5); FlammableBlockRegistry.getDefaultInstance().add(PALM_PLANKS, 5, 20); diff --git a/src/main/java/com/minelittlepony/unicopia/block/UWoodTypes.java b/src/main/java/com/minelittlepony/unicopia/block/UWoodTypes.java index 0a44586e..5f76ed97 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UWoodTypes.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UWoodTypes.java @@ -15,6 +15,7 @@ import net.minecraft.util.Identifier; public interface UWoodTypes { WoodType PALM = register("palm"); + WoodType GOLDEN_OAK = register("golden_oak"); RegistryKey PALM_BOAT_TYPE = TerraformBoatTypeRegistry.createKey(Unicopia.id("palm")); BlockSetType CLOUD = new BlockSetTypeBuilder() diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index 40f9eccd..a5ede2a8 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -107,6 +107,7 @@ public interface UItems { Item GREEN_APPLE_SEEDS = register("green_apple_seeds", new AliasedBlockItem(UBlocks.GREEN_APPLE_SPROUT, new Item.Settings()), ItemGroups.NATURAL); Item SWEET_APPLE_SEEDS = register("sweet_apple_seeds", new AliasedBlockItem(UBlocks.SWEET_APPLE_SPROUT, new Item.Settings()), ItemGroups.NATURAL); Item SOUR_APPLE_SEEDS = register("sour_apple_seeds", new AliasedBlockItem(UBlocks.SOUR_APPLE_SPROUT, new Item.Settings()), ItemGroups.NATURAL); + Item GOLDEN_OAK_SEEDS = register("golden_oak_seeds", new AliasedBlockItem(UBlocks.GOLDEN_OAK_SPROUT, new Item.Settings()), ItemGroups.NATURAL); Item MUG = register("mug", new Item(new Settings().maxCount(16)), ItemGroups.TOOLS); Item CIDER = register("cider", new DrinkableItem(new Item.Settings().food(UFoodComponents.CIDER).maxCount(1).recipeRemainder(MUG)), ItemGroups.FOOD_AND_DRINK); diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java b/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java index bb55a235..8331e694 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java @@ -46,6 +46,16 @@ public interface UTreeGen { Tree GREEN_APPLE_TREE = createAppleTree("green_apple", UBlocks.GREEN_APPLE_LEAVES, 2); Tree SWEET_APPLE_TREE = createAppleTree("sweet_apple", UBlocks.SWEET_APPLE_LEAVES, 3); Tree SOUR_APPLE_TREE = createAppleTree("sour_apple", UBlocks.SOUR_APPLE_LEAVES, 5); + Tree GOLDEN_APPLE_TREE = Tree.Builder.create(Unicopia.id("golden_oak_tree"), + new StraightTrunkPlacer(6, 7, 3), + new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) + ) + .configure(TreeFeatureConfig.Builder::forceDirt) + .farmingCondition(1, 3, 5) + .log(UBlocks.GOLDEN_OAK_LOG) + .leaves(UBlocks.GOLDEN_OAK_LEAVES) + .sapling(Unicopia.id("golden_oak_sapling")) + .build(); Tree BANANA_TREE = Tree.Builder.create(Unicopia.id("banana_tree"), new StraightTrunkPlacer(4, 5, 3), new FernFoliagePlacer(ConstantIntProvider.create(4), ConstantIntProvider.create(0)) @@ -84,6 +94,8 @@ public interface UTreeGen { new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) ) .configure(TreeFeatureConfig.Builder::forceDirt) + .biomes(selector -> selector.hasTag(BiomeTags.IS_FOREST)) + .count(2, 0.01F, 1) .farmingCondition(1, preferredDensity - 2, preferredDensity) .log(Blocks.OAK_LOG) .leaves(leaves) diff --git a/src/main/resources/assets/unicopia/blockstates/golden_apple.json b/src/main/resources/assets/unicopia/blockstates/golden_apple.json new file mode 100644 index 00000000..e0c679aa --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_apple.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "unicopia:block/golden_apple" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/blockstates/golden_oak_leaves.json b/src/main/resources/assets/unicopia/blockstates/golden_oak_leaves.json new file mode 100644 index 00000000..b8084dfb --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_oak_leaves.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "unicopia:block/golden_oak_leaves" + } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/golden_oak_log.json b/src/main/resources/assets/unicopia/blockstates/golden_oak_log.json new file mode 100644 index 00000000..4f50529f --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_oak_log.json @@ -0,0 +1,16 @@ +{ + "variants": { + "axis=x": { + "model": "unicopia:block/golden_oak_log_horizontal", + "x": 90, + "y": 90 + }, + "axis=y": { + "model": "unicopia:block/golden_oak_log" + }, + "axis=z": { + "model": "unicopia:block/golden_oak_log_horizontal", + "x": 90 + } + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/golden_oak_sapling.json b/src/main/resources/assets/unicopia/blockstates/golden_oak_sapling.json new file mode 100644 index 00000000..7288f548 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_oak_sapling.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "unicopia:block/golden_oak_sapling" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/blockstates/golden_oak_sprout.json b/src/main/resources/assets/unicopia/blockstates/golden_oak_sprout.json new file mode 100644 index 00000000..f88ff7ea --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/golden_oak_sprout.json @@ -0,0 +1,28 @@ +{ + "variants": { + "age=0": { + "model": "unicopia:block/apple_sprout_stage0" + }, + "age=1": { + "model": "unicopia:block/apple_sprout_stage1" + }, + "age=2": { + "model": "unicopia:block/apple_sprout_stage2" + }, + "age=3": { + "model": "unicopia:block/apple_sprout_stage3" + }, + "age=4": { + "model": "unicopia:block/apple_sprout_stage4" + }, + "age=5": { + "model": "unicopia:block/apple_sprout_stage5" + }, + "age=6": { + "model": "unicopia:block/apple_sprout_stage6" + }, + "age=7": { + "model": "unicopia:block/apple_sprout_stage7" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 97196487..d516f4d9 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -123,6 +123,7 @@ "item.unicopia.green_apple_seeds": "Granny Smith Apple Seeds", "item.unicopia.sweet_apple_seeds": "Sweet Apple Seeds", "item.unicopia.sour_apple_seeds": "Sour Apple Seeds", + "item.unicopia.golden_oak_seeds": "Golden Oak Seeds", "item.unicopia.apple_pie_hoof": "Apple Pie with a Hoofprint", "item.unicopia.apple_pie_slice": "Slice Of Apple Pie", "item.unicopia.candied_apple": "Candied Apple", @@ -248,6 +249,10 @@ "block.unicopia.weather_vane": "Weather Vane", "block.unicopia.curing_joke": "Curing Joke", "block.unicopia.gold_root": "Gold Root", + "block.unicopia.golden_oak_sprout": "Golden Oak Sprout", + "block.unicopia.golden_oak_sapling": "Golden Oak Sapling", + "block.unicopia.golden_oak_leaves": "Golden Oak Leaves", + "block.unicopia.golden_oak_log": "Golden Oak Log", "block.unicopia.mango": "Mango", "block.unicopia.mango_leaves": "Mango Leaves", "block.unicopia.mango_sapling": "Mango Sapling", diff --git a/src/main/resources/assets/unicopia/models/block/golden_apple.json b/src/main/resources/assets/unicopia/models/block/golden_apple.json new file mode 100644 index 00000000..a2a567a9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_apple.json @@ -0,0 +1,6 @@ +{ + "parent": "unicopia:block/fruit", + "textures": { + "cross": "minecraft:item/golden_apple" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_oak_leaves.json b/src/main/resources/assets/unicopia/models/block/golden_oak_leaves.json new file mode 100644 index 00000000..0c8f2bd9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_oak_leaves.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/leaves", + "textures": { + "all": "unicopia:block/golden_oak_leaves" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_oak_log.json b/src/main/resources/assets/unicopia/models/block/golden_oak_log.json new file mode 100644 index 00000000..d4909cb0 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_oak_log.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/cube_column", + "textures": { + "end": "unicopia:block/golden_oak_log_top", + "side": "unicopia:block/golden_oak_log" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_oak_log_horizontal.json b/src/main/resources/assets/unicopia/models/block/golden_oak_log_horizontal.json new file mode 100644 index 00000000..8dc80c7b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_oak_log_horizontal.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/cube_column_horizontal", + "textures": { + "end": "unicopia:block/golden_oak_log_top", + "side": "unicopia:block/golden_oak_log" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/golden_oak_sapling.json b/src/main/resources/assets/unicopia/models/block/golden_oak_sapling.json new file mode 100644 index 00000000..c4c2ba33 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/golden_oak_sapling.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cross", + "textures": { + "cross": "unicopia:item/golden_oak_sapling" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/golden_oak_leaves.json b/src/main/resources/assets/unicopia/models/item/golden_oak_leaves.json new file mode 100644 index 00000000..14afba2f --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/golden_oak_leaves.json @@ -0,0 +1,3 @@ +{ + "parent": "unicopia:block/golden_oak_leaves" +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/golden_oak_log.json b/src/main/resources/assets/unicopia/models/item/golden_oak_log.json new file mode 100644 index 00000000..699482c5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/golden_oak_log.json @@ -0,0 +1,3 @@ +{ + "parent": "unicopia:block/golden_oak_log" +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/item/golden_oak_sapling.json b/src/main/resources/assets/unicopia/models/item/golden_oak_sapling.json new file mode 100644 index 00000000..cc4be552 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/golden_oak_sapling.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/golden_oak_sapling" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/golden_oak_seeds.json b/src/main/resources/assets/unicopia/models/item/golden_oak_seeds.json new file mode 100644 index 00000000..16cb1fc6 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/golden_oak_seeds.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/golden_oak_seeds" + } +} diff --git a/src/main/resources/assets/unicopia/textures/block/golden_oak_leaves.png b/src/main/resources/assets/unicopia/textures/block/golden_oak_leaves.png new file mode 100644 index 0000000000000000000000000000000000000000..2242b16857eb602a0f29422b0754cb9960283603 GIT binary patch literal 6220 zcmeHLX;f547JeYeHpaN1xDeYWZs?=mzDdUg2(;4d#}G42@|JqgN-uOb(5TUTvF;Np&WIUOP8XY%6VoY#CL~|zKGKtHKsFBH-1WBe|vj{UeXO3r#zkHnPs#kUE zt9!q?_g3+|X(@@lLI;N;2-3@x6rTa>C$$pX6aEfs{#k_88IWyKO{Qoh7`{MBzW~Hf zlYzyrOWWVC2hs!fErz8pY-=)F8SEG6t5?AGkoNXk*dE<3qs_tn{E=R;E{BDH?f$Ud z35#V(+ge9pTYG|if2=>^ljx`uP3ANm5rbnfBm?JT1SMd&fO9&W6>wg_S-8I6oA))+ zkQ!e^WvJh)mbkbyQ(TdB?=3tWd?ubPL@ZWhp zt|_@YwDC~yCkKabe?F&RVx5)Tl)UuIuXaYvNm_C3(wS$6EBs#l_^kM<6`*K2NVeRQ&Umg!Q!nbynPFnMC$ zz`Xj%t1agXHjJ8hd(iuz|MjOCJ=V8AeYmmyt;C5xGme6W`nAT3&Y+07gpygbO^0qt zLmF$Zz2Q8120e4=%BOqYTU8gNi`ze?0SQ`i)KmJ0b1&a)sN?@MZ}#-&7C*lxXGz`d zVjJ|EQ(s2gd%s& zcN9AFedZ{#0ptTK6m`K>@or0M5!X3GqafE}b@*mMvb#yTE$V%;y6L8k_{{052%O%D z+fBN|cAqemGMj~XyIiQLXNouKwe{edit6=b?ajZK`zXqx9pnk6}&;o18_ z(}2?jJyFB-R6&Iv@f-I-;#=Nt8%S+{f848utg-D14x z1`xC}4`+4c;jtJdU~TGaaAvz=QA_@pv=%(NSSSy-JjvpM>zDYt+9E0w6tumyz2;kd zp`_FKfD8-L<3N@(Zp&N-Qa|yowSeNuU&~0#ssn z45a}MNhM?%NOE^{mtA!ii%u{p7xD(5t8#Si8ba5Wgb^#+u6vt2k%fdN< zCIpHe3(qk~nBmtqkp~3LVSq&$f?-gKq!pB7X$~bAiDpz)Avyx4lfZ{yA}>(Pqcf3_ zR1#1kiz+;?#?1;;U(2a?EB4|x}Pfg}VRe{?4D48;-@%b^^`vnVC16e_7K zjp7O|tCT{D7-&m04`mlgJPU_cEx3z(dt^TXN5vQwz@THGqjE4UQ$R#{R)Q9bJWy$x z;wXad9$o*Bqqdo-SxG%Q6BS+&MN(l=QHAFgMPmTvNl8Lg4kvLL9vXm`9t%b2hJp2KKJBO5uepRy_jr*9;`cFKkLh|K1|CTG zv3EVD>wy?}AmPW}_0Z@Fz5js<*x>(_#qjgfLEwK6e!A)@O-+nP{)N1W96a4JNc)@> z;z)Ylg&<-5wBmdA?TW4-#-2NTn$D~BZ&X+P4SbmxKBW4PSTq6h`H*< z*G?Xp{Fikp4PV&PKGdBq|MJHJ`<@OBG=&%rh8`d7Z&)@tGXpq#{| z)~VwwQhVwWkY5&J-Ep{XpLnlotN&pB=y0{N z{8G{9*-uI4i+*J(#+iq|TM=Ej@j%w)jNmQYoU?cJmFJhQ+SU60iu4t!M~+o)4ndA) zc&Cb<*~7xqPE?jJ8oq|BcsFoxkJJ!k#6P|{8NWekx>7s-&gMBu+m=lGqb=*shquwE zhVoO!UH9G$3?oNf9a%n7uE&TTnCA{9aJY`5U`S!z;qBd7G-YuZ+B*j4*E*J!psV zd`ynSym$#;+^}?WfdA2- dst?}_szg7?ySne}Cg`<@DIq0(@8p*j{2Tn{Jxc%p literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/block/golden_oak_log.png b/src/main/resources/assets/unicopia/textures/block/golden_oak_log.png new file mode 100644 index 0000000000000000000000000000000000000000..414dd04ceb1e5f10d54be9265ad3f5856f484786 GIT binary patch literal 6881 zcmeHMc~}$I7N23u0#v}@_EbV#h}MvuEJ6?=KvaZa#HD?NMaHtsQ7R}F?D$= z+Sc7zx8hP)s-ShNYhRVBweI4I)*=cOL|fmPNkFjnd(Zd1{^#&b=FUCm{OPZJg;IYd^Hl zz)dCplA%Q)H$9V7DQQeWB%c>4jDmQcKqTk!<$NiVFOds`ay}ot(N(W9f>4Bp{EoP> z38W@Y1Z)#0qTR%)KeW9)`4Y@(2T$^>*E8~e6S-v!M-8k{5Ze1 zYK+1f%MeVNzna>u|F(mn=@m8Ig<9)W8ogcn4jnu7=o!K8)mtQvl1OFp_ylEQQnG5m z5Y5nxVZ%p^)ah|UW|ncZ%|32?t|M>4wCRO2W){tIE?8K+Xz{0?eZIuC=8LuK)^FJO z<@OyrckM3yde7cNhmU-F^w{weCx1A5?);Aze!6(6^5(7EckbS+y8qzuliIpppVl`# zYb1CPJew{zv)6b911~QhA8#KD!3*`uC5Q+6_=fZSLgLgEY;0SWU<#GdfBx!i2Wj1e z={2ERYX!YscTr`J#{@N!*?&iD>i@*7nb<2{myiH&6eiC*7>Py7l#F5bC@)~=kJK38D(AlnncHnMlhy$0J(`B3r zW$64KU$R~M54@-<0lZ@?U5mhU&ILI87TzeTaycK*+ZP6wos&9RA#{b}_C|O+6Gl&h z$L#$3MAZ3}%Yp#u@%pMlh}&P#kS=Gt%5{&Fki!*45cHJ`t54b0O1)|{V0;xT7i@4j zpT*Z%Tu!!EtD!vU)v1+n;YvoD4+J@mU0bqKt;`?s>|h{6{ZXgMMH;}ZYE#$^9!C{* z`M|uQMh9s>PA&JOx)!o>SvzWg{OU%jTFF3rfY*td1ORqKTwRpqayGWDoDKko6%Q9c zPWlFxJLe=I@=KVX0Qwlu54FmzJGfs|Q0IcF!}GHl@F!P{e-hgrOQ|5G7!IZbiYg~y zSQ-R#AbG6*j+5=$5#-L~D5L=7J^(jXGR#8#yy=7}U6c$>(P9V+cz$RtLp`#XbQl0R zj820ILEc0|4H(}kUTjk`GPGi1j&{^e>kSaau}SVy9YB<~XTCt%^0w6{n*dRhlf7vc z6A-Q`g4}cT1GqyK&jL+rQp;Fix}pe}0eH;km31I@(!nJfwQ@p4xTmhtz{7gT$zDYR z8c$QFdPISO&sZDco`X~nXlnjNUr^z3&2#{PYWgRb9)(dorcf|d*62lQWgg`^`1Nb8 z_v>SLiWmk&TJr+ziT4gI218Aoy}ebZUQvR&J|w@kG4^7O0(Sr14K zR)L)?t9d{d3IWs}R*+vo*3N696Z80naw1*dxEjI%@>XgEouO8mhQlnQuWb(iSlXx6 zg5_2yL#O!4VcVGAfEAMkN{B|RgX0Upec!E08nj{lIH18(%Dn^;ENWw8I3%%km4eZF zHU8hcu!gp5Ko-I+&=GYGE zrer!Z4J?84dZ+Odu8H@J}9>bvZ z(b5FnH*9cC7j!<7B}v{k-&Ondynh_2J#{<%#89tp$Gm!^s9W<=U>hS4Q5G<|99dN^S>QEYt-5;(gV%)hoW=^V{XK zORl#m8f)G9_?Tc5{BCy`HN)-0QwGU(=A1}OZ`R_GjvNd5dnP*8VZn4`a640rXBtg0 ztVdbDSmXwrcc$3Ye0_eeYU=}VvlE=--;kJyh*_Gn~#FNk)BW#*H3(if) zZRT-S9j+XYo9x|MLg;nP@s@GcY?6*%$HlYp9B{S4sQgw#5~q~r2ttBPV~&N408TQ8Sj6~py;*0}%gH8Rs24_I zA_+&v*J?Q;Ehged*s}ZUa%Wjz!0i_2hp-719w0s#yk45Pq8!g7s zYQ+W)E|eK0Qi)I^6-JRzdYwGcY|X)7I*mD4CeF2(GD!m=xI8XR6~hul@?Q0%Wn*>& z1jMig8BOCHuM8Sv4nD+=5o+=!Jh50N70dWhVxb_aMRW#kwLv8knB;kr3@4^V4rD-B zjHpupAp3!fJl=|9cC%GuHfP7Mnop|D<`h^?dd!X`V0Iipo6oMArnBq&Jf58APJn<* zG3$+ny#M4)EFNYw_`{SZ8*MOt9@*tyQA6;t?l<>qwvk*)OeVP~WEFjFQDyN9P-oBEA!C;EPf{nc>_H^Hx*ngoYmXcOq@B;<KsXOVNo?3)bDuujj) zFVLQRzxnvn8T}_Nxw&a)!Mue^NR5hZEtt&^*0$m5^`7CP&Kt0KPV zinFMA?VXrgk_*!x(3gJNxi0T3#)e+uTX(b0hdN6qb)B92arWm$Os#PHhlfA-X#|QK z%KYQF7Gr_a!z3+;Cc%FgWCd$Qjr!|n{Gt++9BaY@*N#brNsS;ZQ>y!yy-!Iool igr(Con?hcApSiPm=q=qw_y+`nsFZ^fw)Fpa>c0W+)X@F_ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/block/golden_oak_log_top.png b/src/main/resources/assets/unicopia/textures/block/golden_oak_log_top.png new file mode 100644 index 0000000000000000000000000000000000000000..be183711ca43ba9a8e6395457654d477366aaac5 GIT binary patch literal 6689 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 Date: Fri, 19 Jan 2024 15:27:28 +0000 Subject: [PATCH 34/98] Remove old temporary assets --- assets/models/cloud_bed.java | 50 -------- assets/models/cloud_chest.java | 32 ----- assets/models/ignimious_bulb.java | 69 ----------- assets/models/slime_pustule_cap.json | 46 ------- assets/models/slime_pustule_pod.json | 137 --------------------- assets/models/slime_pustule_rope.json | 22 ---- assets/models/tentacle.java | 169 -------------------------- 7 files changed, 525 deletions(-) delete mode 100644 assets/models/cloud_bed.java delete mode 100644 assets/models/cloud_chest.java delete mode 100644 assets/models/ignimious_bulb.java delete mode 100644 assets/models/slime_pustule_cap.json delete mode 100644 assets/models/slime_pustule_pod.json delete mode 100644 assets/models/slime_pustule_rope.json delete mode 100644 assets/models/tentacle.java diff --git a/assets/models/cloud_bed.java b/assets/models/cloud_bed.java deleted file mode 100644 index 88008846..00000000 --- a/assets/models/cloud_bed.java +++ /dev/null @@ -1,50 +0,0 @@ -// Made with Blockbench 4.8.3 -// Exported for Minecraft version 1.17 or later with Mojang mappings -// Paste this class into your mod and generate all required imports - - -public class cloud_bed extends EntityModel { - // This layer location should be baked with EntityRendererProvider.Context in the entity renderer and passed into this model's constructor - public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(new ResourceLocation("modid", "cloud_bed"), "main"); - private final ModelPart head; - private final ModelPart foot; - - public cloud_bed(ModelPart root) { - this.head = root.getChild("head"); - this.foot = root.getChild("foot"); - } - - public static LayerDefinition createBodyLayer() { - MeshDefinition meshdefinition = new MeshDefinition(); - PartDefinition partdefinition = meshdefinition.getRoot(); - - PartDefinition head = partdefinition.addOrReplaceChild("head", CubeListBuilder.create(), PartPose.offset(0.0F, 24.0F, 0.0F)); - - PartDefinition main_r1 = head.addOrReplaceChild("main_r1", CubeListBuilder.create().texOffs(0, 0).addBox(-8.0F, -21.0F, -9.0F, 16.0F, 13.0F, 6.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, -16.0F, -1.5708F, 0.0F, 0.0F)); - - PartDefinition head_board = head.addOrReplaceChild("head_board", CubeListBuilder.create().texOffs(52, 24).addBox(7.0F, -13.0F, -3.0F, 2.0F, 13.0F, 3.0F, new CubeDeformation(0.0F)) - .texOffs(0, 43).addBox(-6.0F, -15.0F, -2.0F, 13.0F, 15.0F, 3.0F, new CubeDeformation(0.0F)) - .texOffs(52, 24).addBox(-8.0F, -13.0F, -3.0F, 2.0F, 13.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(-0.5F, 0.0F, 7.0F)); - - PartDefinition foot = partdefinition.addOrReplaceChild("foot", CubeListBuilder.create(), PartPose.offset(0.0F, 24.0F, 0.0F)); - - PartDefinition main_r2 = foot.addOrReplaceChild("main_r2", CubeListBuilder.create().texOffs(0, 22).addBox(-8.0F, -8.0F, -9.0F, 16.0F, 15.0F, 6.0F, new CubeDeformation(0.0F)), PartPose.offsetAndRotation(0.0F, 0.0F, 0.0F, -1.5708F, 0.0F, 0.0F)); - - PartDefinition head_board2 = foot.addOrReplaceChild("head_board2", CubeListBuilder.create().texOffs(52, 40).addBox(6.0F, -13.0F, -2.0F, 3.0F, 10.0F, 3.0F, new CubeDeformation(0.0F)) - .texOffs(0, 43).addBox(-6.0F, -15.0F, -3.0F, 13.0F, 11.0F, 3.0F, new CubeDeformation(0.0F)) - .texOffs(52, 40).addBox(-8.0F, -13.0F, -2.0F, 3.0F, 10.0F, 3.0F, new CubeDeformation(0.0F)), PartPose.offset(-0.5F, 3.0F, -7.0F)); - - return LayerDefinition.create(meshdefinition, 64, 64); - } - - @Override - public void setupAnim(T entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { - - } - - @Override - public void renderToBuffer(PoseStack poseStack, VertexConsumer vertexConsumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { - head.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); - foot.render(poseStack, vertexConsumer, packedLight, packedOverlay, red, green, blue, alpha); - } -} \ No newline at end of file diff --git a/assets/models/cloud_chest.java b/assets/models/cloud_chest.java deleted file mode 100644 index 87e684a2..00000000 --- a/assets/models/cloud_chest.java +++ /dev/null @@ -1,32 +0,0 @@ -// Made with Blockbench 4.8.3 -// Exported for Minecraft version 1.17+ for Yarn -// Paste this class into your mod and generate all required imports -public class cloud_chest extends EntityModel { - private final ModelPart lid; - private final ModelPart lock_r1; - private final ModelPart bb_main; - public cloud_chest(ModelPart root) { - this.lid = root.getChild("lid"); - this.bb_main = root.getChild("bb_main"); - } - public static TexturedModelData getTexturedModelData() { - ModelData modelData = new ModelData(); - ModelPartData modelPartData = modelData.getRoot(); - ModelPartData lid = modelPartData.addChild("lid", ModelPartBuilder.create().uv(0, 0).cuboid(-1.0F, -2.0F, 13.8F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F)) - .uv(0, 0).cuboid(-1.0F, -1.0F, 14.0F, 2.0F, 2.0F, 1.0F, new Dilation(0.0F)) - .uv(0, 0).cuboid(-7.0F, -5.0F, 0.0F, 14.0F, 5.0F, 14.0F, new Dilation(0.3F)), ModelTransform.of(0.0F, 16.0F, -7.0F, 1.0908F, 0.0F, 0.0F)); - - ModelPartData lock_r1 = lid.addChild("lock_r1", ModelPartBuilder.create().uv(0, 0).cuboid(-2.0F, -4.0F, -0.5F, 2.0F, 4.0F, 1.0F, new Dilation(0.0F)), ModelTransform.of(-2.0F, 1.0F, 14.3F, 0.0F, 0.0F, 1.5708F)); - - ModelPartData bb_main = modelPartData.addChild("bb_main", ModelPartBuilder.create().uv(0, 19).cuboid(-7.0F, -10.0F, -7.0F, 14.0F, 10.0F, 14.0F, new Dilation(0.0F)), ModelTransform.pivot(0.0F, 24.0F, 0.0F)); - return TexturedModelData.of(modelData, 64, 64); - } - @Override - public void setAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { - } - @Override - public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) { - lid.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); - bb_main.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); - } -} \ No newline at end of file diff --git a/assets/models/ignimious_bulb.java b/assets/models/ignimious_bulb.java deleted file mode 100644 index 8eb9b255..00000000 --- a/assets/models/ignimious_bulb.java +++ /dev/null @@ -1,69 +0,0 @@ -// Made with Blockbench 4.8.3 -// Exported for Minecraft version 1.17+ for Yarn -// Paste this class into your mod and generate all required imports -public class ignimious_bulb extends EntityModel { - private final ModelPart head; - private final ModelPart cube_r1; - private final ModelPart head_r1; - private final ModelPart leaves; - private final ModelPart cube_r2; - private final ModelPart cube_r3; - private final ModelPart cube_r4; - private final ModelPart cube_r5; - private final ModelPart cube_r6; - private final ModelPart cube_r7; - private final ModelPart cube_r8; - private final ModelPart cube_r9; - private final ModelPart cube_r10; - private final ModelPart cube_r11; - private final ModelPart cube_r12; - private final ModelPart cube_r13; - public ignimious_bulb(ModelPart root) { - this.head = root.getChild("head"); - this.leaves = root.getChild("leaves"); - } - public static TexturedModelData getTexturedModelData() { - ModelData modelData = new ModelData(); - ModelPartData modelPartData = modelData.getRoot(); - ModelPartData head = modelPartData.addChild("head", ModelPartBuilder.create().uv(0, 0).cuboid(-24.0432F, -0.9905F, -48.1305F, 48.0F, 23.0F, 48.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 7.0F, 24.0F, -0.1308F, 0.0057F, 0.0433F)); - - ModelPartData cube_r1 = head.addChild("cube_r1", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, -27.0F, 5.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(1.3731F, -21.6152F, -11.61F, -1.9802F, 0.1003F, 0.0006F)); - - ModelPartData head_r1 = head.addChild("head_r1", ModelPartBuilder.create().uv(0, 71).cuboid(-23.0F, -16.0F, -46.0F, 46.0F, 23.0F, 46.0F, new Dilation(0.0F)), ModelTransform.of(-0.0432F, -0.9905F, -0.1305F, -0.1309F, 0.0F, 0.0F)); - - ModelPartData leaves = modelPartData.addChild("leaves", ModelPartBuilder.create(), ModelTransform.pivot(0.0F, 27.0F, -1.0F)); - - ModelPartData cube_r2 = leaves.addChild("cube_r2", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, 3.0F, -55.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.8316F, -1.0127F, -3.0858F)); - - ModelPartData cube_r3 = leaves.addChild("cube_r3", ModelPartBuilder.create().uv(112, 0).cuboid(-14.0F, 12.0F, -60.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.6063F, -0.075F, -3.1196F)); - - ModelPartData cube_r4 = leaves.addChild("cube_r4", ModelPartBuilder.create().uv(112, 0).cuboid(-20.0F, 1.0F, -51.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.7079F, -1.4622F, 0.4842F)); - - ModelPartData cube_r5 = leaves.addChild("cube_r5", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, 6.0F, -58.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.4967F, 0.1003F, 0.0006F)); - - ModelPartData cube_r6 = leaves.addChild("cube_r6", ModelPartBuilder.create().uv(-64, 140).cuboid(-30.0F, 7.0F, -83.0F, 64.0F, 0.0F, 64.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.8708F, -0.1388F, -3.0651F)); - - ModelPartData cube_r7 = leaves.addChild("cube_r7", ModelPartBuilder.create().uv(-64, 140).cuboid(-30.0F, 7.0F, -83.0F, 64.0F, 0.0F, 64.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -1.084F, -1.4961F, 0.807F)); - - ModelPartData cube_r8 = leaves.addChild("cube_r8", ModelPartBuilder.create().uv(-64, 140).cuboid(-30.0F, 3.0F, -83.0F, 64.0F, 0.0F, 64.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.2651F, -0.035F, -0.0332F)); - - ModelPartData cube_r9 = leaves.addChild("cube_r9", ModelPartBuilder.create().uv(-64, 140).cuboid(-30.0F, 7.0F, -83.0F, 64.0F, 0.0F, 64.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -1.95F, 1.4932F, -1.6857F)); - - ModelPartData cube_r10 = leaves.addChild("cube_r10", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, 10.0F, -57.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -2.5197F, 1.466F, -2.0202F)); - - ModelPartData cube_r11 = leaves.addChild("cube_r11", ModelPartBuilder.create().uv(112, 0).cuboid(-8.0F, 3.0F, -61.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.9628F, 0.6133F, -2.978F)); - - ModelPartData cube_r12 = leaves.addChild("cube_r12", ModelPartBuilder.create().uv(112, 0).cuboid(-13.0F, 6.0F, -60.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.4768F, 0.8786F, -0.1186F)); - - ModelPartData cube_r13 = leaves.addChild("cube_r13", ModelPartBuilder.create().uv(112, 0).cuboid(-16.0F, 0.0F, -59.0F, 32.0F, 0.0F, 32.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, -0.2911F, -0.5857F, 0.0605F)); - return TexturedModelData.of(modelData, 256, 256); - } - @Override - public void setAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { - } - @Override - public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) { - head.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); - leaves.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); - } -} \ No newline at end of file diff --git a/assets/models/slime_pustule_cap.json b/assets/models/slime_pustule_cap.json deleted file mode 100644 index f337aa4f..00000000 --- a/assets/models/slime_pustule_cap.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "parent": "minecraft:block/cube_all", - "textures": { - "all": "unicopia:block/slime_pustule", - "particle": "unicopia:block/slime_pustule" - }, - "elements": [ - { - "from": [7, 14.9, 7], - "to": [12, 16, 12], - "faces": { - "north": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "east": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "south": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "west": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "from": [3, 15, 4], - "to": [9, 16, 10], - "faces": { - "north": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "east": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "south": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "west": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "rope", - "from": [7, 10, 7], - "to": [9, 15, 9], - "faces": { - "north": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "east": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "south": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "west": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "up": {"uv": [2, 2, 4, 4], "texture": "#all"}, - "down": {"uv": [2, 2, 4, 4], "texture": "#all"} - } - } - ] -} \ No newline at end of file diff --git a/assets/models/slime_pustule_pod.json b/assets/models/slime_pustule_pod.json deleted file mode 100644 index 7c15dd7b..00000000 --- a/assets/models/slime_pustule_pod.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "parent": "minecraft:block/cube_all", - "textures": { - "all": "unicopia:block/slime_pustule", - "particle": "unicopia:block/slime_pustule" - }, - "elements": [ - { - "name": "rope", - "from": [7.5, 0, 7.5], - "to": [8.5, 16, 8.5], - "faces": { - "north": {"uv": [12, 0, 13, 16], "texture": "#all"}, - "east": {"uv": [13, 0, 14, 16], "texture": "#all"}, - "south": {"uv": [15, 0, 16, 16], "texture": "#all"}, - "west": {"uv": [14, 0, 15, 16], "texture": "#all"}, - "up": {"uv": [13, 0, 14, 1], "texture": "#all"}, - "down": {"uv": [12, 0, 13, 1], "texture": "#all"} - } - }, - { - "from": [7, 14.9, 7], - "to": [12, 16, 12], - "faces": { - "north": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "east": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "south": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "west": {"uv": [0, 0, 6, 1.1], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "from": [3, 15, 4], - "to": [9, 16, 10], - "faces": { - "north": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "east": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "south": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "west": {"uv": [0, 0, 6, 1], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "rope", - "from": [7, 10, 7], - "to": [9, 15, 9], - "faces": { - "north": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "east": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "south": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "west": {"uv": [1, 7, 5, 13], "texture": "#all"}, - "up": {"uv": [2, 2, 4, 4], "texture": "#all"}, - "down": {"uv": [2, 2, 4, 4], "texture": "#all"} - } - }, - { - "name": "rope", - "from": [7, 15, 7], - "to": [9, 20, 9], - "faces": { - "north": {"uv": [2, 7, 4, 13], "texture": "#all"}, - "east": {"uv": [2, 7, 4, 13], "texture": "#all"}, - "south": {"uv": [2, 7, 4, 13], "texture": "#all"}, - "west": {"uv": [2, 7, 4, 13], "texture": "#all"}, - "up": {"uv": [2, 2, 4, 4], "texture": "#all"}, - "down": {"uv": [2, 2, 4, 4], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [5, 10, 5], - "to": [11, 13, 11], - "faces": { - "north": {"uv": [0, 6, 6, 9], "texture": "#all"}, - "east": {"uv": [0, 6, 6, 9], "texture": "#all"}, - "south": {"uv": [0, 6, 6, 9], "texture": "#all"}, - "west": {"uv": [0, 6, 6, 9], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [6, 13, 6], - "to": [10, 15, 10], - "faces": { - "north": {"uv": [0, 6, 6, 8], "texture": "#all"}, - "east": {"uv": [0, 6, 6, 8], "texture": "#all"}, - "south": {"uv": [0, 6, 6, 8], "texture": "#all"}, - "west": {"uv": [0, 6, 6, 8], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [5, 0, 5], - "to": [11, 1, 11], - "faces": { - "north": {"uv": [0, 13, 6, 14], "texture": "#all"}, - "east": {"uv": [0, 13, 6, 14], "texture": "#all"}, - "south": {"uv": [0, 13, 6, 14], "texture": "#all"}, - "west": {"uv": [0, 13, 6, 14], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [4, 1, 4], - "to": [12, 10, 12], - "faces": { - "north": {"uv": [0, 6, 6, 14], "texture": "#all"}, - "east": {"uv": [0, 6, 6, 14], "texture": "#all"}, - "south": {"uv": [0, 6, 6, 14], "texture": "#all"}, - "west": {"uv": [0, 6, 6, 14], "texture": "#all"}, - "up": {"uv": [0, 0, 6, 6], "texture": "#all"}, - "down": {"uv": [0, 0, 6, 6], "texture": "#all"} - } - }, - { - "name": "drop", - "from": [5, 2, 5], - "to": [11, 9, 11], - "faces": { - "north": {"uv": [7, 7, 11, 13], "texture": "#all"}, - "east": {"uv": [7, 7, 11, 13], "texture": "#all"}, - "south": {"uv": [7, 7, 11, 13], "texture": "#all"}, - "west": {"uv": [7, 7, 11, 13], "texture": "#all"}, - "up": {"uv": [7, 1, 11, 5], "texture": "#all"}, - "down": {"uv": [7, 1, 11, 5], "texture": "#all"} - } - } - ] -} \ No newline at end of file diff --git a/assets/models/slime_pustule_rope.json b/assets/models/slime_pustule_rope.json deleted file mode 100644 index aab23dfc..00000000 --- a/assets/models/slime_pustule_rope.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "parent": "minecraft:block/cube_all", - "textures": { - "all": "unicopia:block/slime_pustule", - "particle": "unicopia:block/slime_pustule" - }, - "elements": [ - { - "name": "rope", - "from": [7.5, 0, 7.5], - "to": [8.5, 16, 8.5], - "faces": { - "north": {"uv": [12, 0, 13, 16], "texture": "#all"}, - "east": {"uv": [13, 0, 14, 16], "texture": "#all"}, - "south": {"uv": [15, 0, 16, 16], "texture": "#all"}, - "west": {"uv": [14, 0, 15, 16], "texture": "#all"}, - "up": {"uv": [13, 0, 14, 1], "texture": "#all"}, - "down": {"uv": [12, 0, 13, 1], "texture": "#all"} - } - } - ] -} \ No newline at end of file diff --git a/assets/models/tentacle.java b/assets/models/tentacle.java deleted file mode 100644 index 81792fec..00000000 --- a/assets/models/tentacle.java +++ /dev/null @@ -1,169 +0,0 @@ -// Made with Blockbench 4.8.3 -// Exported for Minecraft version 1.17+ for Yarn -// Paste this class into your mod and generate all required imports -public class tentacle extends EntityModel { - private final ModelPart bone_a; - private final ModelPart flower_4; - private final ModelPart cube_r1; - private final ModelPart cube_r2; - private final ModelPart flower_8; - private final ModelPart cube_r3; - private final ModelPart cube_r4; - private final ModelPart flower_3; - private final ModelPart cube_r5; - private final ModelPart cube_r6; - private final ModelPart flower_7; - private final ModelPart cube_r7; - private final ModelPart cube_r8; - private final ModelPart flower_2; - private final ModelPart cube_r9; - private final ModelPart cube_r10; - private final ModelPart flower_6; - private final ModelPart cube_r11; - private final ModelPart cube_r12; - private final ModelPart flower_1; - private final ModelPart cube_r13; - private final ModelPart cube_r14; - private final ModelPart flower_5; - private final ModelPart cube_r15; - private final ModelPart cube_r16; - private final ModelPart bone_b; - private final ModelPart bone_c; - private final ModelPart flower_9; - private final ModelPart cube_r17; - private final ModelPart cube_r18; - private final ModelPart bone_d; - private final ModelPart flower_10; - private final ModelPart cube_r19; - private final ModelPart cube_r20; - private final ModelPart bone_e; - private final ModelPart bone_f; - private final ModelPart flower_11; - private final ModelPart cube_r21; - private final ModelPart cube_r22; - private final ModelPart bone_g; - private final ModelPart bone_h; - private final ModelPart flower_12; - private final ModelPart cube_r23; - private final ModelPart cube_r24; - private final ModelPart flower_13; - private final ModelPart cube_r25; - private final ModelPart cube_r26; - private final ModelPart flower_14; - private final ModelPart cube_r27; - private final ModelPart cube_r28; - public tentacle(ModelPart root) { - this.bone_a = root.getChild("bone_a"); - } - public static TexturedModelData getTexturedModelData() { - ModelData modelData = new ModelData(); - ModelPartData modelPartData = modelData.getRoot(); - ModelPartData bone_a = modelPartData.addChild("bone_a", ModelPartBuilder.create().uv(0, 0).cuboid(-7.0F, -10.0F, -7.0F, 14.0F, 16.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 24.0F, 0.0F, 0.0F, 0.0F, 0.0F)); - - ModelPartData flower_4 = bone_a.addChild("flower_4", ModelPartBuilder.create(), ModelTransform.of(6.0F, 0.6703F, -6.7725F, 1.5929F, -0.909F, -1.1179F)); - - ModelPartData cube_r1 = flower_4.addChild("cube_r1", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r2 = flower_4.addChild("cube_r2", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_8 = flower_4.addChild("flower_8", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.9259F, -0.8201F, -2.1974F)); - - ModelPartData cube_r3 = flower_8.addChild("cube_r3", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r4 = flower_8.addChild("cube_r4", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_3 = bone_a.addChild("flower_3", ModelPartBuilder.create(), ModelTransform.of(6.0F, 0.6703F, 4.2275F, -2.4079F, 0.2344F, -2.5374F)); - - ModelPartData cube_r5 = flower_3.addChild("cube_r5", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r6 = flower_3.addChild("cube_r6", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_7 = flower_3.addChild("flower_7", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, -2.4079F, 0.2344F, -2.5374F)); - - ModelPartData cube_r7 = flower_7.addChild("cube_r7", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r8 = flower_7.addChild("cube_r8", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_2 = bone_a.addChild("flower_2", ModelPartBuilder.create(), ModelTransform.of(-5.0F, 0.6703F, 4.2275F, -1.2698F, 0.9678F, -1.7981F)); - - ModelPartData cube_r9 = flower_2.addChild("cube_r9", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r10 = flower_2.addChild("cube_r10", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_6 = flower_2.addChild("flower_6", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, -1.2698F, 0.9678F, -1.7981F)); - - ModelPartData cube_r11 = flower_6.addChild("cube_r11", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r12 = flower_6.addChild("cube_r12", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_1 = bone_a.addChild("flower_1", ModelPartBuilder.create(), ModelTransform.of(-3.0F, 0.6703F, -7.7725F, 0.6103F, -0.0535F, -0.5864F)); - - ModelPartData cube_r13 = flower_1.addChild("cube_r13", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r14 = flower_1.addChild("cube_r14", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_5 = flower_1.addChild("flower_5", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, 0.6103F, -0.0535F, -0.5864F)); - - ModelPartData cube_r15 = flower_5.addChild("cube_r15", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r16 = flower_5.addChild("cube_r16", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData bone_b = bone_a.addChild("bone_b", ModelPartBuilder.create().uv(0, 30).cuboid(-6.0F, -18.0F, -6.0F, 12.0F, 19.0F, 12.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -9.0F, 0.0F, -0.2618F, 0.0F, 0.0F)); - - ModelPartData bone_c = bone_b.addChild("bone_c", ModelPartBuilder.create().uv(48, 20).cuboid(-5.0F, -23.0F, -5.0F, 10.0F, 23.0F, 10.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -16.0F, 0.0F, 0.0F, 0.0F, 0.0F)); - - ModelPartData flower_9 = bone_c.addChild("flower_9", ModelPartBuilder.create(), ModelTransform.of(4.0F, -15.6242F, -3.3435F, 2.9259F, -0.8201F, -2.4592F)); - - ModelPartData cube_r17 = flower_9.addChild("cube_r17", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r18 = flower_9.addChild("cube_r18", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData bone_d = bone_c.addChild("bone_d", ModelPartBuilder.create().uv(40, 53).cuboid(-4.0F, -23.0F, -4.0F, 8.0F, 21.0F, 8.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -18.0F, 0.0F, -0.1745F, 0.0F, 0.0F)); - - ModelPartData flower_10 = bone_d.addChild("flower_10", ModelPartBuilder.create(), ModelTransform.of(-2.0F, -17.1355F, 3.0055F, -2.8606F, -0.5942F, 2.4482F)); - - ModelPartData cube_r19 = flower_10.addChild("cube_r19", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r20 = flower_10.addChild("cube_r20", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData bone_e = bone_d.addChild("bone_e", ModelPartBuilder.create().uv(0, 61).cuboid(-3.0F, -25.0F, -3.0F, 6.0F, 22.0F, 6.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -18.0F, 0.0F, 0.1745F, 0.0F, 0.0F)); - - ModelPartData bone_f = bone_e.addChild("bone_f", ModelPartBuilder.create().uv(72, 53).cuboid(-3.0F, -17.0F, -3.0F, 6.0F, 15.0F, 6.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, -22.0F, 0.0F, 0.1745F, 0.0F, 0.0F)); - - ModelPartData flower_11 = bone_f.addChild("flower_11", ModelPartBuilder.create(), ModelTransform.of(-2.0F, -6.0251F, 0.478F, 2.7587F, 0.3479F, 2.5432F)); - - ModelPartData cube_r21 = flower_11.addChild("cube_r21", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r22 = flower_11.addChild("cube_r22", ModelPartBuilder.create().uv(86, 0).cuboid(-14.0F, 0.0F, 0.0F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(0.0F, 0.0F, 0.0F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData bone_g = bone_f.addChild("bone_g", ModelPartBuilder.create().uv(56, 0).cuboid(-2.6F, -16.0F, -2.6F, 5.2F, 15.0F, 5.2F, new Dilation(0.0F)), ModelTransform.of(0.0F, -15.0F, 0.0F, 0.3054F, 0.0F, 0.0F)); - - ModelPartData bone_h = bone_g.addChild("bone_h", ModelPartBuilder.create().uv(24, 61).cuboid(-2.1F, -17.0F, -2.1F, 4.2F, 15.0F, 4.2F, new Dilation(0.0F)), ModelTransform.of(0.0F, -13.0F, 0.0F, 0.2618F, 0.0F, 0.0F)); - - ModelPartData flower_12 = bone_h.addChild("flower_12", ModelPartBuilder.create(), ModelTransform.of(0.0F, -15.1462F, 0.6365F, 2.7587F, 0.3479F, 2.9795F)); - - ModelPartData cube_r23 = flower_12.addChild("cube_r23", ModelPartBuilder.create().uv(86, 0).cuboid(-50.2096F, 2.6718F, -13.6647F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r24 = flower_12.addChild("cube_r24", ModelPartBuilder.create().uv(86, 0).cuboid(-27.1617F, -35.2665F, 9.3832F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_13 = flower_12.addChild("flower_13", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, 2.7587F, 0.3479F, 2.5868F)); - - ModelPartData cube_r25 = flower_13.addChild("cube_r25", ModelPartBuilder.create().uv(86, 0).cuboid(-50.2096F, 2.6718F, -13.6647F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r26 = flower_13.addChild("cube_r26", ModelPartBuilder.create().uv(86, 0).cuboid(-27.1617F, -35.2665F, 9.3832F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 3.1416F, 0.7854F, 1.5708F)); - - ModelPartData flower_14 = flower_12.addChild("flower_14", ModelPartBuilder.create(), ModelTransform.of(0.0F, 0.0F, 0.0F, -1.9412F, -1.0444F, 1.7406F)); - - ModelPartData cube_r27 = flower_14.addChild("cube_r27", ModelPartBuilder.create().uv(86, 0).cuboid(-50.2096F, 2.6718F, -13.6647F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 1.5708F, 0.0F, 0.7854F)); - - ModelPartData cube_r28 = flower_14.addChild("cube_r28", ModelPartBuilder.create().uv(86, 0).cuboid(-27.1617F, -35.2665F, 9.3832F, 14.0F, 0.0F, 14.0F, new Dilation(0.0F)), ModelTransform.of(36.0432F, 15.5243F, -3.1436F, 3.1416F, 0.7854F, 1.5708F)); - return TexturedModelData.of(modelData, 128, 128); - } - @Override - public void setAngles(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { - } - @Override - public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) { - bone_a.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); - } -} \ No newline at end of file From f2b817d63573ccdca56e31e7420293af042cc4ad Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 19 Jan 2024 15:28:40 +0000 Subject: [PATCH 35/98] Make places hay bales edible --- assets/models/hay_bale.bbmodel | 1 + assets/models/hay_bale.json | 113 ++++++++ .../ability/EarthPonyGrowAbility.java | 13 +- .../unicopia/block/EdibleBlock.java | 214 +++++++++++++++ .../unicopia/block/UBlocks.java | 9 + .../block/cloud/PoreousCloudBlock.java | 4 +- .../block/cloud/PoreousCloudStairsBlock.java | 4 +- .../unicopia/block/cloud/Soakable.java | 12 - .../unicopia/block/cloud/SoggyCloudBlock.java | 6 +- .../block/cloud/SoggyCloudSlabBlock.java | 6 +- .../block/cloud/SoggyCloudStairsBlock.java | 6 +- .../unicopia/block/state/StateChange.java | 16 +- .../unicopia/block/state/StateUtil.java | 19 ++ .../unicopia/blockstates/hay_block.json | 31 +++ .../unicopia/blockstates/rice_block.json | 31 +++ .../unicopia/blockstates/straw_block.json | 31 +++ .../unicopia/models/block/hay_bale_bne.json | 22 ++ .../unicopia/models/block/hay_bale_bnw.json | 22 ++ .../unicopia/models/block/hay_bale_bse.json | 22 ++ .../unicopia/models/block/hay_bale_bsw.json | 22 ++ .../unicopia/models/block/hay_bale_tne.json | 22 ++ .../unicopia/models/block/hay_bale_tnw.json | 22 ++ .../unicopia/models/block/hay_bale_tse.json | 22 ++ .../unicopia/models/block/hay_bale_tsw.json | 22 ++ .../unicopia/models/block/rice_bale_bne.json | 8 + .../unicopia/models/block/rice_bale_bnw.json | 8 + .../unicopia/models/block/rice_bale_bse.json | 8 + .../unicopia/models/block/rice_bale_bsw.json | 8 + .../unicopia/models/block/rice_bale_tne.json | 8 + .../unicopia/models/block/rice_bale_tnw.json | 8 + .../unicopia/models/block/rice_bale_tse.json | 8 + .../unicopia/models/block/rice_bale_tsw.json | 8 + .../unicopia/models/block/straw_bale_bne.json | 8 + .../unicopia/models/block/straw_bale_bnw.json | 8 + .../unicopia/models/block/straw_bale_bse.json | 8 + .../unicopia/models/block/straw_bale_bsw.json | 8 + .../unicopia/models/block/straw_bale_tne.json | 8 + .../unicopia/models/block/straw_bale_tnw.json | 8 + .../unicopia/models/block/straw_bale_tse.json | 8 + .../unicopia/models/block/straw_bale_tsw.json | 8 + .../tags/items/foraging/edibles_filling.json | 4 +- .../minecraft/tags/blocks/mineable/hoe.json | 5 +- .../loot_tables/blocks/hay_block.json | 245 ++++++++++++++++++ .../food_types/forage_edible_filling.json | 3 +- 44 files changed, 1027 insertions(+), 50 deletions(-) create mode 100644 assets/models/hay_bale.bbmodel create mode 100644 assets/models/hay_bale.json create mode 100644 src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java create mode 100644 src/main/java/com/minelittlepony/unicopia/block/state/StateUtil.java create mode 100644 src/main/resources/assets/unicopia/blockstates/hay_block.json create mode 100644 src/main/resources/assets/unicopia/blockstates/rice_block.json create mode 100644 src/main/resources/assets/unicopia/blockstates/straw_block.json create mode 100644 src/main/resources/assets/unicopia/models/block/hay_bale_bne.json create mode 100644 src/main/resources/assets/unicopia/models/block/hay_bale_bnw.json create mode 100644 src/main/resources/assets/unicopia/models/block/hay_bale_bse.json create mode 100644 src/main/resources/assets/unicopia/models/block/hay_bale_bsw.json create mode 100644 src/main/resources/assets/unicopia/models/block/hay_bale_tne.json create mode 100644 src/main/resources/assets/unicopia/models/block/hay_bale_tnw.json create mode 100644 src/main/resources/assets/unicopia/models/block/hay_bale_tse.json create mode 100644 src/main/resources/assets/unicopia/models/block/hay_bale_tsw.json create mode 100644 src/main/resources/assets/unicopia/models/block/rice_bale_bne.json create mode 100644 src/main/resources/assets/unicopia/models/block/rice_bale_bnw.json create mode 100644 src/main/resources/assets/unicopia/models/block/rice_bale_bse.json create mode 100644 src/main/resources/assets/unicopia/models/block/rice_bale_bsw.json create mode 100644 src/main/resources/assets/unicopia/models/block/rice_bale_tne.json create mode 100644 src/main/resources/assets/unicopia/models/block/rice_bale_tnw.json create mode 100644 src/main/resources/assets/unicopia/models/block/rice_bale_tse.json create mode 100644 src/main/resources/assets/unicopia/models/block/rice_bale_tsw.json create mode 100644 src/main/resources/assets/unicopia/models/block/straw_bale_bne.json create mode 100644 src/main/resources/assets/unicopia/models/block/straw_bale_bnw.json create mode 100644 src/main/resources/assets/unicopia/models/block/straw_bale_bse.json create mode 100644 src/main/resources/assets/unicopia/models/block/straw_bale_bsw.json create mode 100644 src/main/resources/assets/unicopia/models/block/straw_bale_tne.json create mode 100644 src/main/resources/assets/unicopia/models/block/straw_bale_tnw.json create mode 100644 src/main/resources/assets/unicopia/models/block/straw_bale_tse.json create mode 100644 src/main/resources/assets/unicopia/models/block/straw_bale_tsw.json create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json diff --git a/assets/models/hay_bale.bbmodel b/assets/models/hay_bale.bbmodel new file mode 100644 index 00000000..7328ccde --- /dev/null +++ b/assets/models/hay_bale.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"java_block","box_uv":false},"name":"hay_bale_bse","parent":"","ambientocclusion":true,"front_gui_light":false,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":16,"height":16},"elements":[{"name":"bottom_south_east","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,0,8],"to":[16,8,16],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[0,8,8,16],"texture":0},"east":{"uv":[0,8,8,16],"texture":1},"south":{"uv":[8,8,16,16],"texture":1},"west":{"uv":[8,8,16,16],"texture":0},"up":{"uv":[8,8,16,16],"texture":0},"down":{"uv":[8,0,16,8],"texture":0}},"type":"cube","uuid":"523f4164-daa5-4b79-eec7-013710b7ae88"},{"name":"bottom_north_west","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,0,0],"to":[8,8,8],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[8,8,16,16],"texture":1},"east":{"uv":[8,8,16,16],"texture":0},"south":{"uv":[0,8,8,16],"texture":0},"west":{"uv":[0,8,8,16],"texture":1},"up":{"uv":[0,0,8,8],"texture":0},"down":{"uv":[0,8,8,16],"texture":0}},"type":"cube","uuid":"1fd288fe-a293-1ca2-a5b7-15d602eb44f6"},{"name":"top_south_east","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,8,8],"to":[16,16,16],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,8,8],"texture":0},"east":{"uv":[0,0,8,8],"texture":1},"south":{"uv":[8,0,16,8],"texture":1},"west":{"uv":[8,0,16,8],"texture":0},"up":{"uv":[8,8,16,16],"texture":0},"down":{"uv":[8,0,16,8],"texture":0}},"type":"cube","uuid":"4f48967d-c722-5593-6f77-079ee47d08af"},{"name":"top_north_west","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,8,0],"to":[8,16,8],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[8,0,16,8],"texture":1},"east":{"uv":[8,0,16,8],"texture":0},"south":{"uv":[0,0,8,8],"texture":0},"west":{"uv":[0,0,8,8],"texture":1},"up":{"uv":[0,0,8,8],"texture":0},"down":{"uv":[0,8,8,16],"texture":0}},"type":"cube","uuid":"c4356ab3-a7a5-43e5-489a-9ebb52492675"},{"name":"bottom_south_west","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,0,8],"to":[8,8,16],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[8,8,16,16],"texture":0},"east":{"uv":[0,8,8,16],"texture":0},"south":{"uv":[0,8,8,16],"texture":1},"west":{"uv":[8,8,16,16],"texture":1},"up":{"uv":[0,8,8,16],"texture":0},"down":{"uv":[0,0,8,8],"texture":0}},"type":"cube","uuid":"d07d02d7-5ab9-0026-b3ed-029701536cf6"},{"name":"bottom_north_east","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,0,0],"to":[16,8,8],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[0,8,8,16],"texture":1},"east":{"uv":[8,8,16,16],"texture":1},"south":{"uv":[8,8,16,16],"texture":0},"west":{"uv":[0,8,8,16],"texture":0},"up":{"uv":[8,0,16,8],"texture":0},"down":{"uv":[8,8,16,16],"texture":0}},"type":"cube","uuid":"d10fb8d4-fe44-ca10-77f0-63b32855f5d6"},{"name":"top_south_west","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[0,8,8],"to":[8,16,16],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[8,0,16,8],"texture":0},"east":{"uv":[0,0,8,8],"texture":0},"south":{"uv":[0,0,8,8],"texture":1},"west":{"uv":[8,0,16,8],"texture":1},"up":{"uv":[0,8,8,16],"texture":0},"down":{"uv":[0,0,8,8],"texture":0}},"type":"cube","uuid":"b9c9d695-4003-c9e6-efbb-9a75c658b47e"},{"name":"top_north_east","box_uv":false,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[8,8,0],"to":[16,16,8],"autouv":0,"color":2,"origin":[0,0,0],"faces":{"north":{"uv":[0,0,8,8],"texture":1},"east":{"uv":[8,0,16,8],"texture":1},"south":{"uv":[8,0,16,8],"texture":0},"west":{"uv":[0,0,8,8],"texture":0},"up":{"uv":[8,0,16,8],"texture":0},"down":{"uv":[8,8,16,16],"texture":0}},"type":"cube","uuid":"20a14bb3-edf7-1ae1-132e-ef3d56d84f67"}],"outliner":["523f4164-daa5-4b79-eec7-013710b7ae88","4f48967d-c722-5593-6f77-079ee47d08af","d10fb8d4-fe44-ca10-77f0-63b32855f5d6","20a14bb3-edf7-1ae1-132e-ef3d56d84f67","d07d02d7-5ab9-0026-b3ed-029701536cf6","b9c9d695-4003-c9e6-efbb-9a75c658b47e","1fd288fe-a293-1ca2-a5b7-15d602eb44f6","c4356ab3-a7a5-43e5-489a-9ebb52492675"],"textures":[{"path":"/home/sollace/Desktop/hay_block_top.png","name":"hay_block_top.png","folder":"blocks","namespace":"minecraft","id":"top","particle":false,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"f73f23e2-d591-dd05-db6f-60869e1592c7","relative_path":"../../../../../../../Desktop/hay_block_top.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAVVJREFUOE9tkyFOBEEQRWvckEAyuBWYFSRgCQgk3IgDcAKC4wo4rgBIxBIsJIg1iHVsAgnjhryevE6xTJvd6ar69ev/6ub+thte3yJmXR+c1bqNn++I+V5f/nuMm8Mvd83VRTccHUd8rvrYnbWRwUhafrQVjAJBDw/Gmubuuh3sMt+PWL5HASKY79df4x2xl+eRIeCFgXRBJXh+1sfTog2+ZSQTm/C9tR3R3Fx2AzPzwZkqMtkRpM84VQNpcUkiRTCRetbi9KSPh8dRmz8aMF+3858+oBQJZpPCABFVX/s2rWJuNNHKPFLRAHTVpxgWdFPA3FEm5FUbcwIBmHDcDxhk+opITmGgaHqrrapO1zyC+1H3gE66QDDPTFLeVGjneBFRilNWyirvCXmAolHdRH3O6wttHw06ObuuVQ1y0ua2EXM/cMojSMNzZi5fWX7KWdwppxjjF8NCOiclFu9xAAAAAElFTkSuQmCC"},{"path":"/home/sollace/Desktop/hay_block_side.png","name":"hay_block_side.png","folder":"blocks","namespace":"minecraft","id":"side","particle":true,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"1cf36f16-9f26-3125-d49c-45217088cbc7","relative_path":"../../../../../../../Desktop/hay_block_side.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAV1JREFUOE+VU7tOQkEUPGtiuAokeCExNIpBK2x9tITK7/EHqCmJv2DlNxhblZqORK2MiVxJBLmEhDVzktkclopt9p7X7OzcWde7bfiL6wMZDsfSalVl8Pwj7U5FXl4zjZn//pxKrV5aixunRXF33SNvGzFwdZmuARymBfnKFgpIIBx0crwrDgz2yytBE05AgYyQe/tYCup/vzs6gBg7l7u/Ofc+8ZJNckkrSSgg5ppNF1IsFTbq6NcrkCJPxAmgbPOMyYDMggaxSBTz6XGilGNAArmH/pnH3QEAVa36EPN9NFNtbN2KugGAAQvEwRiIfQqAASa23QMAqJNyDGivEDNUH8TOs+rjV0JALDoVPqHort9uehTpAZc7GedzqSZ7An8wtj34pm+CE5GE2+yiA7Fjxc5EXgFoUTTR1tYw9AEPsdYOTuQAH419E3xs1qnoh3aBgR20otKRVn2+Vuz/aIRXgq2GkfcAAAAASUVORK5CYII="}]} \ No newline at end of file diff --git a/assets/models/hay_bale.json b/assets/models/hay_bale.json new file mode 100644 index 00000000..a2ce1538 --- /dev/null +++ b/assets/models/hay_bale.json @@ -0,0 +1,113 @@ +{ + "textures": { + "top": "blocks/hay_block_top", + "particle": "blocks/hay_block_side", + "side": "blocks/hay_block_side" + }, + "elements": [ + { + "name": "bottom_south_east", + "from": [8, 0, 8], + "to": [16, 8, 16], + "faces": { + "north": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "east": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "south": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "west": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "up": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#top"} + } + }, + { + "name": "top_south_east", + "from": [8, 8, 8], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "east": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "up": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#top"} + } + }, + { + "name": "bottom_north_east", + "from": [8, 0, 0], + "to": [16, 8, 8], + "faces": { + "north": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "south": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#top"} + } + }, + { + "name": "top_north_east", + "from": [8, 8, 0], + "to": [16, 16, 8], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#top"} + } + }, + { + "name": "bottom_south_west", + "from": [0, 0, 8], + "to": [8, 8, 16], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "east": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "west": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#top"} + } + }, + { + "name": "top_south_west", + "from": [0, 8, 8], + "to": [8, 16, 16], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "east": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#top"} + } + }, + { + "name": "bottom_north_west", + "from": [0, 0, 0], + "to": [8, 8, 8], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#top"} + } + }, + { + "name": "top_north_west", + "from": [0, 8, 0], + "to": [8, 16, 8], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index f02ee066..b1dcf2a5 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.block.UBlocks; +import com.minelittlepony.unicopia.block.state.StateUtil; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; @@ -29,7 +30,6 @@ import net.minecraft.item.BoneMealItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.particle.ParticleTypes; -import net.minecraft.state.property.Property; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; @@ -233,16 +233,7 @@ public class EarthPonyGrowAbility implements Ability { } public BlockState getResult(World world, BlockPos pos) { - BlockState input = world.getBlockState(pos); - BlockState output = this.output; - for (var property : input.getProperties()) { - output = copyProperty(input, output, property); - } - return output; - } - - private > BlockState copyProperty(BlockState from, BlockState to, Property property) { - return to.withIfExists(property, from.get(property)); + return StateUtil.copyState(world.getBlockState(pos), output); } record Result (TransmutationRecipe recipe, Set matchedLocations) { diff --git a/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java b/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java new file mode 100644 index 00000000..b4f0a5d2 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java @@ -0,0 +1,214 @@ +package com.minelittlepony.unicopia.block; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.entity.player.Pony; + +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.HayBlock; +import net.minecraft.block.ShapeContext; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.registry.Registries; +import net.minecraft.sound.SoundCategory; +import net.minecraft.state.StateManager; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.Util; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +import net.minecraft.world.World; + +public class EdibleBlock extends HayBlock { + private static final List REGISTRY = new ArrayList<>(); + + static final BooleanProperty TOP_NORTH_EAST = BooleanProperty.of("top_north_east"); + static final BooleanProperty TOP_NORTH_WEST = BooleanProperty.of("top_north_west"); + static final BooleanProperty TOP_SOUTH_EAST = BooleanProperty.of("top_south_east"); + static final BooleanProperty TOP_SOUTH_WEST = BooleanProperty.of("top_south_west"); + + static final BooleanProperty BOTTOM_NORTH_EAST = BooleanProperty.of("bottom_north_east"); + static final BooleanProperty BOTTOM_NORTH_WEST = BooleanProperty.of("bottom_north_west"); + static final BooleanProperty BOTTOM_SOUTH_EAST = BooleanProperty.of("bottom_south_east"); + static final BooleanProperty BOTTOM_SOUTH_WEST = BooleanProperty.of("bottom_south_west"); + + // [up/down][north/south][west/east] + private static final BooleanProperty[] SEGMENTS = { + BOTTOM_NORTH_WEST, + BOTTOM_NORTH_EAST, + BOTTOM_SOUTH_WEST, + BOTTOM_SOUTH_EAST, + TOP_NORTH_WEST, + TOP_NORTH_EAST, + TOP_SOUTH_WEST, + TOP_SOUTH_EAST + }; + private static final VoxelShape[] SHAPES = { + Block.createCuboidShape(0, 0, 0, 8, 8, 8), + Block.createCuboidShape(8, 0, 0, 16, 8, 8), + Block.createCuboidShape(0, 0, 8, 8, 8, 16), + Block.createCuboidShape(8, 0, 8, 16, 8, 16), + Block.createCuboidShape(0, 8, 0, 8, 16, 8), + Block.createCuboidShape(8, 8, 0, 16, 16, 8), + Block.createCuboidShape(0, 8, 8, 8, 16, 16), + Block.createCuboidShape(8, 8, 8, 16, 16, 16) + }; + private static final Function SHAPE_CACHE = Util.memoize(state -> { + @Nullable + VoxelShape shape = null; + for (int i = 0; i < SEGMENTS.length; i++) { + if (state.get(SEGMENTS[i])) { + shape = shape == null ? SHAPES[i] : VoxelShapes.union(shape, SHAPES[i]); + } + } + return shape == null ? VoxelShapes.fullCube() : shape.simplify(); + }); + + static void bootstrap() { + UseBlockCallback.EVENT.register((PlayerEntity player, World world, Hand hand, BlockHitResult hitResult) -> { + if (!Pony.of(player).getSpecies().isEquine() + || (player.shouldCancelInteraction() && (!player.getMainHandStack().isEmpty() || !player.getOffHandStack().isEmpty()))) { + return ActionResult.PASS; + } + + BlockPos pos = hitResult.getBlockPos(); + BlockState state = world.getBlockState(pos); + + for (EdibleBlock edibleBlock : REGISTRY) { + Block match = edibleBlock.getBaseBlock(); + if (match != Blocks.AIR && state.isOf(match)) { + ActionResult result = StateUtil.copyState(state, edibleBlock.getDefaultState()).onUse(world, player, hand, hitResult); + + if (result.isAccepted()) { + return result; + } + } + } + + return ActionResult.PASS; + }); + } + + private final Identifier baseBlock; + + public EdibleBlock(Identifier baseBlock, boolean register) { + super(Settings.copy(Blocks.HAY_BLOCK)); + for (BooleanProperty segment : SEGMENTS) { + setDefaultState(getDefaultState().with(segment, true)); + } + this.baseBlock = baseBlock; + if (register) { + REGISTRY.add(this); + FlammableBlockRegistry.getDefaultInstance().add(this, 60, 20); + } + } + + public Block getBaseBlock() { + return Registries.BLOCK.get(baseBlock); + } + + @Override + public String getTranslationKey() { + return getBaseBlock().getTranslationKey(); + } + + @Override + protected void appendProperties(StateManager.Builder builder) { + super.appendProperties(builder); + builder.add(SEGMENTS); + } + + @Override + public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { + return SHAPE_CACHE.apply(state); + } + + @Override + @Deprecated + public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { + if (player.isSpectator()) { + return ActionResult.FAIL; + } + + ItemStack stack = player.getStackInHand(hand); + if (stack.isOf(Items.WHEAT)) { + BooleanProperty segment = getHitCorner(hit, 1); + + if (!state.get(segment)) { + stack.decrement(1); + if (!world.isClient) { + state = state.with(segment, true); + if (SHAPE_CACHE.apply(state) == VoxelShapes.fullCube()) { + state = StateUtil.copyState(state, getBaseBlock().getDefaultState()); + } + world.setBlockState(pos, state); + } + world.playSound(player, pos, this.getSoundGroup(state).getPlaceSound(), SoundCategory.BLOCKS); + + return ActionResult.SUCCESS; + } + + return ActionResult.FAIL; + } + + BooleanProperty corner = getHitCorner(hit, -1); + + if (!state.get(corner)) { + return ActionResult.PASS; + } + + if (!(player.isCreative() || player.getHungerManager().isNotFull()) || !player.isSneaking()) { + return ActionResult.FAIL; + } + + if (!world.isClient) { + state = state.with(corner, false); + if (SHAPE_CACHE.apply(state) == VoxelShapes.fullCube()) { + world.removeBlock(pos, false); + } else { + world.setBlockState(pos, state); + } + } + player.playSound(USounds.Vanilla.ENTITY_GENERIC_EAT, 1, 1); + if (world.random.nextInt(10) == 0) { + player.playSound(USounds.Vanilla.ENTITY_PLAYER_BURP, 1, player.getSoundPitch()); + } + player.getHungerManager().add(4, 2.3F); + return ActionResult.SUCCESS; + } + + static BooleanProperty getHitCorner(BlockHitResult hit, int direction) { + Vec3d pos = hit.getPos().add(Vec3d.of(hit.getSide().getVector()).multiply(direction * 0.001F)); + + BlockPos bPos = hit.getBlockPos(); + + return SEGMENTS[ + (4 * getIndex(pos.y, bPos.getY())) + + (2 * getIndex(pos.z, bPos.getZ())) + + (1 - getIndex(pos.x, bPos.getX())) + ]; + } + + static int getIndex(double axisHit, int tile) { + axisHit = Math.abs(axisHit); + tile = Math.abs(tile); + return axisHit - ((int)axisHit) > 0.5 ? 1 : 0; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java index 7fc8d666..af03300f 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java @@ -29,6 +29,7 @@ import com.minelittlepony.unicopia.server.world.UTreeGen; import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; import net.fabricmc.fabric.api.registry.FlammableBlockRegistry; import net.fabricmc.fabric.api.registry.StrippableBlockRegistry; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.block.*; import net.minecraft.block.AbstractBlock.Settings; import net.minecraft.block.entity.BlockEntityType; @@ -224,6 +225,8 @@ public interface UBlocks { Block CRYSTAL_DOOR = register("crystal_door", new CrystalDoorBlock(Settings.copy(Blocks.IRON_DOOR), UWoodTypes.CRYSTAL), ItemGroups.FUNCTIONAL); Block CLOUD_DOOR = register("cloud_door", new CloudDoorBlock(Settings.copy(CLOUD), CLOUD.getDefaultState(), UWoodTypes.CLOUD), ItemGroups.FUNCTIONAL); + EdibleBlock HAY_BLOCK = register("hay_block", new EdibleBlock(new Identifier("hay_block"), true)); + private static T register(String name, T item) { return register(Unicopia.id(name), item); } @@ -247,10 +250,15 @@ public interface UBlocks { if (block instanceof CloudLike || block instanceof SlimePustuleBlock || block instanceof PileBlock) { SEMI_TRANSPARENT_BLOCKS.add(block); } + return Registry.register(Registries.BLOCK, id, block); } static void bootstrap() { + if (FabricLoader.getInstance().isModLoaded("farmersdelight")) { + register("rice_block", new EdibleBlock(new Identifier("farmersdelight", "rice_bale"), true)); + register("straw_block", new EdibleBlock(new Identifier("farmersdelight", "straw_bale"), true)); + } BlockEntityTypeSupportHelper.of(BlockEntityType.SIGN).addSupportedBlocks(PALM_SIGN, PALM_WALL_SIGN); BlockEntityTypeSupportHelper.of(BlockEntityType.HANGING_SIGN).addSupportedBlocks(PALM_HANGING_SIGN, PALM_WALL_HANGING_SIGN); @@ -276,5 +284,6 @@ public interface UBlocks { FlammableBlockRegistry.getDefaultInstance().add(BANANAS, 5, 20); UBlockEntities.bootstrap(); + EdibleBlock.bootstrap(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java index 5e17ca06..15303184 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudBlock.java @@ -4,6 +4,8 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; + import net.minecraft.block.BlockState; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; @@ -22,7 +24,7 @@ public class PoreousCloudBlock extends CloudBlock implements Soakable { @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, getDefaultState()); + return StateUtil.copyState(state, getDefaultState()); } return soggyBlock == null ? null : soggyBlock.get().getStateWithMoisture(state, moisture); } diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java index 6733ee2d..3ee498a1 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/PoreousCloudStairsBlock.java @@ -4,6 +4,8 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; + import net.minecraft.block.BlockState; public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakable { @@ -19,7 +21,7 @@ public class PoreousCloudStairsBlock extends CloudStairsBlock implements Soakabl @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, getDefaultState()); + return StateUtil.copyState(state, getDefaultState()); } return soggyBlock.get().getStateWithMoisture(state, moisture); } diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java index e800bbf8..94ce19e8 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/Soakable.java @@ -15,7 +15,6 @@ import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvents; import net.minecraft.state.property.IntProperty; -import net.minecraft.state.property.Property; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Util; @@ -105,15 +104,4 @@ public interface Soakable { world.setBlockState(pos, soakable.getStateWithMoisture(state, newMoisture)); world.playSound(null, pos, SoundEvents.ENTITY_SALMON_FLOP, SoundCategory.BLOCKS, 1, (float)world.random.nextTriangular(0.5, 0.3F)); } - - @Nullable - @SuppressWarnings({ "rawtypes", "unchecked" }) - static BlockState copyProperties(BlockState from, @Nullable BlockState to) { - if (to != null) { - for (Property property : from.getProperties()) { - to = to.withIfExists(property, from.get(property)); - } - } - return to; - } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java index e508b96a..5f769024 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudBlock.java @@ -4,6 +4,8 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; + import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; @@ -43,9 +45,9 @@ public class SoggyCloudBlock extends CloudBlock implements Soakable { @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, dryBlock.get().getDefaultState()); + return StateUtil.copyState(state, dryBlock.get().getDefaultState()); } - return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture); + return StateUtil.copyState(state, getDefaultState()).with(MOISTURE, moisture); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java index d074b2cb..7595c2b1 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudSlabBlock.java @@ -4,6 +4,8 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; + import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; @@ -43,9 +45,9 @@ public class SoggyCloudSlabBlock extends CloudSlabBlock { @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, dryBlock.get().getDefaultState()); + return StateUtil.copyState(state, dryBlock.get().getDefaultState()); } - return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture); + return StateUtil.copyState(state, getDefaultState()).with(MOISTURE, moisture); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java index 495c0f37..9e51e398 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/cloud/SoggyCloudStairsBlock.java @@ -4,6 +4,8 @@ import java.util.function.Supplier; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.block.state.StateUtil; + import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.item.ItemStack; @@ -36,8 +38,8 @@ public class SoggyCloudStairsBlock extends CloudStairsBlock implements Soakable @Override public BlockState getStateWithMoisture(BlockState state, int moisture) { if (moisture <= 0) { - return Soakable.copyProperties(state, dryBlock.get().getDefaultState()); + return StateUtil.copyState(state, dryBlock.get().getDefaultState()); } - return Soakable.copyProperties(state, getDefaultState()).with(MOISTURE, moisture); + return StateUtil.copyState(state, getDefaultState()).with(MOISTURE, moisture); } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/state/StateChange.java b/src/main/java/com/minelittlepony/unicopia/block/state/StateChange.java index 72e7b9e4..972eef4f 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/state/StateChange.java +++ b/src/main/java/com/minelittlepony/unicopia/block/state/StateChange.java @@ -11,7 +11,6 @@ import com.minelittlepony.unicopia.Unicopia; import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.state.property.Property; import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper; import net.minecraft.registry.Registries; @@ -50,7 +49,7 @@ public abstract class StateChange { return state; } return Registries.BLOCK.getOrEmpty(id).map(Block::getDefaultState) - .map(newState -> merge(newState, state)) + .map(newState -> StateUtil.copyState(state, newState)) .orElse(state); } }; @@ -101,17 +100,4 @@ public abstract class StateChange { return serializer.apply(json); }).orElseThrow(() -> new IllegalArgumentException("Invalid action " + action)); } - - private static BlockState merge(BlockState into, BlockState from) { - for (var property : from.getProperties()) { - if (into.contains(property)) { - into = copy(into, from, property); - } - } - return into; - } - - private static > BlockState copy(BlockState to, BlockState from, Property property) { - return to.with(property, from.get(property)); - } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/state/StateUtil.java b/src/main/java/com/minelittlepony/unicopia/block/state/StateUtil.java new file mode 100644 index 00000000..42334368 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/state/StateUtil.java @@ -0,0 +1,19 @@ +package com.minelittlepony.unicopia.block.state; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.block.BlockState; +import net.minecraft.state.property.Property; + +public interface StateUtil { + @SuppressWarnings({ "unchecked", "rawtypes" }) + static BlockState copyState(BlockState from, @Nullable BlockState to) { + if (to == null) { + return to; + } + for (var property : from.getProperties()) { + to = to.withIfExists((Property)property, from.get(property)); + } + return to; + } +} diff --git a/src/main/resources/assets/unicopia/blockstates/hay_block.json b/src/main/resources/assets/unicopia/blockstates/hay_block.json new file mode 100644 index 00000000..1378545e --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/hay_block.json @@ -0,0 +1,31 @@ +{ + "multipart": [ + { "apply": { "model": "unicopia:block/hay_bale_bne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_east": true } }, + + { "apply": { "model": "unicopia:block/hay_bale_bne" }, "when": { "axis": "y", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bnw" }, "when": { "axis": "y", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bse" }, "when": { "axis": "y", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bsw" }, "when": { "axis": "y", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tne" }, "when": { "axis": "y", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tnw" }, "when": { "axis": "y", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tse" }, "when": { "axis": "y", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tsw" }, "when": { "axis": "y", "top_south_west": true } }, + + + { "apply": { "model": "unicopia:block/hay_bale_bne", "x": 90 }, "when": { "axis": "z", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bnw", "x": 90 }, "when": { "axis": "z", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bse", "x": 90 }, "when": { "axis": "z", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_bsw", "x": 90 }, "when": { "axis": "z", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tne", "x": 90 }, "when": { "axis": "z", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tnw", "x": 90 }, "when": { "axis": "z", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tse", "x": 90 }, "when": { "axis": "z", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/hay_bale_tsw", "x": 90 }, "when": { "axis": "z", "top_north_west": true } } + ] +} diff --git a/src/main/resources/assets/unicopia/blockstates/rice_block.json b/src/main/resources/assets/unicopia/blockstates/rice_block.json new file mode 100644 index 00000000..61251408 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/rice_block.json @@ -0,0 +1,31 @@ +{ + "multipart": [ + { "apply": { "model": "unicopia:block/rice_bale_bne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_east": true } }, + + { "apply": { "model": "unicopia:block/rice_bale_bne" }, "when": { "axis": "y", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bnw" }, "when": { "axis": "y", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bse" }, "when": { "axis": "y", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bsw" }, "when": { "axis": "y", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tne" }, "when": { "axis": "y", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tnw" }, "when": { "axis": "y", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tse" }, "when": { "axis": "y", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tsw" }, "when": { "axis": "y", "top_south_west": true } }, + + + { "apply": { "model": "unicopia:block/rice_bale_bne", "x": 90 }, "when": { "axis": "z", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bnw", "x": 90 }, "when": { "axis": "z", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bse", "x": 90 }, "when": { "axis": "z", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_bsw", "x": 90 }, "when": { "axis": "z", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tne", "x": 90 }, "when": { "axis": "z", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tnw", "x": 90 }, "when": { "axis": "z", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tse", "x": 90 }, "when": { "axis": "z", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/rice_bale_tsw", "x": 90 }, "when": { "axis": "z", "top_north_west": true } } + ] +} diff --git a/src/main/resources/assets/unicopia/blockstates/straw_block.json b/src/main/resources/assets/unicopia/blockstates/straw_block.json new file mode 100644 index 00000000..4f1da006 --- /dev/null +++ b/src/main/resources/assets/unicopia/blockstates/straw_block.json @@ -0,0 +1,31 @@ +{ + "multipart": [ + { "apply": { "model": "unicopia:block/straw_bale_bne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tne", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tnw", "x": 90, "y": 90 }, "when": { "axis": "x", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tse", "x": 90, "y": 90 }, "when": { "axis": "x", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tsw", "x": 90, "y": 90 }, "when": { "axis": "x", "top_north_east": true } }, + + { "apply": { "model": "unicopia:block/straw_bale_bne" }, "when": { "axis": "y", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bnw" }, "when": { "axis": "y", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bse" }, "when": { "axis": "y", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bsw" }, "when": { "axis": "y", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tne" }, "when": { "axis": "y", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tnw" }, "when": { "axis": "y", "top_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tse" }, "when": { "axis": "y", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tsw" }, "when": { "axis": "y", "top_south_west": true } }, + + + { "apply": { "model": "unicopia:block/straw_bale_bne", "x": 90 }, "when": { "axis": "z", "bottom_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bnw", "x": 90 }, "when": { "axis": "z", "bottom_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bse", "x": 90 }, "when": { "axis": "z", "top_south_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_bsw", "x": 90 }, "when": { "axis": "z", "top_south_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tne", "x": 90 }, "when": { "axis": "z", "bottom_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tnw", "x": 90 }, "when": { "axis": "z", "bottom_north_west": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tse", "x": 90 }, "when": { "axis": "z", "top_north_east": true } }, + { "apply": { "model": "unicopia:block/straw_bale_tsw", "x": 90 }, "when": { "axis": "z", "top_north_west": true } } + ] +} diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_bne.json b/src/main/resources/assets/unicopia/models/block/hay_bale_bne.json new file mode 100644 index 00000000..7453b2e9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_bne.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "bottom_north_east", + "from": [8, 0, 0], + "to": [16, 8, 8], + "faces": { + "north": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "south": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_bnw.json b/src/main/resources/assets/unicopia/models/block/hay_bale_bnw.json new file mode 100644 index 00000000..6cde59a7 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_bnw.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "bottom_north_west", + "from": [0, 0, 0], + "to": [8, 8, 8], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "east": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "west": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_bse.json b/src/main/resources/assets/unicopia/models/block/hay_bale_bse.json new file mode 100644 index 00000000..76ec139b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_bse.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "bottom_south_east", + "from": [8, 0, 8], + "to": [16, 8, 16], + "faces": { + "north": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "east": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "south": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "west": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "up": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_bsw.json b/src/main/resources/assets/unicopia/models/block/hay_bale_bsw.json new file mode 100644 index 00000000..ed4ade6d --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_bsw.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "bottom_south_west", + "from": [0, 0, 8], + "to": [8, 8, 16], + "faces": { + "north": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "east": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "south": {"uv": [0, 8, 8, 16], "texture": "#side"}, + "west": {"uv": [8, 8, 16, 16], "texture": "#side"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_tne.json b/src/main/resources/assets/unicopia/models/block/hay_bale_tne.json new file mode 100644 index 00000000..d23896b4 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_tne.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "top_north_east", + "from": [8, 8, 0], + "to": [16, 16, 8], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "up": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "down": {"uv": [8, 8, 16, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_tnw.json b/src/main/resources/assets/unicopia/models/block/hay_bale_tnw.json new file mode 100644 index 00000000..b465617c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_tnw.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "top_north_west", + "from": [0, 8, 0], + "to": [8, 16, 8], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "east": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "west": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "up": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "down": {"uv": [0, 8, 8, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_tse.json b/src/main/resources/assets/unicopia/models/block/hay_bale_tse.json new file mode 100644 index 00000000..64d34fa6 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_tse.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "top_south_east", + "from": [8, 8, 8], + "to": [16, 16, 16], + "faces": { + "north": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "east": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "south": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "up": {"uv": [8, 8, 16, 16], "texture": "#top"}, + "down": {"uv": [8, 0, 16, 8], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/hay_bale_tsw.json b/src/main/resources/assets/unicopia/models/block/hay_bale_tsw.json new file mode 100644 index 00000000..01f205b0 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/hay_bale_tsw.json @@ -0,0 +1,22 @@ +{ + "textures": { + "top": "block/hay_block_top", + "particle": "block/hay_block_side", + "side": "block/hay_block_side" + }, + "elements": [ + { + "name": "top_south_west", + "from": [0, 8, 8], + "to": [8, 16, 16], + "faces": { + "north": {"uv": [8, 0, 16, 8], "texture": "#top"}, + "east": {"uv": [0, 0, 8, 8], "texture": "#top"}, + "south": {"uv": [0, 0, 8, 8], "texture": "#side"}, + "west": {"uv": [8, 0, 16, 8], "texture": "#side"}, + "up": {"uv": [0, 8, 8, 16], "texture": "#top"}, + "down": {"uv": [0, 0, 8, 8], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_bne.json b/src/main/resources/assets/unicopia/models/block/rice_bale_bne.json new file mode 100644 index 00000000..3c280742 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_bne.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bne", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_bnw.json b/src/main/resources/assets/unicopia/models/block/rice_bale_bnw.json new file mode 100644 index 00000000..9b3d6ce0 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_bnw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bnw", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_bse.json b/src/main/resources/assets/unicopia/models/block/rice_bale_bse.json new file mode 100644 index 00000000..6fd0b774 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_bse.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bse", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_bsw.json b/src/main/resources/assets/unicopia/models/block/rice_bale_bsw.json new file mode 100644 index 00000000..c2ec227a --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_bsw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bsw", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_tne.json b/src/main/resources/assets/unicopia/models/block/rice_bale_tne.json new file mode 100644 index 00000000..81eb613c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_tne.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tne", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_tnw.json b/src/main/resources/assets/unicopia/models/block/rice_bale_tnw.json new file mode 100644 index 00000000..c4bef47d --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_tnw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tnw", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_tse.json b/src/main/resources/assets/unicopia/models/block/rice_bale_tse.json new file mode 100644 index 00000000..f58ce7d1 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_tse.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tse", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/rice_bale_tsw.json b/src/main/resources/assets/unicopia/models/block/rice_bale_tsw.json new file mode 100644 index 00000000..9608f77e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/rice_bale_tsw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tsw", + "textures": { + "top": "farmersdelight:block/rice_bale_top", + "particle": "farmersdelight:block/rice_bale_side", + "side": "farmersdelight:block/rice_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_bne.json b/src/main/resources/assets/unicopia/models/block/straw_bale_bne.json new file mode 100644 index 00000000..fef62f35 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_bne.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bne", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_bnw.json b/src/main/resources/assets/unicopia/models/block/straw_bale_bnw.json new file mode 100644 index 00000000..a5b3162b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_bnw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bnw", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_bse.json b/src/main/resources/assets/unicopia/models/block/straw_bale_bse.json new file mode 100644 index 00000000..95d23675 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_bse.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bse", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_bsw.json b/src/main/resources/assets/unicopia/models/block/straw_bale_bsw.json new file mode 100644 index 00000000..3a5ef025 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_bsw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_bsw", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_tne.json b/src/main/resources/assets/unicopia/models/block/straw_bale_tne.json new file mode 100644 index 00000000..f8cc37a5 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_tne.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tne", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_tnw.json b/src/main/resources/assets/unicopia/models/block/straw_bale_tnw.json new file mode 100644 index 00000000..1b06dba9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_tnw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tnw", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_tse.json b/src/main/resources/assets/unicopia/models/block/straw_bale_tse.json new file mode 100644 index 00000000..44d1f681 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_tse.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tse", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/models/block/straw_bale_tsw.json b/src/main/resources/assets/unicopia/models/block/straw_bale_tsw.json new file mode 100644 index 00000000..0c74b850 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/block/straw_bale_tsw.json @@ -0,0 +1,8 @@ +{ + "parent": "unicopia:block/hay_bale_tsw", + "textures": { + "top": "farmersdelight:block/straw_bale_end", + "particle": "farmersdelight:block/straw_bale_side", + "side": "farmersdelight:block/straw_bale_side" + } +} \ No newline at end of file diff --git a/src/main/resources/data/c/tags/items/foraging/edibles_filling.json b/src/main/resources/data/c/tags/items/foraging/edibles_filling.json index c183c3fb..bc799386 100644 --- a/src/main/resources/data/c/tags/items/foraging/edibles_filling.json +++ b/src/main/resources/data/c/tags/items/foraging/edibles_filling.json @@ -1,6 +1,8 @@ { "replace": false, "values": [ - { "id": "farmersdelight:horse_feed", "required": false } + { "id": "farmersdelight:horse_feed", "required": false }, + { "id": "farmersdelight:rice_bale", "required": false }, + { "id": "farmersdelight:straw_bale", "required": false } ] } diff --git a/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json b/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json index ced25022..c6d84218 100644 --- a/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json +++ b/src/main/resources/data/minecraft/tags/blocks/mineable/hoe.json @@ -8,6 +8,9 @@ "unicopia:sweet_apple_leaves", "unicopia:sour_apple_leaves", "unicopia:golden_oak_leaves", - "unicopia:mango_leaves" + "unicopia:mango_leaves", + "unicopia:hay_block", + { "id": "unicopia:rice_block", "required": false }, + { "id": "unicopia:straw_block", "required": false } ] } diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json new file mode 100644 index 00000000..cce83e89 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json @@ -0,0 +1,245 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:gold_root", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "enchantment": "minecraft:fortune", + "formula": "minecraft:binomial_with_bonus_count", + "function": "minecraft:apply_bonus", + "parameters": { + "extra": 3, + "probability": 0.5714286 + } + } + ], + "name": "minecraft:wheat" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json index ce1ec0f6..5bf8d28d 100644 --- a/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json +++ b/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json @@ -3,7 +3,6 @@ "values": [ "minecraft:hay_block", "#minecraft:leaves", - "#c:foraging/edibles_filling", - { "id": "farmersdelight:rice_bale", "required": false } + "#c:foraging/edibles_filling" ] } From e5236e5802a9e01668cfb46149bb2711a26f56bf Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 19 Jan 2024 16:11:48 +0000 Subject: [PATCH 36/98] Fix loot tables --- .../loot_tables/blocks/hay_block.json | 104 +----------- .../loot_tables/blocks/rice_block.json | 157 ++++++++++++++++++ .../loot_tables/blocks/straw_block.json | 157 ++++++++++++++++++ 3 files changed, 322 insertions(+), 96 deletions(-) create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json create mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json index cce83e89..1101857f 100644 --- a/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json +++ b/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json @@ -5,7 +5,7 @@ "bonus_rolls": 0.0, "conditions": [ { - "block": "unicopia:gold_root", + "block": "unicopia:hay_block", "condition": "minecraft:block_state_property", "properties": { "top_north_east": "true" @@ -15,17 +15,6 @@ "entries": [ { "type": "minecraft:item", - "functions": [ - { - "enchantment": "minecraft:fortune", - "formula": "minecraft:binomial_with_bonus_count", - "function": "minecraft:apply_bonus", - "parameters": { - "extra": 3, - "probability": 0.5714286 - } - } - ], "name": "minecraft:wheat" } ], @@ -35,7 +24,7 @@ "bonus_rolls": 0.0, "conditions": [ { - "block": "unicopia:gold_root", + "block": "unicopia:hay_block", "condition": "minecraft:block_state_property", "properties": { "top_north_west": "true" @@ -45,17 +34,6 @@ "entries": [ { "type": "minecraft:item", - "functions": [ - { - "enchantment": "minecraft:fortune", - "formula": "minecraft:binomial_with_bonus_count", - "function": "minecraft:apply_bonus", - "parameters": { - "extra": 3, - "probability": 0.5714286 - } - } - ], "name": "minecraft:wheat" } ], @@ -65,7 +43,7 @@ "bonus_rolls": 0.0, "conditions": [ { - "block": "unicopia:gold_root", + "block": "unicopia:hay_block", "condition": "minecraft:block_state_property", "properties": { "top_south_east": "true" @@ -75,17 +53,6 @@ "entries": [ { "type": "minecraft:item", - "functions": [ - { - "enchantment": "minecraft:fortune", - "formula": "minecraft:binomial_with_bonus_count", - "function": "minecraft:apply_bonus", - "parameters": { - "extra": 3, - "probability": 0.5714286 - } - } - ], "name": "minecraft:wheat" } ], @@ -95,7 +62,7 @@ "bonus_rolls": 0.0, "conditions": [ { - "block": "unicopia:gold_root", + "block": "unicopia:hay_block", "condition": "minecraft:block_state_property", "properties": { "top_south_west": "true" @@ -105,17 +72,6 @@ "entries": [ { "type": "minecraft:item", - "functions": [ - { - "enchantment": "minecraft:fortune", - "formula": "minecraft:binomial_with_bonus_count", - "function": "minecraft:apply_bonus", - "parameters": { - "extra": 3, - "probability": 0.5714286 - } - } - ], "name": "minecraft:wheat" } ], @@ -125,7 +81,7 @@ "bonus_rolls": 0.0, "conditions": [ { - "block": "unicopia:gold_root", + "block": "unicopia:hay_block", "condition": "minecraft:block_state_property", "properties": { "bottom_north_east": "true" @@ -135,17 +91,6 @@ "entries": [ { "type": "minecraft:item", - "functions": [ - { - "enchantment": "minecraft:fortune", - "formula": "minecraft:binomial_with_bonus_count", - "function": "minecraft:apply_bonus", - "parameters": { - "extra": 3, - "probability": 0.5714286 - } - } - ], "name": "minecraft:wheat" } ], @@ -155,7 +100,7 @@ "bonus_rolls": 0.0, "conditions": [ { - "block": "unicopia:gold_root", + "block": "unicopia:hay_block", "condition": "minecraft:block_state_property", "properties": { "bottom_north_west": "true" @@ -165,17 +110,6 @@ "entries": [ { "type": "minecraft:item", - "functions": [ - { - "enchantment": "minecraft:fortune", - "formula": "minecraft:binomial_with_bonus_count", - "function": "minecraft:apply_bonus", - "parameters": { - "extra": 3, - "probability": 0.5714286 - } - } - ], "name": "minecraft:wheat" } ], @@ -185,7 +119,7 @@ "bonus_rolls": 0.0, "conditions": [ { - "block": "unicopia:gold_root", + "block": "unicopia:hay_block", "condition": "minecraft:block_state_property", "properties": { "bottom_south_east": "true" @@ -195,17 +129,6 @@ "entries": [ { "type": "minecraft:item", - "functions": [ - { - "enchantment": "minecraft:fortune", - "formula": "minecraft:binomial_with_bonus_count", - "function": "minecraft:apply_bonus", - "parameters": { - "extra": 3, - "probability": 0.5714286 - } - } - ], "name": "minecraft:wheat" } ], @@ -215,7 +138,7 @@ "bonus_rolls": 0.0, "conditions": [ { - "block": "unicopia:gold_root", + "block": "unicopia:hay_block", "condition": "minecraft:block_state_property", "properties": { "bottom_south_west": "true" @@ -225,17 +148,6 @@ "entries": [ { "type": "minecraft:item", - "functions": [ - { - "enchantment": "minecraft:fortune", - "formula": "minecraft:binomial_with_bonus_count", - "function": "minecraft:apply_bonus", - "parameters": { - "extra": 3, - "probability": 0.5714286 - } - } - ], "name": "minecraft:wheat" } ], diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json new file mode 100644 index 00000000..20a3852d --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json @@ -0,0 +1,157 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:rice_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:rice_panicle" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json new file mode 100644 index 00000000..ab4fa683 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json @@ -0,0 +1,157 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "top_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_north_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_east": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + }, + { + "bonus_rolls": 0.0, + "conditions": [ + { + "block": "unicopia:straw_block", + "condition": "minecraft:block_state_property", + "properties": { + "bottom_south_west": "true" + } + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "farmersdelight:straw" + } + ], + "rolls": 1.0 + } + ] +} \ No newline at end of file From 08e1702a1a3b16d0154db1285a6db8583234703e Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 19 Jan 2024 16:12:25 +0000 Subject: [PATCH 37/98] Fix bales being rebuilt with only wheat and added option to trim bales with a hoe --- .../unicopia/block/EdibleBlock.java | 40 ++++++++++++------- .../unicopia/block/UBlocks.java | 6 +-- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java b/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java index b4f0a5d2..57517560 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java @@ -19,8 +19,8 @@ import net.minecraft.block.HayBlock; import net.minecraft.block.ShapeContext; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; import net.minecraft.registry.Registries; +import net.minecraft.registry.tag.ItemTags; import net.minecraft.sound.SoundCategory; import net.minecraft.state.StateManager; import net.minecraft.state.property.BooleanProperty; @@ -107,13 +107,15 @@ public class EdibleBlock extends HayBlock { } private final Identifier baseBlock; + private final Identifier material; - public EdibleBlock(Identifier baseBlock, boolean register) { + public EdibleBlock(Identifier baseBlock, Identifier material, boolean register) { super(Settings.copy(Blocks.HAY_BLOCK)); for (BooleanProperty segment : SEGMENTS) { setDefaultState(getDefaultState().with(segment, true)); } this.baseBlock = baseBlock; + this.material = material; if (register) { REGISTRY.add(this); FlammableBlockRegistry.getDefaultInstance().add(this, 60, 20); @@ -148,7 +150,8 @@ public class EdibleBlock extends HayBlock { } ItemStack stack = player.getStackInHand(hand); - if (stack.isOf(Items.WHEAT)) { + + if (!stack.isEmpty() && stack.isOf(Registries.ITEM.get(material))) { BooleanProperty segment = getHitCorner(hit, 1); if (!state.get(segment)) { @@ -160,7 +163,7 @@ public class EdibleBlock extends HayBlock { } world.setBlockState(pos, state); } - world.playSound(player, pos, this.getSoundGroup(state).getPlaceSound(), SoundCategory.BLOCKS); + world.playSound(player, pos, getSoundGroup(state).getPlaceSound(), SoundCategory.BLOCKS); return ActionResult.SUCCESS; } @@ -174,8 +177,12 @@ public class EdibleBlock extends HayBlock { return ActionResult.PASS; } - if (!(player.isCreative() || player.getHungerManager().isNotFull()) || !player.isSneaking()) { - return ActionResult.FAIL; + boolean usingHoe = stack.isIn(ItemTags.HOES); + + if (!usingHoe) { + if (!(player.isCreative() || player.getHungerManager().isNotFull()) || !player.isSneaking()) { + return ActionResult.FAIL; + } } if (!world.isClient) { @@ -186,11 +193,17 @@ public class EdibleBlock extends HayBlock { world.setBlockState(pos, state); } } - player.playSound(USounds.Vanilla.ENTITY_GENERIC_EAT, 1, 1); - if (world.random.nextInt(10) == 0) { - player.playSound(USounds.Vanilla.ENTITY_PLAYER_BURP, 1, player.getSoundPitch()); + + if (usingHoe) { + dropStack(world, pos, Registries.ITEM.get(material).getDefaultStack()); + player.playSound(USounds.Vanilla.ITEM_HOE_TILL, 1, 1); + } else { + player.playSound(USounds.Vanilla.ENTITY_GENERIC_EAT, 1, 1); + if (world.random.nextInt(10) == 0) { + player.playSound(USounds.Vanilla.ENTITY_PLAYER_BURP, 1, player.getSoundPitch()); + } + player.getHungerManager().add(4, 2.3F); } - player.getHungerManager().add(4, 2.3F); return ActionResult.SUCCESS; } @@ -202,13 +215,12 @@ public class EdibleBlock extends HayBlock { return SEGMENTS[ (4 * getIndex(pos.y, bPos.getY())) + (2 * getIndex(pos.z, bPos.getZ())) - + (1 - getIndex(pos.x, bPos.getX())) + + (getIndex(pos.x, bPos.getX())) ]; } static int getIndex(double axisHit, int tile) { - axisHit = Math.abs(axisHit); - tile = Math.abs(tile); - return axisHit - ((int)axisHit) > 0.5 ? 1 : 0; + axisHit -= tile; + return Math.abs(axisHit) > 0.5 ? 1 : 0; } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java index af03300f..02ddcdb6 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java +++ b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java @@ -225,7 +225,7 @@ public interface UBlocks { Block CRYSTAL_DOOR = register("crystal_door", new CrystalDoorBlock(Settings.copy(Blocks.IRON_DOOR), UWoodTypes.CRYSTAL), ItemGroups.FUNCTIONAL); Block CLOUD_DOOR = register("cloud_door", new CloudDoorBlock(Settings.copy(CLOUD), CLOUD.getDefaultState(), UWoodTypes.CLOUD), ItemGroups.FUNCTIONAL); - EdibleBlock HAY_BLOCK = register("hay_block", new EdibleBlock(new Identifier("hay_block"), true)); + EdibleBlock HAY_BLOCK = register("hay_block", new EdibleBlock(new Identifier("hay_block"), new Identifier("wheat"), true)); private static T register(String name, T item) { return register(Unicopia.id(name), item); @@ -256,8 +256,8 @@ public interface UBlocks { static void bootstrap() { if (FabricLoader.getInstance().isModLoaded("farmersdelight")) { - register("rice_block", new EdibleBlock(new Identifier("farmersdelight", "rice_bale"), true)); - register("straw_block", new EdibleBlock(new Identifier("farmersdelight", "straw_bale"), true)); + register("rice_block", new EdibleBlock(new Identifier("farmersdelight", "rice_bale"), new Identifier("farmersdelight", "rice_panicle"), true)); + register("straw_block", new EdibleBlock(new Identifier("farmersdelight", "straw_bale"), new Identifier("farmersdelight", "straw"), true)); } BlockEntityTypeSupportHelper.of(BlockEntityType.SIGN).addSupportedBlocks(PALM_SIGN, PALM_WALL_SIGN); BlockEntityTypeSupportHelper.of(BlockEntityType.HANGING_SIGN).addSupportedBlocks(PALM_HANGING_SIGN, PALM_WALL_HANGING_SIGN); From 5d2478604c228eb8a0e10795fde10d8e10a12ebd Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 19 Jan 2024 16:13:54 +0000 Subject: [PATCH 38/98] Fixed items being consumed in creative mode --- .../java/com/minelittlepony/unicopia/block/EdibleBlock.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java b/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java index 57517560..b450c828 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/EdibleBlock.java @@ -155,7 +155,9 @@ public class EdibleBlock extends HayBlock { BooleanProperty segment = getHitCorner(hit, 1); if (!state.get(segment)) { - stack.decrement(1); + if (!player.isCreative()) { + stack.decrement(1); + } if (!world.isClient) { state = state.with(segment, true); if (SHAPE_CACHE.apply(state) == VoxelShapes.fullCube()) { @@ -195,6 +197,7 @@ public class EdibleBlock extends HayBlock { } if (usingHoe) { + stack.damage(1, player, p -> p.sendToolBreakStatus(hand)); dropStack(world, pos, Registries.ITEM.get(material).getDefaultStack()); player.playSound(USounds.Vanilla.ITEM_HOE_TILL, 1, 1); } else { From d22dd3bdf9cb90e55f742487f50b6b96364dc5a6 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 19 Jan 2024 22:12:22 +0000 Subject: [PATCH 39/98] Change how black holes function to instead generate energy --- .../magic/spell/effect/DarkVortexSpell.java | 16 +++++++------- .../magic/spell/effect/ShieldSpell.java | 22 +++++++++++-------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index bda10118..9402d692 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -37,10 +37,6 @@ import net.minecraft.world.World.ExplosionSourceType; /** * More powerful version of the vortex spell which creates a black hole. - * - * TODO: Possible uses - * - Garbage bin - * - Link with a teleportation spell to create a wormhole */ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelegate.BlockHitListener { public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder() @@ -91,10 +87,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1); } - if (!source.subtractEnergyCost(-accumulatedMass)) { - setDead(); - } - if (!source.isClient() && source.asWorld().random.nextInt(300) == 0) { ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, getOrigin(source), Vec3d.ZERO); } @@ -102,6 +94,14 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega return super.tick(source, situation); } + @Override + protected void consumeManage(Caster source, long costMultiplier, float knowledge) { + if (!source.subtractEnergyCost(-accumulatedMass)) { + setDead(); + } + } + + @Override public boolean isFriendlyTogether(Affine other) { return accumulatedMass < 4; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java index 61cc6e79..71f5cede 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java @@ -97,20 +97,24 @@ public class ShieldSpell extends AbstractSpell { long costMultiplier = applyEntities(source); if (costMultiplier > 0) { - double cost = 2 - source.getLevel().getScaled(2); - - cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F); - cost /= knowledge; - cost += getDrawDropOffRange(source) / 10F; - - if (!source.subtractEnergyCost(cost)) { - setDead(); - } + consumeManage(source, costMultiplier, knowledge); } return !isDead(); } + protected void consumeManage(Caster source, long costMultiplier, float knowledge) { + double cost = 2 - source.getLevel().getScaled(2); + + cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F); + cost /= knowledge; + cost += getDrawDropOffRange(source) / 10F; + + if (!source.subtractEnergyCost(cost)) { + setDead(); + } + } + /** * Calculates the maximum radius of the shield. aka The area of effect. */ From ab3ac8cb0d6c1b1689e2587e2a55f48814246bb7 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 20 Jan 2024 15:12:23 +0000 Subject: [PATCH 40/98] Improve spell lookup performance by using the Ether --- .../unicopia/InteractionManager.java | 12 - .../unicopia/ability/magic/Caster.java | 7 +- .../unicopia/ability/magic/CasterView.java | 44 --- .../ability/magic/spell/PlaceableSpell.java | 21 +- .../spell/effect/AreaProtectionSpell.java | 8 + .../spell/effect/DisperseIllusionSpell.java | 2 +- .../magic/spell/effect/HydrophobicSpell.java | 41 ++- .../magic/spell/effect/PortalSpell.java | 44 +-- .../client/ClientInteractionManager.java | 15 -- .../unicopia/mixin/MixinFlowableFluid.java | 3 +- .../unicopia/server/world/Ether.java | 251 ++++++++++++------ .../unicopia/util/NbtSerialisable.java | 13 + 12 files changed, 263 insertions(+), 198 deletions(-) delete mode 100644 src/main/java/com/minelittlepony/unicopia/ability/magic/CasterView.java diff --git a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java index 0e507331..05a101d2 100644 --- a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java @@ -1,22 +1,17 @@ package com.minelittlepony.unicopia; import java.util.Map; -import java.util.Optional; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.ability.magic.CasterView; import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity; -import com.minelittlepony.unicopia.server.world.Ether; import com.mojang.authlib.GameProfile; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.PacketByteBuf; -import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; -import net.minecraft.world.BlockView; import net.minecraft.world.World; public class InteractionManager { @@ -37,13 +32,6 @@ public class InteractionManager { return INSTANCE; } - public Optional getCasterView(BlockView view) { - if (view instanceof ServerWorld world) { - return Optional.of(Ether.get(world)); - } - return Optional.empty(); - } - public Map readChapters(PacketByteBuf buf) { throw new RuntimeException("Method not supported"); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/Caster.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/Caster.java index 98264836..150b1004 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/Caster.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/Caster.java @@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.damage.UDamageSources; import com.minelittlepony.unicopia.particle.ParticleSource; +import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.server.world.ModificationType; import com.minelittlepony.unicopia.util.SoundEmitter; import com.minelittlepony.unicopia.util.VecHelper; @@ -99,11 +100,7 @@ public interface Caster extends } default boolean canCastAt(Vec3d pos) { - return findAllSpellsInRange(500, SpellType.ARCANE_PROTECTION::isOn).noneMatch(caster -> caster - .getSpellSlot().get(SpellType.ARCANE_PROTECTION, false) - .filter(spell -> spell.blocksMagicFor(caster, this, pos)) - .isPresent() - ); + return !Ether.get(asWorld()).anyMatch(SpellType.ARCANE_PROTECTION, (spell, caster) -> spell.blocksMagicFor(caster, this, pos)); } static Stream> stream(Stream entities) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/CasterView.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/CasterView.java deleted file mode 100644 index 572f4542..00000000 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/CasterView.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.minelittlepony.unicopia.ability.magic; - -import java.util.Map; -import java.util.function.Predicate; -import java.util.stream.Stream; - -import org.jetbrains.annotations.Nullable; - -import com.minelittlepony.unicopia.EquinePredicates; -import com.minelittlepony.unicopia.ability.magic.spell.Spell; -import com.minelittlepony.unicopia.util.VecHelper; - -import net.minecraft.entity.Entity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.*; - -public interface CasterView { - EntityView getWorld(); - - default Stream, S>> findAllSpellsInRange(BlockPos pos, double radius, SpellPredicate type) { - return findAllCastersInRange(pos, radius).flatMap(caster -> { - return caster.getSpellSlot().stream(type, false).map(spell -> { - return Map.entry(caster, spell); - }); - }); - } - - default Stream> findAllCastersInRange(BlockPos pos, double radius) { - return findAllCastersInRange(pos, radius, null); - } - - default Stream> findAllCastersInRange(BlockPos pos, double radius, @Nullable Predicate test) { - return Caster.stream(findAllEntitiesInRange(pos, radius, test == null ? EquinePredicates.IS_CASTER : EquinePredicates.IS_CASTER.and(test))); - } - - default Stream findAllEntitiesInRange(BlockPos pos, double radius, @Nullable Predicate test) { - return VecHelper.findInRange(null, getWorld(), Vec3d.ofCenter(pos), radius, test).stream(); - } - - default Stream findAllEntitiesInRange(BlockPos pos, double radius) { - return findAllEntitiesInRange(pos, radius, null); - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index 7154056a..e3736e59 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -45,6 +45,12 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS */ private final ParticleHandle particlEffect = new ParticleHandle(); + /** + * ID of the placed counterpart of this spell. + */ + @Nullable + private UUID placedSpellId; + /** * The cast spell entity */ @@ -82,10 +88,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public boolean tick(Caster source, Situation situation) { - if (situation == Situation.BODY) { if (!source.isClient()) { - if (dimension == null) { dimension = source.asWorld().getRegistryKey(); if (source instanceof Pony) { @@ -105,8 +109,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS if (situation == Situation.GROUND_ENTITY) { if (!source.isClient()) { - Ether ether = Ether.get(source.asWorld()); - if (ether.getEntry(getType(), source).isEmpty()) { + if (Ether.get(source.asWorld()).get(this, source) == null) { setDead(); return false; } @@ -127,7 +130,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS } private void checkDetachment(Caster source, EntityValues target) { - if (getWorld(source).map(Ether::get).flatMap(ether -> ether.getEntry(getType(), target.uuid())).isEmpty()) { + if (getWorld(source).map(Ether::get).map(ether -> ether.get(getType(), target, placedSpellId)).isEmpty()) { setDead(); } } @@ -143,7 +146,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS entity.getSpellSlot().put(copy); entity.setCaster(source); entity.getWorld().spawnEntity(entity); - Ether.get(entity.getWorld()).put(getType(), entity); + placedSpellId = copy.getUuid(); + Ether.get(entity.getWorld()).getOrCreate(copy, entity); castEntity.set(entity); setDirty(); @@ -174,8 +178,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS if (!source.isClient()) { castEntity.getTarget().ifPresent(target -> { getWorld(source).map(Ether::get) - .flatMap(ether -> ether.getEntry(getType(), target.uuid())) - .ifPresent(Ether.Entry::markDead); + .ifPresent(ether -> ether.remove(getType(), target.uuid())); }); castEntity.set(null); getSpellEntity(source).ifPresent(e -> { @@ -183,7 +186,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS }); if (source.asEntity() instanceof CastSpellEntity spellcast) { - Ether.get(source.asWorld()).remove(getType(), source); + Ether.get(source.asWorld()).remove(this, source); } } super.onDestroyed(source); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AreaProtectionSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AreaProtectionSpell.java index 75615785..920c5bd5 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AreaProtectionSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AreaProtectionSpell.java @@ -9,6 +9,7 @@ import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import com.minelittlepony.unicopia.particle.MagicParticleEffect; +import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.entity.Entity; @@ -42,6 +43,8 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell { source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO); } }); + } else { + Ether.get(source.asWorld()).getOrCreate(this, source); } source.findAllSpellsInRange(radius, e -> isValidTarget(source, e)).filter(caster -> !caster.hasCommonOwner(source)).forEach(caster -> { @@ -51,6 +54,11 @@ public class AreaProtectionSpell extends AbstractAreaEffectSpell { return !isDead(); } + @Override + protected void onDestroyed(Caster caster) { + Ether.get(caster.asWorld()).remove(this, caster); + } + /** * Calculates the maximum radius of the shield. aka The area of effect. */ diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisperseIllusionSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisperseIllusionSpell.java index 4806d10d..9affae3e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisperseIllusionSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisperseIllusionSpell.java @@ -12,7 +12,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.util.math.Vec3d; /** - * An area-effect spell that disperses illussions. + * An area-effect spell that disperses illusions. */ public class DisperseIllusionSpell extends AbstractAreaEffectSpell { protected DisperseIllusionSpell(CustomisedSpellType type) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/HydrophobicSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/HydrophobicSpell.java index 5ff51120..d88d9dd0 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/HydrophobicSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/HydrophobicSpell.java @@ -5,14 +5,16 @@ import java.util.Set; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Caster; -import com.minelittlepony.unicopia.ability.magic.CasterView; +import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; import com.minelittlepony.unicopia.ability.magic.spell.Situation; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; +import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.shape.*; @@ -21,8 +23,10 @@ import net.minecraft.fluid.*; import net.minecraft.nbt.*; import net.minecraft.state.property.Properties; import net.minecraft.registry.tag.TagKey; +import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.BlockView; import net.minecraft.world.World; public class HydrophobicSpell extends AbstractSpell { @@ -41,16 +45,15 @@ public class HydrophobicSpell extends AbstractSpell { } @Override - public boolean apply(Caster source) { - if (getTraits().get(Trait.GENEROSITY) > 0) { - return toPlaceable().apply(source); + public Spell prepareForCast(Caster caster, CastingMethod method) { + if ((method == CastingMethod.DIRECT || method == CastingMethod.STAFF) && getTraits().get(Trait.GENEROSITY) > 0) { + return toPlaceable(); } - return super.apply(source); + return this; } @Override public boolean tick(Caster source, Situation situation) { - if (!source.isClient()) { World world = source.asWorld(); @@ -86,7 +89,11 @@ public class HydrophobicSpell extends AbstractSpell { setDead(); } - source.spawnParticles(new Sphere(true, getRange(source)), 10, pos -> { + double range = getRange(source); + var entry = Ether.get(source.asWorld()).getOrCreate(this, source); + entry.radius = (float)range; + + source.spawnParticles(new Sphere(true, range), 10, pos -> { BlockPos bp = BlockPos.ofFloored(pos); if (source.asWorld().getFluidState(bp.up()).isIn(affectedFluid)) { source.addParticle(UParticles.RAIN_DROPS, pos, Vec3d.ZERO); @@ -107,6 +114,7 @@ public class HydrophobicSpell extends AbstractSpell { @Override protected void onDestroyed(Caster caster) { + Ether.get(caster.asWorld()).remove(this, caster); storedFluidPositions.removeIf(entry -> { entry.restore(caster.asWorld()); return true; @@ -162,13 +170,20 @@ public class HydrophobicSpell extends AbstractSpell { } } - public boolean blocksFlow(Caster caster, BlockPos pos, FluidState fluid) { - return fluid.isIn(affectedFluid) && pos.isWithinDistance(caster.getOrigin(), getRange(caster) + 1); + public boolean blocksFlow(Ether.Entry entry, Vec3d center, BlockPos pos, FluidState fluid) { + return fluid.isIn(affectedFluid) && pos.isWithinDistance(center, (double)entry.radius + 1); } - public static boolean blocksFluidFlow(CasterView world, BlockPos pos, FluidState state) { - return world.findAllSpellsInRange(pos, 500, SpellType.HYDROPHOBIC).anyMatch(pair -> { - return pair.getValue().blocksFlow(pair.getKey(), pos, state); - }); + public static boolean blocksFluidFlow(BlockView world, BlockPos pos, FluidState state) { + if (world instanceof ServerWorld sw) { + return Ether.get(sw).anyMatch(SpellType.HYDROPHOBIC, entry -> { + var spell = entry.getSpell(); + var target = entry.entity.getTarget().orElse(null); + return spell != null && target != null && spell.blocksFlow(entry, target.pos(), pos, state); + }); + } + + return false; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index 680a0ff1..8b25fbe9 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -1,6 +1,9 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect; import java.util.Optional; +import java.util.UUID; + +import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; @@ -34,6 +37,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme .build(); private static final Shape PARTICLE_AREA = new Sphere(true, 2, 1, 1, 0); + @Nullable + private UUID targetPortalId; private final EntityReference teleportationTarget = new EntityReference<>(); private boolean publishedPosition; @@ -63,10 +68,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme Vec3d origin = source.getOriginVector(); ParticleEffect effect = teleportationTarget.getTarget() - .map(target -> { - getType(); - return (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK); - }) + .map(target -> (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK)) .orElse(ParticleTypes.ELECTRIC_SPARK); source.spawnParticles(origin, particleArea, 5, pos -> { @@ -82,7 +84,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme }); } else { teleportationTarget.getTarget().ifPresent(target -> { - if (Ether.get(source.asWorld()).getEntry(getType(), target.uuid()).isEmpty()) { + if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) != null) { Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid()); teleportationTarget.set(null); setDirty(); @@ -96,18 +98,15 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme ); } - if (!publishedPosition) { - publishedPosition = true; - Ether.Entry entry = Ether.get(source.asWorld()).put(getType(), source); - entry.pitch = pitch; - entry.yaw = yaw; - } + var entry = Ether.get(source.asWorld()).getOrCreate(this, source); + entry.pitch = pitch; + entry.yaw = yaw; } return !isDead(); } - private void tickWithTargetLink(Caster source, Ether.Entry destination) { + private void tickWithTargetLink(Caster source, Ether.Entry destination) { destination.entity.getTarget().ifPresent(target -> { source.findAllEntitiesInRange(1).forEach(entity -> { @@ -142,20 +141,21 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme return; } - Ether ether = Ether.get(source.asWorld()); - ether.getEntries(getType()) - .stream() - .filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) - .findAny() - .ifPresent(entry -> { + Ether.get(source.asWorld()).anyMatch(getType(), entry -> { + if (entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) { entry.setTaken(true); teleportationTarget.copyFrom(entry.entity); + targetPortalId = entry.getSpellId(); setDirty(); - }); + } + return false; + }); } - private Optional getTarget(Caster source) { - return teleportationTarget.getTarget().flatMap(target -> Ether.get(source.asWorld()).getEntry(getType(), target.uuid())); + @SuppressWarnings("unchecked") + private Optional> getTarget(Caster source) { + return teleportationTarget.getTarget() + .map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target, targetPortalId)); } @Override @@ -189,7 +189,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme protected void onDestroyed(Caster caster) { particleEffect.destroy(); Ether ether = Ether.get(caster.asWorld()); - ether.remove(getType(), caster.asEntity().getUuid()); + ether.remove(getType(), caster); getTarget(caster).ifPresent(e -> e.setTaken(false)); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java index 0be0da61..68cf31a5 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java @@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.client; import java.lang.ref.WeakReference; import java.util.Map; -import java.util.Optional; import java.util.function.Predicate; import java.util.function.Supplier; @@ -13,14 +12,12 @@ import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.FlightType; import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.USounds; -import com.minelittlepony.unicopia.ability.magic.CasterView; import com.minelittlepony.unicopia.client.gui.DismissSpellScreen; import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters; import com.minelittlepony.unicopia.client.sound.*; import com.minelittlepony.unicopia.entity.player.PlayerPhysics; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity; -import com.minelittlepony.unicopia.server.world.Ether; import com.mojang.authlib.GameProfile; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -37,29 +34,17 @@ import net.minecraft.entity.passive.BeeEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.vehicle.AbstractMinecartEntity; import net.minecraft.network.PacketByteBuf; -import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.util.Identifier; import net.minecraft.util.math.random.Random; -import net.minecraft.world.BlockView; import net.minecraft.world.World; public class ClientInteractionManager extends InteractionManager { private final MinecraftClient client = MinecraftClient.getInstance(); - private final Optional clientWorld = Optional.of(() -> MinecraftClient.getInstance().world); - private final Int2ObjectMap> playingSounds = new Int2ObjectOpenHashMap<>(); - @Override - public Optional getCasterView(BlockView view) { - if (view instanceof ServerWorld world) { - return Optional.of(Ether.get(world)); - } - return clientWorld; - } - @Override public Map readChapters(PacketByteBuf buffer) { return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter); diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinFlowableFluid.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinFlowableFluid.java index dff7a8f9..14a9a84d 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinFlowableFluid.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinFlowableFluid.java @@ -5,7 +5,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.ability.magic.spell.effect.HydrophobicSpell; import net.minecraft.block.BlockState; @@ -17,7 +16,7 @@ import net.minecraft.world.BlockView; abstract class MixinFlowableFluid { @Inject(method = "canFill", at = @At("HEAD"), cancellable = true) private void onCanFill(BlockView world, BlockPos pos, BlockState state, Fluid fluid, CallbackInfoReturnable info) { - if (InteractionManager.instance().getCasterView(world).filter(view -> HydrophobicSpell.blocksFluidFlow(view, pos, fluid.getDefaultState())).isPresent()) { + if (HydrophobicSpell.blocksFluidFlow(world, pos, fluid.getDefaultState())) { info.setReturnValue(false); } } diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java index b6eb0eea..8d2e309c 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java @@ -1,88 +1,93 @@ package com.minelittlepony.unicopia.server.world; +import java.lang.ref.WeakReference; import java.util.*; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.ability.magic.CasterView; import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.util.NbtSerialisable; - import net.minecraft.nbt.*; import net.minecraft.util.Identifier; import net.minecraft.world.PersistentState; import net.minecraft.world.World; -public class Ether extends PersistentState implements CasterView { +public class Ether extends PersistentState { private static final Identifier ID = Unicopia.id("ether"); public static Ether get(World world) { return WorldOverlay.getPersistableStorage(world, ID, Ether::new, Ether::new); } - private final Map> advertisingEndpoints = new HashMap<>(); + private final Map>>> endpoints; private final Object locker = new Object(); private final World world; Ether(World world, NbtCompound compound) { - this(world); - compound.getKeys().forEach(key -> { - Identifier typeId = Identifier.tryParse(key); - if (typeId != null) { - Set uuids = getEntries(typeId); - compound.getList(key, NbtElement.COMPOUND_TYPE).forEach(entry -> { - Entry e = new Entry(); - e.fromNBT((NbtCompound)entry); - uuids.add(e); - }); - } + this.world = world; + this.endpoints = NbtSerialisable.readMap(compound.getCompound("endpoints"), Identifier::tryParse, typeNbt -> { + return NbtSerialisable.readMap((NbtCompound)typeNbt, UUID::fromString, entityNbt -> { + return NbtSerialisable.readMap((NbtCompound)entityNbt, UUID::fromString, Entry::new); + }); }); } Ether(World world) { this.world = world; + this.endpoints = new HashMap<>(); } @Override public NbtCompound writeNbt(NbtCompound compound) { synchronized (locker) { - advertisingEndpoints.forEach((id, uuids) -> { - NbtList list = new NbtList(); - uuids.forEach(uuid -> { - if (uuid.isAlive()) { - list.add(uuid.toNBT()); - } + pruneNodes(); + compound.put("endpoints", NbtSerialisable.writeMap(endpoints, Identifier::toString, entities -> { + return NbtSerialisable.writeMap(entities, UUID::toString, spells -> { + return NbtSerialisable.writeMap(spells, UUID::toString, Entry::toNBT); }); - compound.put(id.toString(), list); - }); - + })); return compound; } } - public Entry put(SpellType spellType, Caster caster) { + @SuppressWarnings("unchecked") + public Entry getOrCreate(T spell, Caster caster) { synchronized (locker) { - var entry = new Entry(caster); - getEntries(spellType.getId()).add(entry); - markDirty(); + Entry entry = (Entry)endpoints + .computeIfAbsent(spell.getType().getId(), typeId -> new HashMap<>()) + .computeIfAbsent(caster.asEntity().getUuid(), entityId -> new HashMap<>()) + .computeIfAbsent(spell.getUuid(), spellid -> { + markDirty(); + return new Entry<>(spell, caster); + }); + if (entry.removed) { + entry.removed = false; + markDirty(); + } + if (entry.spell.get() != spell) { + entry.spell = new WeakReference<>(spell); + markDirty(); + } return entry; } } - public void remove(SpellType spellType, UUID id) { + public void remove(SpellType spellType, UUID entityId) { synchronized (locker) { - Identifier typeId = spellType.getId(); - Set refs = advertisingEndpoints.get(typeId); - if (refs != null) { - refs.removeIf(ref -> ref.isDead() || ref.entity.getTarget().filter(target -> id.equals(target.uuid())).isPresent()); - if (refs.isEmpty()) { - advertisingEndpoints.remove(typeId); + endpoints.computeIfPresent(spellType.getId(), (typeId, entries) -> { + if (entries.remove(entityId) != null) { + markDirty(); } - markDirty(); - } + return entries.isEmpty() ? null : entries; + }); } } @@ -90,72 +95,123 @@ public class Ether extends PersistentState implements CasterView { remove(spellType, caster.asEntity().getUuid()); } - public Set getEntries(SpellType spellType) { - return getEntries(spellType.getId()); + public void remove(T spell, Caster caster) { + Entry entry = get(spell, caster); + if (entry != null) { + entry.markDead(); + } } - private Set getEntries(Identifier typeId) { + @SuppressWarnings("unchecked") + public Entry get(T spell, Caster caster) { + return get((SpellType)spell.getType(), caster.asEntity().getUuid(), spell.getUuid()); + } + + public Entry get(SpellType spell, EntityReference.EntityValues entityId, @Nullable UUID spellId) { + return get(spell, entityId.uuid(), spellId); + } + + @SuppressWarnings("unchecked") + @Nullable + private Entry get(SpellType spell, UUID entityId, @Nullable UUID spellId) { + if (spellId == null) { + return null; + } synchronized (locker) { - return advertisingEndpoints.compute(typeId, (k, old) -> { - if (old == null) { - old = new HashSet<>(); - } else { - old.removeIf(Entry::isDead); + Entry entry = endpoints + .getOrDefault(spell.getId(), Map.of()) + .getOrDefault(entityId, Map.of()) + .get(spellId); + return entry == null || entry.isDead() ? null : (Entry)entry; + } + } + + public boolean anyMatch(SpellType spellType, BiPredicate> condition) { + return anyMatch(spellType, entry -> { + var spell = entry.getSpell(); + var caster = entry.getCaster(); + return spell != null && caster != null && condition.test(spell, caster); + }); + } + + @SuppressWarnings("unchecked") + public boolean anyMatch(SpellType spellType, Predicate> condition) { + synchronized (locker) { + for (var entries : endpoints.getOrDefault(spellType.getId(), Map.of()).values()) { + for (var entry : entries.values()) { + if (!entry.isDead() && condition.test((Entry)entry)) { + return true; + } } - return old; + } + } + return false; + } + + private void pruneNodes() { + this.endpoints.values().removeIf(entities -> { + entities.values().removeIf(spells -> { + spells.values().removeIf(Entry::isDead); + return spells.isEmpty(); }); - } + return entities.isEmpty(); + }); } - public Optional getEntry(SpellType spellType, Caster caster) { - synchronized (locker) { - return getEntries(spellType).stream().filter(e -> e.entity.referenceEquals(caster.asEntity())).findFirst(); - } - } - - public Optional getEntry(SpellType spellType, UUID uuid) { - synchronized (locker) { - return getEntries(spellType).stream().filter(e -> e.equals(uuid)).findFirst(); - } - } - - @Override - public World getWorld() { - return world; - } - - public class Entry implements NbtSerialisable { + public class Entry implements NbtSerialisable { public final EntityReference entity; + + @Nullable + private UUID spellId; + private WeakReference spell; + private boolean removed; private boolean taken; public float pitch; public float yaw; + public float radius; - public Entry() { - entity = new EntityReference<>(); + private Entry(NbtElement nbt) { + this.entity = new EntityReference<>(); + this.spell = new WeakReference<>(null); + this.fromNBT((NbtCompound)nbt); } - public Entry(Caster caster) { - entity = new EntityReference<>(caster.asEntity()); + public Entry(T spell, Caster caster) { + this.entity = new EntityReference<>(caster.asEntity()); + this.spell = new WeakReference<>(spell); + spellId = spell.getUuid(); } boolean isAlive() { - return !removed; + return !isDead(); } boolean isDead() { + if (!removed) { + getSpell(); + } return removed; } + @Nullable + public UUID getSpellId() { + return spellId; + } + public void markDead() { Unicopia.LOGGER.debug("Marking " + entity.getTarget().orElse(null) + " as dead"); removed = true; markDirty(); } + public boolean entityMatches(UUID uuid) { + return entity.getTarget().filter(target -> uuid.equals(target.uuid())).isPresent(); + } + public boolean isAvailable() { - return !removed && !taken; + return !isDead() && !taken; } public void setTaken(boolean taken) { @@ -163,6 +219,43 @@ public class Ether extends PersistentState implements CasterView { markDirty(); } + @Nullable + public T getSpell() { + if (removed) { + return null; + } + T spell = this.spell.get(); + if (spell == null) { + if (spellId != null) { + spell = entity + .getOrEmpty(world) + .flatMap(Caster::of) + .flatMap(caster -> caster.getSpellSlot().get(s -> s.getUuid().equals(spellId), true)) + .orElse(null); + + if (spell != null) { + this.spell = new WeakReference<>(spell); + } + } + } + + if (spell != null && spell.isDead()) { + spellId = null; + spell = null; + markDead(); + } + + return spell; + } + + @Nullable + public Caster getCaster() { + if (removed) { + return null; + } + return Caster.of(this.entity.get(world)).orElse(null); + } + @Override public void toNBT(NbtCompound compound) { entity.toNBT(compound); @@ -170,6 +263,10 @@ public class Ether extends PersistentState implements CasterView { compound.putBoolean("taken", taken); compound.putFloat("pitch", pitch); compound.putFloat("yaw", yaw); + compound.putFloat("radius", radius); + if (spellId != null) { + compound.putUuid("spellId", spellId); + } } @Override @@ -179,20 +276,24 @@ public class Ether extends PersistentState implements CasterView { taken = compound.getBoolean("taken"); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); + radius = compound.getFloat("radius"); + spellId = compound.containsUuid("spellid") ? compound.getUuid("spellId") : null; } @Override public boolean equals(Object other) { - return other instanceof Entry e && e.entity.referenceEquals(entity); + return other instanceof Entry e + && e.entity.referenceEquals(entity) + && Objects.equals(e.spell.get(), spell.get()); } - public boolean equals(UUID uuid) { - return entity.referenceEquals(uuid); + public boolean equals(UUID entityId, UUID spellId) { + return entity.referenceEquals(entityId) && spellId.equals(this.spellId); } @Override public int hashCode() { - return entity.hashCode(); + return Objects.hash(entity, spell.get()); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java b/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java index 7f40eb69..a346c460 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java +++ b/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java @@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.util; import java.util.*; import java.util.function.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import com.mojang.datafixers.util.Pair; @@ -65,6 +66,18 @@ public interface NbtSerialisable { return parent; } + static Map readMap(NbtCompound nbt, Function keyFunction, Function valueFunction) { + return nbt.getKeys().stream().collect(Collectors.toMap(keyFunction, k -> valueFunction.apply(nbt.get(k)))); + } + + static NbtCompound writeMap(Map map, Function keyFunction, Function valueFunction) { + NbtCompound nbt = new NbtCompound(); + map.forEach((k, v) -> { + nbt.put(keyFunction.apply(k), valueFunction.apply(v)); + }); + return nbt; + } + interface Serializer { T read(NbtCompound compound); From 69985e50f9ded41b27f9b073c340dd40191777e1 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 20 Jan 2024 15:42:26 +0000 Subject: [PATCH 41/98] Fixed placed spells breaking when reloading a world --- .../ability/magic/spell/PlaceableSpell.java | 4 ++++ .../advancements/unicopia/earth/dead_ringer.json | 12 ++++++------ .../unicopia/earth/sticks_and_stones.json | 14 +++++++------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index e3736e59..082460cf 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -219,6 +219,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS position.ifPresent(pos -> { compound.put("position", NbtSerialisable.writeVector(pos)); }); + if (placedSpellId != null) { + compound.putUuid("placedSpellId", placedSpellId); + } if (dimension != null) { compound.putString("dimension", dimension.getValue().toString()); } @@ -232,6 +235,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); position = compound.contains("position") ? Optional.of(NbtSerialisable.readVector(compound.getList("position", NbtElement.FLOAT_TYPE))) : Optional.empty(); + placedSpellId = compound.containsUuid("placedSpellId") ? compound.getUuid("placedSpellId") : null; if (compound.contains("dimension", NbtElement.STRING_TYPE)) { Identifier id = Identifier.tryParse(compound.getString("dimension")); if (id != null) { diff --git a/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json b/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json index 881e8c8d..b9859933 100644 --- a/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json +++ b/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json @@ -2,13 +2,13 @@ "parent": "unicopia:unicopia/earth/born_on_a_rock_farm", "display": { "icon": { - "item": "unicopia:rock" + "item": "unicopia:iron_horseshoe" }, "title": { - "translate": "advancements.unicopia.sticks_and_stones.title" + "translate": "advancements.unicopia.dead_ringer.title" }, "description": { - "translate": "advancements.unicopia.sticks_and_stones.description" + "translate": "advancements.unicopia.dead_ringer.description" }, "frame": "task", "show_toast": true, @@ -16,13 +16,13 @@ "hidden": true }, "criteria": { - "killed_entity_with_rock": { + "killed_entity_with_horseshoe": { "trigger": "minecraft:player_killed_entity", "conditions": { "killing_blow": { "tags": [ { - "id": "unicopia:from_rocks", + "id": "unicopia:from_horseshoes", "expected": true } ] @@ -31,6 +31,6 @@ } }, "requirements": [ - [ "killed_entity_with_rock" ] + [ "killed_entity_with_horseshoe" ] ] } diff --git a/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json b/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json index 591e4616..083c3c04 100644 --- a/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json +++ b/src/main/resources/data/unicopia/advancements/unicopia/earth/sticks_and_stones.json @@ -2,13 +2,13 @@ "parent": "unicopia:unicopia/earth/born_on_a_rock_farm", "display": { "icon": { - "item": "unicopia:horseshoe" + "item": "unicopia:rock" }, "title": { - "translate": "advancements.unicopia.dead_ringer.title" + "translate": "advancements.unicopia.sticks_and_stones.title" }, "description": { - "translate": "advancements.unicopia.dead_ringer.description" + "translate": "advancements.unicopia.sticks_and_stones.description" }, "frame": "task", "show_toast": true, @@ -16,13 +16,13 @@ "hidden": true }, "criteria": { - "killed_entity_with_horseshoe": { + "killed_entity_with_rock": { "trigger": "minecraft:player_killed_entity", "conditions": { "killing_blow": { "tags": [ { - "id": "unicopia:from_horseshoes", + "id": "unicopia:from_rocks", "expected": true } ] @@ -31,6 +31,6 @@ } }, "requirements": [ - [ "killed_entity_with_horseshoe" ] + [ "killed_entity_with_rock" ] ] -} +} \ No newline at end of file From 35536e6e4ad9d2a5947b0cee9d07cccdd6e810fa Mon Sep 17 00:00:00 2001 From: LingVarr Date: Sun, 21 Jan 2024 03:03:41 +1100 Subject: [PATCH 42/98] Update ru_ru.json --- .../resources/assets/unicopia/lang/ru_ru.json | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/ru_ru.json b/src/main/resources/assets/unicopia/lang/ru_ru.json index 1e626a2f..eb31d7f4 100644 --- a/src/main/resources/assets/unicopia/lang/ru_ru.json +++ b/src/main/resources/assets/unicopia/lang/ru_ru.json @@ -123,6 +123,7 @@ "item.unicopia.green_apple_seeds": "Семена яблони Гренни Смит", "item.unicopia.sweet_apple_seeds": "Семена яблони Яблочной Аллеи", "item.unicopia.sour_apple_seeds": "Семена кислой яблони", + "item.unicopia.golden_oak_seeds": "Семена золотого дуба", "item.unicopia.apple_pie_hoof": "Яблочный пирог с отпечатком копыта", "item.unicopia.apple_pie_slice": "Кусочек яблочного пирога", "item.unicopia.candied_apple": "Засахаренное яблоко", @@ -248,6 +249,10 @@ "block.unicopia.weather_vane": "Флюгер", "block.unicopia.curing_joke": "Лечащая шутка", "block.unicopia.gold_root": "Золотой корень", + "block.unicopia.golden_oak_sprout": "Росток золотого дуба", + "block.unicopia.golden_oak_sapling": "Саженец золотого дуба", + "block.unicopia.golden_oak_leaves": "Листья золотого дуба", + "block.unicopia.golden_oak_log": "Бревно золотого дуба", "block.unicopia.mango": "Манго", "block.unicopia.mango_leaves": "Листья мангового дерева", "block.unicopia.mango_sapling": "Саженец мангового дерева", @@ -986,13 +991,17 @@ "death.attack.unicopia.kick.player": "%2$s очень сильно пнул %1$s", "death.attack.unicopia.steamroller": "%1$s был расплющен", "death.attack.unicopia.steamroller.self": "%1$s был расплющен", - "death.attack.unicopia.steamroller.player": "%2$s рассправился с %1$s", - "death.attack.unicopia.stalagmite.pegasus": "%1$s пытался устроиться на сталагмите", + "death.attack.unicopia.steamroller.player": "%2$s расплющил %1$s", + "death.attack.unicopia.stalagmite.pegasus": "%1$s попытался устроиться на сталагмите", "death.attack.unicopia.stalagmite.pegasus.player": "%1$s влетел в сталагмит пока боролся %2$s", - "death.attack.unicopia.rock": "%1$s был избит", - "death.attack.unicopia.rock.self": "%1$s был избит", - "death.attack.unicopia.rock.item": "%1$s был избит %2$s используя %3$s", - "death.attack.unicopia.rock.player": "%1$s был избит %2$s", + "death.attack.unicopia.rock": "%1$s был обстрелян камнями", + "death.attack.unicopia.rock.self": "%1$s обстрелял себя камнями", + "death.attack.unicopia.rock.item": "%1$s был обстрелян камнями %2$s используя %3$s", + "death.attack.unicopia.rock.player": "%1$s был обстрелян камнями %2$s", + "death.attack.unicopia.horseshoe": "В %1$s прилетела подкова", + "death.attack.unicopia.horseshoe.self": "%1$s зарядил подковой в себя", + "death.attack.unicopia.horseshoe.item": "%2$s зарядил подковой в %1$s используя %3$s", + "death.attack.unicopia.horseshoe.player": "%2$s зарядил подковой в %1$s", "death.fell.accident.ladder.pegasus": "%1$s забыл, что умеет летать, и упал с лестницы", "death.fell.accident.vines.pegasus": "%1$s забыл, что умеет летать, и упал с лозы", @@ -1159,6 +1168,8 @@ "advancements.unicopia.earth_route.description": "Вступить в клан Яблока", "advancements.unicopia.sticks_and_stones.title": "Палки и камни", "advancements.unicopia.sticks_and_stones.description": "Убейте моба, бросая в него камни", + "advancements.unicopia.dead_ringer.title": "Звон смерти", + "advancements.unicopia.dead_ringer.description": "Убейте моба подковой", "advancements.unicopia.born_on_a_rock_farm.title": "Рождённый на Ферме Камней", "advancements.unicopia.born_on_a_rock_farm.description": "Успешно вырастить свой первый камень", "advancements.unicopia.thats_unusual.title": "Это необычно", From 7b1f5ce0db11a896b5ff4ba837aa792d952af397 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 20 Jan 2024 16:44:33 +0000 Subject: [PATCH 43/98] Fix error loading the dead ringer advancement --- .../data/unicopia/advancements/unicopia/earth/dead_ringer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json b/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json index b9859933..dc70d6e7 100644 --- a/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json +++ b/src/main/resources/data/unicopia/advancements/unicopia/earth/dead_ringer.json @@ -2,7 +2,7 @@ "parent": "unicopia:unicopia/earth/born_on_a_rock_farm", "display": { "icon": { - "item": "unicopia:iron_horseshoe" + "item": "unicopia:iron_horse_shoe" }, "title": { "translate": "advancements.unicopia.dead_ringer.title" From e017fb1b899d27587f74323f18d4599e7e9b3804 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 20 Jan 2024 16:45:22 +0000 Subject: [PATCH 44/98] Fix lag when both a dark vortex and hydrophobic (as well as other combinations) of spells are active at once --- .../ability/magic/spell/effect/AttractiveSpell.java | 3 ++- .../ability/magic/spell/effect/DarkVortexSpell.java | 13 ++++++------- .../unicopia/network/datasync/EffectSync.java | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java index 669411a1..d91970b2 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java @@ -44,9 +44,10 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp if (timer.getTicksRemaining() <= 0) { return false; } + + setDirty(); } - setDirty(); target.getOrEmpty(caster.asWorld()) .filter(entity -> entity.distanceTo(caster.asEntity()) > getDrawDropOffRange(caster)) .ifPresent(entity -> { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index 9402d692..4e50d13e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -80,10 +80,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega return true; } - age++; - setDirty(); - - if (age % 20 == 0) { + if (++age % 20 == 0) { source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1); } @@ -162,7 +159,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega applyRadialEffect(source, e, e.getPos().distanceTo(origin), radius); }); } - setDirty(); }); } } @@ -221,8 +217,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega double massOfTarget = AttractionUtils.getMass(target); - accumulatedMass += massOfTarget; - setDirty(); + if (massOfTarget != 0) { + accumulatedMass += massOfTarget; + setDirty(); + } + target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE); if (!(target instanceof PlayerEntity)) { target.discard(); diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java index f54aebaa..2a4568e9 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java @@ -136,7 +136,7 @@ public class EffectSync implements SpellContainer, NbtSerialisable { @SuppressWarnings("unchecked") private Stream read(@Nullable SpellPredicate type, boolean synchronize, boolean sendUpdate) { if (synchronize && spells.fromNbt(owner.asEntity().getDataTracker().get(param)) && sendUpdate) { - owner.asEntity().getDataTracker().set(param, spells.toNbt()); + write(); } if (type == null) { From 81070be4b3ea012adae135ac523929d63ef4fed2 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 20 Jan 2024 16:45:33 +0000 Subject: [PATCH 45/98] Fixed spells not affecting experience orbs --- src/main/java/com/minelittlepony/unicopia/EquinePredicates.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java b/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java index be559b38..9f055212 100644 --- a/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java +++ b/src/main/java/com/minelittlepony/unicopia/EquinePredicates.java @@ -33,7 +33,7 @@ public interface EquinePredicates { Predicate IS_CASTER = e -> !e.isRemoved() && (e instanceof Caster || IS_PLAYER.test(e)); Predicate IS_PLACED_SPELL = e -> e instanceof Caster && !e.isRemoved(); - Predicate IS_MAGIC_IMMUNE = e -> (e instanceof MagicImmune || !(e instanceof LivingEntity)) && !(e instanceof ItemEntity); + Predicate IS_MAGIC_IMMUNE = e -> (e instanceof MagicImmune || !(e instanceof LivingEntity)) && !(e instanceof ItemEntity) && !(e instanceof ExperienceOrbEntity); Predicate EXCEPT_MAGIC_IMMUNE = IS_MAGIC_IMMUNE.negate(); Predicate VALID_LIVING_AND_NOT_MAGIC_IMMUNE = EntityPredicates.VALID_LIVING_ENTITY.and(EXCEPT_MAGIC_IMMUNE); From b3a7d338d236059c60790074491c600aca79f3c3 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 20 Jan 2024 19:48:43 +0000 Subject: [PATCH 46/98] Reimplement placed spell rendering to no longer require a particle --- .../ability/magic/spell/PlaceableSpell.java | 47 ++++------ .../magic/spell/RainboomAbilitySpell.java | 2 +- .../magic/spell/effect/DisplacementSpell.java | 17 +--- .../magic/spell/effect/PortalSpell.java | 9 -- .../particle/OrientedBillboardParticle.java | 1 - .../client/particle/RunesParticle.java | 1 + .../unicopia/client/render/RenderUtil.java | 24 +++-- .../entity/CastSpellEntityRenderer.java | 4 +- .../render/spell/PlacedSpellRenderer.java | 88 +++++++++++++++++++ .../spell/SpellEffectsRenderDispatcher.java | 6 ++ .../unicopia/particle/ParticleHandle.java | 1 + .../unicopia/particle/UParticles.java | 1 + 12 files changed, 125 insertions(+), 76 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index 082460cf..98095393 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -13,10 +13,6 @@ import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.MsgCasterLookRequest; -import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle; -import com.minelittlepony.unicopia.particle.UParticles; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.NbtSerialisable; @@ -24,6 +20,7 @@ import net.minecraft.nbt.*; import net.minecraft.registry.*; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; @@ -40,11 +37,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Nullable private RegistryKey dimension; - /** - * The visual effect - */ - private final ParticleHandle particlEffect = new ParticleHandle(); - /** * ID of the placed counterpart of this spell. */ @@ -64,6 +56,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS public float pitch; public float yaw; + private int prevAge; + private int age; + private Optional position = Optional.empty(); public PlaceableSpell(CustomisedSpellType type) { @@ -75,15 +70,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS return this; } - @Override - public Collection getDelegates() { - return List.of(spell); + public float getAge(float tickDelta) { + return MathHelper.lerp(tickDelta, prevAge, age); } @Override - public void setDead() { - super.setDead(); - particlEffect.destroy(); + public Collection getDelegates() { + return List.of(spell); } @Override @@ -113,16 +106,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS setDead(); return false; } + } else { + prevAge = age; + if (age < 25) { + age++; + } } - if (spell instanceof PlacementDelegate delegate) { - delegate.updatePlacement(source, this); - } - - getParticleEffectAttachment(source).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_COLOR, spell.getType().getColor()); - }); - return super.tick(source, Situation.GROUND); } @@ -200,12 +190,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS return castEntity.getTarget().map(EntityValues::pos); } - public Optional getParticleEffectAttachment(Caster source) { - return particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, pitch + 90, yaw), Vec3d.ZERO, Vec3d.ZERO); - }); - } - protected Optional getWorld(Caster source) { return Optional.ofNullable(dimension) .map(dim -> source.asWorld().getServer().getWorld(dim)); @@ -214,6 +198,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); + compound.putInt("age", age); compound.putFloat("pitch", pitch); compound.putFloat("yaw", yaw); position.ifPresent(pos -> { @@ -232,6 +217,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); + age = compound.getInt("age"); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); position = compound.contains("position") ? Optional.of(NbtSerialisable.readVector(compound.getList("position", NbtElement.FLOAT_TYPE))) : Optional.empty(); @@ -264,9 +250,6 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS } public interface PlacementDelegate { - void onPlaced(Caster source, PlaceableSpell parent, CastSpellEntity entity); - - void updatePlacement(Caster source, PlaceableSpell parent); } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java index 4bf0d726..d7b7b562 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java @@ -54,7 +54,7 @@ public class RainboomAbilitySpell extends AbstractSpell { }); if (source.isClient()) { - // source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO); + //source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO); } source.findAllEntitiesInRange(RADIUS).forEach(e -> { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisplacementSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisplacementSpell.java index 14d3abf8..a58b77de 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisplacementSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DisplacementSpell.java @@ -6,8 +6,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.*; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; -import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; @@ -16,7 +14,7 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.math.Vec3d; -public class DisplacementSpell extends AbstractSpell implements HomingSpell, PlaceableSpell.PlacementDelegate, ProjectileDelegate.EntityHitListener { +public class DisplacementSpell extends AbstractSpell implements HomingSpell, ProjectileDelegate.EntityHitListener { private final EntityReference target = new EntityReference<>(); @@ -67,19 +65,6 @@ public class DisplacementSpell extends AbstractSpell implements HomingSpell, Pla originator.subtractEnergyCost(destinationPos.distanceTo(sourcePos) / 20F); } - @Override - public void onPlaced(Caster source, PlaceableSpell parent, CastSpellEntity entity) { - - } - - @Override - public void updatePlacement(Caster caster, PlaceableSpell parent) { - parent.getParticleEffectAttachment(caster).ifPresent(attachment -> { - float r = 3 - (1 - ((ticks + 10) / 20F)) * 3; - attachment.setAttribute(Attachment.ATTR_RADIUS, r); - }); - } - private void teleport(Caster source, Entity entity, Vec3d pos, Vec3d vel) { entity.teleport(pos.x, pos.y, pos.z); entity.setVelocity(vel); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index 8b25fbe9..e55f388c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -14,7 +14,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import com.minelittlepony.unicopia.particle.*; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.server.world.Ether; import com.minelittlepony.unicopia.util.shape.*; @@ -177,14 +176,6 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme entity.setPos(targetPos.x, caster.getY() + 1.5, targetPos.z); } - @Override - public void updatePlacement(Caster source, PlaceableSpell parent) { - parent.getParticleEffectAttachment(source).ifPresent(attachment -> { - attachment.setAttribute(Attachment.ATTR_RADIUS, 2); - attachment.setAttribute(Attachment.ATTR_OPACITY, 0.92F); - }); - } - @Override protected void onDestroyed(Caster caster) { particleEffect.destroy(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/OrientedBillboardParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/OrientedBillboardParticle.java index f3f04b23..4c25b465 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/OrientedBillboardParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/OrientedBillboardParticle.java @@ -22,7 +22,6 @@ public abstract class OrientedBillboardParticle extends AbstractBillboardParticl fixed = effect.fixed(); if (fixed) { - // Was hamiltonianProduct (CHECK THIS!!) rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(effect.pitch())); rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - effect.yaw())); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java index 58a8d453..87ee30c2 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java @@ -20,6 +20,7 @@ import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; import net.minecraft.util.math.*; +@Deprecated public class RunesParticle extends OrientedBillboardParticle implements Attachment { private static final Identifier[] TEXTURES = new Identifier[] { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java index a126ee28..fc981555 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java @@ -1,6 +1,5 @@ package com.minelittlepony.unicopia.client.render; -import org.joml.Matrix4f; import org.joml.Vector3f; import org.joml.Vector4f; @@ -11,7 +10,7 @@ import net.minecraft.client.render.VertexFormats; import net.minecraft.client.util.math.MatrixStack; public class RenderUtil { - private static final Vector4f TEMP_VECTOR = new Vector4f(); + public static final Vector4f TEMP_VECTOR = new Vector4f(); public static final Vertex[] UNIT_FACE = new Vertex[] { new Vertex(new Vector3f(0, 0, 0), 1, 1), new Vertex(new Vector3f(0, 1, 0), 1, 0), @@ -21,21 +20,18 @@ public class RenderUtil { public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) { buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); - - Vertex[] UNIT_FACE = new Vertex[] { - new Vertex(new Vector3f(0, 0, 0), 1, 1), - new Vertex(new Vector3f(0, 1, 0), 1, 0), - new Vertex(new Vector3f(1, 1, 0), 0, 0), - new Vertex(new Vector3f(1, 0, 0), 0, 1) - }; - - Matrix4f transformation = matrices.peek().getPositionMatrix(); for (Vertex vertex : UNIT_FACE) { - transformation.transform(TEMP_VECTOR.set(vertex.position(), 1)); - buffer.vertex(TEMP_VECTOR.x, TEMP_VECTOR.y, TEMP_VECTOR.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next(); + Vector4f position = vertex.position(matrices); + buffer.vertex(position.x, position.y, position.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next(); } te.draw(); } - record Vertex(Vector3f position, float u, float v) {} + public record Vertex(Vector3f position, float u, float v) { + + public Vector4f position(MatrixStack matrices) { + matrices.peek().getPositionMatrix().transform(TEMP_VECTOR.set(position, 1)); + return TEMP_VECTOR; + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java index 7b4fae4e..ece6638b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CastSpellEntityRenderer.java @@ -11,8 +11,6 @@ import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.util.Identifier; public class CastSpellEntityRenderer extends EntityRenderer { - private final SpellEffectsRenderDispatcher spellRenderDispatcher = new SpellEffectsRenderDispatcher(); - public CastSpellEntityRenderer(EntityRendererFactory.Context ctx) { super(ctx); } @@ -24,7 +22,7 @@ public class CastSpellEntityRenderer extends EntityRenderer { @Override public void render(CastSpellEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light) { - spellRenderDispatcher.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0); + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, entity, 0, 0, tickDelta, getAnimationProgress(entity, tickDelta), yaw, 0); } protected float getAnimationProgress(CastSpellEntity entity, float tickDelta) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java new file mode 100644 index 00000000..4e293248 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -0,0 +1,88 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import org.joml.Vector3f; +import org.joml.Vector4f; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; +import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.client.render.RenderUtil; + +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.RotationAxis; + +public class PlacedSpellRenderer implements SpellRenderer { + private static final Identifier[] TEXTURES = new Identifier[] { + Unicopia.id("textures/particles/runes_0.png"), + Unicopia.id("textures/particles/runes_1.png"), + Unicopia.id("textures/particles/runes_2.png"), + Unicopia.id("textures/particles/runes_3.png"), + Unicopia.id("textures/particles/runes_4.png"), + Unicopia.id("textures/particles/runes_5.png") + }; + private static final RenderUtil.Vertex[] CORNERS = new RenderUtil.Vertex[]{ + new RenderUtil.Vertex(new Vector3f(-1, -1, 0), 0, 0), + new RenderUtil.Vertex(new Vector3f(-1, 1, 0), 1, 0), + new RenderUtil.Vertex(new Vector3f( 1, 1, 0), 1, 1), + new RenderUtil.Vertex(new Vector3f( 1, -1, 0), 0, 1) + }; + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + + for (Spell delegate : spell.getDelegates()) { + + matrices.push(); + matrices.translate(0, 0.001, 0); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(spell.pitch)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90 - spell.yaw)); + float scale = (spell.getAge(tickDelta) / 25F) * 3; + matrices.scale(scale, scale, scale); + + float alpha = scale; + + float angle = (animationProgress / 9F) % 360; + + int color = delegate.getType().getColor(); + + float red = Color.r(color); + float green = Color.g(color); + float blue = Color.b(color); + + for (int i = 0; i < TEXTURES.length; i++) { + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(TEXTURES[i])); + + for (int dim = 0; dim < 3; dim++) { + float ringSpeed = (i % 2 == 0 ? i : -1) * i; + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim)); + renderQuad(buffer, matrices, red, green, blue, alpha / ((float)(dim * 3) + 1), light); + matrices.pop(); + } + } + + matrices.pop(); + + var renderer = SpellEffectsRenderDispatcher.INSTANCE.getRenderer(delegate); + if (renderer != null) { + renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + } + } + } + + protected final void renderQuad(VertexConsumer buffer, MatrixStack matrices, float red, float green, float blue, float alpha, int light) { + for (var corner : CORNERS) { + Vector4f pos = corner.position(matrices); + buffer.vertex(pos.x, pos.y, pos.z, red, green, blue, alpha, corner.u(), corner.v(), 0, light, 1, 1, 1); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index 7f5d26a8..f44aad5f 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -43,10 +43,16 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader REGISTRY.put(type, rendererFactory); } + static { + register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new); + } + @Nullable private Map, SpellRenderer> renderers = Map.of(); private final MinecraftClient client = MinecraftClient.getInstance(); + private SpellEffectsRenderDispatcher() {} + @Override public Identifier getFabricId() { return ID; diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java index 1fc12c06..b099f347 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java @@ -20,6 +20,7 @@ import net.minecraft.world.World; /** * A connection class for updating and persisting an attached particle effect. */ +@Deprecated public class ParticleHandle { private final Map loadedEffects = new WeakHashMap<>(); diff --git a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java index 8d5208f0..186be3e0 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java @@ -16,6 +16,7 @@ public interface UParticles { ParticleType RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); DefaultParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.simple()); + @Deprecated ParticleType MAGIC_RUNES = register("magic_runes", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple()); From 7fb9543dbe21fd3f57d1216916f5a50d6be0cf6b Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 20 Jan 2024 22:31:32 +0000 Subject: [PATCH 47/98] Move shield effect over to a spell renderer --- .../magic/spell/effect/DarkVortexSpell.java | 9 ++++ .../magic/spell/effect/ShieldSpell.java | 53 +++++++++++-------- .../client/particle/MagicParticle.java | 6 +-- .../unicopia/client/render/RenderLayers.java | 10 +++- .../client/render/model/BakedModel.java | 25 +++++---- .../render/spell/ShieldSpellRenderer.java | 41 ++++++++++++++ .../spell/SpellEffectsRenderDispatcher.java | 1 + 7 files changed, 107 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index 4e50d13e..740a2a21 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -13,6 +13,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; +import com.minelittlepony.unicopia.particle.ParticleHandle; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.SphereParticleEffect; import com.minelittlepony.unicopia.particle.UParticles; @@ -51,6 +52,9 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega private int age = 0; private float accumulatedMass = 0; + @Deprecated + protected final ParticleHandle particlEffect = new ParticleHandle(); + protected DarkVortexSpell(CustomisedSpellType type) { super(type); } @@ -109,6 +113,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega return EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(entity) && getAttractiveForce(source, entity) > 0; } + @Override + protected void onDestroyed(Caster caster) { + particlEffect.destroy(); + } + @Override public void generateParticles(Caster source) { super.generateParticles(source); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java index 71f5cede..023da1c7 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect; +import java.util.Optional; + import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; @@ -10,11 +12,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle; -import com.minelittlepony.unicopia.particle.SphereParticleEffect; -import com.minelittlepony.unicopia.particle.UParticles; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; +import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.projectile.ProjectileUtil; import com.minelittlepony.unicopia.util.shape.Sphere; @@ -30,6 +30,7 @@ import net.minecraft.entity.passive.PassiveEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.vehicle.AbstractMinecartEntity; import net.minecraft.entity.vehicle.BoatEntity; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; public class ShieldSpell extends AbstractSpell { @@ -40,10 +41,14 @@ public class ShieldSpell extends AbstractSpell { .with(Trait.AIR, 9) .build(); - protected final ParticleHandle particlEffect = new ParticleHandle(); - private final TargetSelecter targetSelecter = new TargetSelecter(this); + private float prevRadius; + private float radius; + + private float rangeMultiplier; + private float targetRangeMultiplier; + protected ShieldSpell(CustomisedSpellType type) { super(type); } @@ -53,33 +58,27 @@ public class ShieldSpell extends AbstractSpell { return method == CastingMethod.STAFF || getTraits().get(Trait.GENEROSITY) > 0 ? toPlaceable() : this; } - @Override - protected void onDestroyed(Caster caster) { - particlEffect.destroy(); - } - @Override public Affinity getAffinity() { return getTraits().get(Trait.DARKNESS) > 0 ? Affinity.BAD : Affinity.GOOD; } protected void generateParticles(Caster source) { - float radius = (float)getDrawDropOffRange(source); Vec3d origin = getOrigin(source); source.spawnParticles(origin, new Sphere(true, radius), (int)(radius * 6), pos -> { source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO); - }); - particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, getType().getColor(), 0.3F, radius), origin, Vec3d.ZERO); - }).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_RADIUS, radius); + if (source.asWorld().random.nextInt(10) == 0 && source.asWorld().random.nextFloat() < source.getCorruption().getScaled(1)) { + ParticleUtils.spawnParticle(source.asWorld(), new LightningBoltParticleEffect(true, 3, 2, 0.1F, Optional.empty()), pos, Vec3d.ZERO); + } }); } @Override public boolean tick(Caster source, Situation situation) { + prevRadius = radius; + radius = (float)getDrawDropOffRange(source); if (source.isClient()) { generateParticles(source); @@ -108,20 +107,32 @@ public class ShieldSpell extends AbstractSpell { cost *= costMultiplier / ((1 + source.getLevel().get()) * 3F); cost /= knowledge; - cost += getDrawDropOffRange(source) / 10F; + cost += radius / 10F; if (!source.subtractEnergyCost(cost)) { setDead(); } } + public float getRadius(float tickDelta) { + return MathHelper.lerp(tickDelta, prevRadius, radius); + } + /** * Calculates the maximum radius of the shield. aka The area of effect. */ public double getDrawDropOffRange(Caster source) { - float multiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2; + targetRangeMultiplier = source instanceof Pony pony && pony.asEntity().isSneaking() ? 1 : 2; + if (rangeMultiplier < targetRangeMultiplier - 0.1F) { + rangeMultiplier += 0.1F; + } else if (rangeMultiplier > targetRangeMultiplier + 0.1) { + rangeMultiplier -= 0.1F; + } else { + rangeMultiplier = targetRangeMultiplier; + } + float min = (source instanceof Pony ? 4 : 6) + getTraits().get(Trait.POWER); - double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / multiplier; + double range = (min + (source.getLevel().getScaled(source instanceof Pony ? 4 : 40) * (source instanceof Pony ? 2 : 10))) / rangeMultiplier; return range; } @@ -150,7 +161,7 @@ public class ShieldSpell extends AbstractSpell { } protected long applyEntities(Caster source) { - double radius = getDrawDropOffRange(source); + double radius = this.radius; Vec3d origin = getOrigin(source); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java index 700963a6..fd40922f 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java @@ -22,9 +22,9 @@ public class MagicParticle extends SpriteBillboardParticle { velocityX = vX; velocityY = vY; velocityZ = vZ; - startX = x + random.nextGaussian()/3; - startY = y + random.nextGaussian()/3; - startZ = z + random.nextGaussian()/3; + startX = x;// + random.nextGaussian()/3; + startY = y;// + random.nextGaussian()/3; + startZ = z;// + random.nextGaussian()/3; scale = random.nextFloat() * 0.12F; maxAge = (int)(Math.random() * 10) + 20; diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java index 80eeefd4..5f58b7b1 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java @@ -72,7 +72,15 @@ public final class RenderLayers extends RenderLayer { } public static RenderLayer getMagicNoColor() { - return MAGIC_NO_COLOR; + //return MAGIC_NO_COLOR; + return of("magic_no_color", VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL, + VertexFormat.DrawMode.QUADS, 256, true, true, MultiPhaseParameters.builder() + .program(COLOR_PROGRAM) + .transparency(TRANSLUCENT_TRANSPARENCY) + .target(TRANSLUCENT_TARGET) + .cull(DISABLE_CULLING) + .writeMaskState(COLOR_MASK) + .build(false)); } public static RenderLayer getMagicColored() { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java index f868ff04..694571c3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java @@ -3,38 +3,37 @@ package com.minelittlepony.unicopia.client.render.model; import java.util.ArrayList; import java.util.List; -import org.joml.Matrix4f; +import org.joml.Vector3f; import org.joml.Vector4f; +import com.minelittlepony.unicopia.client.render.RenderUtil; + import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.util.math.MatrixStack; public class BakedModel { - private static final Vector4f drawVert = new Vector4f(); - - protected final List vertices = new ArrayList<>(); + protected final List vertices = new ArrayList<>(); protected void addVertex(Vector4f vertex) { addVertex(vertex.x, vertex.y, vertex.z, 0, 0); } protected void addVertex(float x, float y, float z, float u, float v) { - vertices.add(new Vertex(x, y, z, u, v)); + vertices.add(new RenderUtil.Vertex(new Vector3f(x, y, z), u, v)); } - public final void render(MatrixStack matrices, VertexConsumer vertexWriter, int light, int overlay, float scale, float r, float g, float b, float a) { + public final void render(MatrixStack matrices, VertexConsumer buffer, int light, int overlay, float scale, float r, float g, float b, float a) { scale = Math.abs(scale); if (scale < 0.001F) { return; } - Matrix4f model = matrices.peek().getPositionMatrix(); - for (Vertex vertex : vertices) { - drawVert.set(vertex.x() * scale, vertex.y() * scale, vertex.z() * scale, 1); - drawVert.mul(model); - vertexWriter.vertex(drawVert.x, drawVert.y, drawVert.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0); + matrices.push(); + matrices.scale(scale, scale, scale); + for (RenderUtil.Vertex vertex : vertices) { + Vector4f pos = vertex.position(matrices); + buffer.vertex(pos.x, pos.y, pos.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0); } + matrices.pop(); } - - record Vertex(float x, float y, float z, float u, float v) {} } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java new file mode 100644 index 00000000..45731fd0 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java @@ -0,0 +1,41 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.ShieldSpell; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.SphereModel; +import com.minelittlepony.unicopia.util.ColorHelper; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.VertexConsumerProvider.Immediate; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; + +public class ShieldSpellRenderer implements SpellRenderer { + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, ShieldSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + matrices.push(); + double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; + matrices.translate(0, height, 0); + + Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers(); + immediate.draw(); + + int color = ColorHelper.lerp(caster.getCorruption().getScaled(1) * (tickDelta / (1 + caster.asWorld().random.nextFloat())), spell.getType().getColor(), 0xFF000); + float[] colors = ColorHelper.changeSaturation(Color.r(color), Color.g(color), Color.b(color), 4); + float radius = 0.35F + spell.getRadius(tickDelta) + MathHelper.sin(animationProgress / 30F) * 0.01F; + + VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicNoColor()); + + float thickness = 0.02F * MathHelper.sin(animationProgress / 30F); + float alpha = 1 - Math.abs(MathHelper.sin(animationProgress / 20F)) * 0.2F; + SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness, colors[0], colors[1], colors[2], alpha * 0.08F); + SphereModel.SPHERE.render(matrices, buffer, light, 1, radius - thickness, colors[0], colors[1], colors[2], alpha * 0.05F); + SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness * 2, colors[0], colors[1], colors[2], alpha * 0.05F); + + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index f44aad5f..692a8e89 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -45,6 +45,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader static { register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new); + register(SpellType.SHIELD, ShieldSpellRenderer::new); } @Nullable From 3827deb2350170ba49242f98e877202d133e296b Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 21 Jan 2024 15:17:21 +0000 Subject: [PATCH 48/98] Set up a proper render layer for shields --- .../magic/spell/effect/DarkVortexSpell.java | 14 +++-------- .../unicopia/client/render/RenderLayers.java | 23 +++++++++++-------- .../render/spell/ShieldSpellRenderer.java | 2 +- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index 740a2a21..0cea87e2 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -11,12 +11,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.ParticleHandle; import com.minelittlepony.unicopia.particle.ParticleUtils; -import com.minelittlepony.unicopia.particle.SphereParticleEffect; -import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; import com.minelittlepony.unicopia.util.shape.Sphere; @@ -113,16 +110,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega return EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(entity) && getAttractiveForce(source, entity) > 0; } - @Override - protected void onDestroyed(Caster caster) { - particlEffect.destroy(); - } - @Override public void generateParticles(Caster source) { super.generateParticles(source); - float radius = (float)getEventHorizonRadius(); + /*float radius = (float)getEventHorizonRadius(); particlEffect.update(getUuid(), source, spawner -> { spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0x000000, 0.99F, radius, SPHERE_OFFSET), source.getOriginVector(), Vec3d.ZERO); @@ -134,7 +126,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega spawner.addParticle(new SphereParticleEffect(UParticles.DISK, 0xAAAAAA, 0.4F, radius + 1, SPHERE_OFFSET), getOrigin(source), Vec3d.ZERO); }).ifPresent(p -> { p.setAttribute(Attachment.ATTR_RADIUS, radius * 0F); - }); + });*/ source.spawnParticles(ParticleTypes.SMOKE, 3); } @@ -185,7 +177,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega // 2. max force (at dist 0) is taken from accumulated mass // 3. force reaches 0 at distance of drawDropOffRange - private double getEventHorizonRadius() { + public double getEventHorizonRadius() { return Math.sqrt(Math.max(0.001, getMass() - 12)); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java index 5f58b7b1..45a3ae35 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderLayers.java @@ -39,6 +39,15 @@ public final class RenderLayers extends RenderLayer { .target(TRANSLUCENT_TARGET) .build(false)); + private static final RenderLayer MAGIC_SHIELD = of("magic_shield", VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL, + VertexFormat.DrawMode.QUADS, 256, true, true, MultiPhaseParameters.builder() + .program(COLOR_PROGRAM) + .transparency(TRANSLUCENT_TRANSPARENCY) + .target(TRANSLUCENT_TARGET) + .cull(DISABLE_CULLING) + .writeMaskState(COLOR_MASK) + .build(false)); + private static final Function MAGIC_COLORIN_FUNC = Util.memoize(color -> { return of("magic_colored_" + color, VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL, @@ -72,15 +81,11 @@ public final class RenderLayers extends RenderLayer { } public static RenderLayer getMagicNoColor() { - //return MAGIC_NO_COLOR; - return of("magic_no_color", VertexFormats.POSITION_COLOR_TEXTURE_OVERLAY_LIGHT_NORMAL, - VertexFormat.DrawMode.QUADS, 256, true, true, MultiPhaseParameters.builder() - .program(COLOR_PROGRAM) - .transparency(TRANSLUCENT_TRANSPARENCY) - .target(TRANSLUCENT_TARGET) - .cull(DISABLE_CULLING) - .writeMaskState(COLOR_MASK) - .build(false)); + return MAGIC_NO_COLOR; + } + + public static RenderLayer getMagicShield() { + return MAGIC_SHIELD; } public static RenderLayer getMagicColored() { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java index 45731fd0..347a66ba 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java @@ -28,7 +28,7 @@ public class ShieldSpellRenderer implements SpellRenderer { float[] colors = ColorHelper.changeSaturation(Color.r(color), Color.g(color), Color.b(color), 4); float radius = 0.35F + spell.getRadius(tickDelta) + MathHelper.sin(animationProgress / 30F) * 0.01F; - VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicNoColor()); + VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicShield()); float thickness = 0.02F * MathHelper.sin(animationProgress / 30F); float alpha = 1 - Math.abs(MathHelper.sin(animationProgress / 20F)) * 0.2F; From e09ade57b15935a1ef9d838537e2e1c8cafac0b2 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 21 Jan 2024 15:19:49 +0000 Subject: [PATCH 49/98] Add a method to render a spell using the dispatcher --- .../client/render/spell/PlacedSpellRenderer.java | 5 +---- .../render/spell/SpellEffectsRenderDispatcher.java | 12 ++++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java index 4e293248..78789761 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -72,10 +72,7 @@ public class PlacedSpellRenderer implements SpellRenderer { matrices.pop(); - var renderer = SpellEffectsRenderDispatcher.INSTANCE.getRenderer(delegate); - if (renderer != null) { - renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); - } + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index 692a8e89..e911a81a 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -64,12 +64,16 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader return (SpellRenderer)renderers.get(spell.getType()); } + public void render(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + var renderer = getRenderer(spell); + if (renderer != null) { + renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + } + } + public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, Caster caster, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { caster.getSpellSlot().forEach(spell -> { - var renderer = getRenderer(spell); - if (renderer != null) { - renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); - } + render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); return Operation.SKIP; }, false); From 9510b83c72f7e2ff21ad025ad0547739f7d3f6af Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 21 Jan 2024 18:06:53 +0000 Subject: [PATCH 50/98] Implement a renderer for the back hole --- .../ability/magic/spell/PlaceableSpell.java | 10 +- .../magic/spell/effect/AttractiveSpell.java | 6 +- .../magic/spell/effect/DarkVortexSpell.java | 38 +++---- .../render/spell/DarkVortexSpellRenderer.java | 99 +++++++++++++++++++ .../render/spell/PlacedSpellRenderer.java | 5 + .../render/spell/ShieldSpellRenderer.java | 5 - .../spell/SpellEffectsRenderDispatcher.java | 2 + .../unicopia/entity/mob/CastSpellEntity.java | 1 + .../unicopia/entity/player/PlayerCamera.java | 2 + 9 files changed, 133 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index 98095393..bba345b4 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -106,11 +106,11 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS setDead(); return false; } - } else { - prevAge = age; - if (age < 25) { - age++; - } + } + + prevAge = age; + if (age < 25) { + age++; } return super.tick(source, Situation.GROUND); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java index d91970b2..a28dc8ee 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java @@ -7,7 +7,6 @@ import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.particle.FollowingParticleEffect; -import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; @@ -16,6 +15,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.entity.Entity; import net.minecraft.entity.ItemEntity; import net.minecraft.nbt.NbtCompound; +import net.minecraft.particle.ParticleTypes; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -60,12 +60,12 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp @Override public void generateParticles(Caster source) { - double range = getDrawDropOffRange(source) + 10; + double range = getDrawDropOffRange(source); source.spawnParticles(getOrigin(source), new Sphere(false, range), 7, p -> { source.addParticle( new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.asEntity(), 0.4F) - .withChild(new MagicParticleEffect(getType().getColor())), + .withChild(ParticleTypes.AMBIENT_ENTITY_EFFECT), p, Vec3d.ZERO ); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index 0cea87e2..edf3b732 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -11,9 +11,10 @@ import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; +import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle; import com.minelittlepony.unicopia.particle.ParticleUtils; +import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; import com.minelittlepony.unicopia.util.shape.Sphere; @@ -49,9 +50,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega private int age = 0; private float accumulatedMass = 0; - @Deprecated - protected final ParticleHandle particlEffect = new ParticleHandle(); - protected DarkVortexSpell(CustomisedSpellType type) { super(type); } @@ -114,21 +112,17 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega public void generateParticles(Caster source) { super.generateParticles(source); - /*float radius = (float)getEventHorizonRadius(); - - particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0x000000, 0.99F, radius, SPHERE_OFFSET), source.getOriginVector(), Vec3d.ZERO); - }).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_RADIUS, radius); - p.setAttribute(Attachment.ATTR_OPACITY, 2F); - }); - particlEffect.update(getUuid(), "_ring", source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.DISK, 0xAAAAAA, 0.4F, radius + 1, SPHERE_OFFSET), getOrigin(source), Vec3d.ZERO); - }).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_RADIUS, radius * 0F); - });*/ - - source.spawnParticles(ParticleTypes.SMOKE, 3); + if (getEventHorizonRadius() > 3) { + double range = getDrawDropOffRange(source); + source.spawnParticles(getOrigin(source), new Sphere(false, range), 17, p -> { + source.addParticle( + new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.asEntity(), 0.4F) + .withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE), + p, + Vec3d.ZERO + ); + }); + } } @Override @@ -178,7 +172,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega // 3. force reaches 0 at distance of drawDropOffRange public double getEventHorizonRadius() { - return Math.sqrt(Math.max(0.001, getMass() - 12)); + return Math.sqrt(Math.max(0.001, getMass() / 3F)); } private double getAttractiveForce(Caster source, Entity target) { @@ -186,8 +180,8 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega } private double getMass() { - float pulse = (float)Math.sin(age * 8) / 1F; - return 10 + Math.min(15, Math.min(0.5F + pulse, (float)Math.exp(age) / 8F - 90) + accumulatedMass / 10F) + pulse; + float pulse = (float)Math.sin(age * 8) / 160F; + return Math.min(15, Math.min(0.5F, (float)Math.exp(age) / 8F - 90) + accumulatedMass / 10F) + pulse; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java new file mode 100644 index 00000000..98b32575 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java @@ -0,0 +1,99 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import org.joml.Vector3f; +import org.joml.Vector4f; + +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.DarkVortexSpell; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.RenderUtil; +import com.minelittlepony.unicopia.client.render.model.SphereModel; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; + +public class DarkVortexSpellRenderer implements SpellRenderer { + + private static float cameraDistortion; + + public static float getCameraDistortion() { + cameraDistortion *= 0.9F; + cameraDistortion = MathHelper.clamp(cameraDistortion, 0, 80); + System.out.println(cameraDistortion); + return cameraDistortion; + } + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, DarkVortexSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + matrices.push(); + double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; + matrices.translate(0, height + 2, 0); + + Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity(); + + float radius = (float)spell.getEventHorizonRadius(); + float absDistance = (float)cameraEntity.getEyePos().distanceTo(caster.getOriginVector().add(0, 2, 0)); + + SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, Math.min(radius, absDistance / 2F), 0, 0, 0, 1); + + matrices.push(); + + + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90)); + matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90 + cameraEntity.getYaw(tickDelta))); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta))); + matrices.scale(0.7F, 1, 1); + + + float distance = 1F / MathHelper.clamp((absDistance / (radius * 4)), 0.0000001F, 1); + distance *= distance; + if (absDistance < radius * 4) { + cameraDistortion += distance; + } + + matrices.scale(distance, distance, distance); + + matrices.push(); + for (int i = 0; i < 10; i++) { + matrices.scale(0.96F, 1, 1); + float brightness = i / 10F; + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicNoColor()), light, 1, radius * (1 + (0.25F * i)), brightness, brightness, brightness, 0.2F); + } + matrices.pop(); + + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getEndPortal()), light, 1, radius * 0.5F, 1, 0.5F, 0, 1); + + matrices.pop(); + + if (radius > 1 && absDistance > radius) { + radius *= 1.1F; + + matrices.scale(radius, radius, radius); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(animationProgress)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(45)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 168)); + + + RenderUtil.Vertex[] CORNERS = new RenderUtil.Vertex[]{ + new RenderUtil.Vertex(new Vector3f(-1, -1, 0), 0, 0), + new RenderUtil.Vertex(new Vector3f(-1, 1, 0), 1, 0), + new RenderUtil.Vertex(new Vector3f( 1, 1, 0), 1, 1), + new RenderUtil.Vertex(new Vector3f( 1, -1, 0), 0, 1) + }; + + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(new Identifier("textures/misc/forcefield.png"))); + + for (var corner : CORNERS) { + Vector4f pos = corner.position(matrices); + buffer.vertex(pos.x, pos.y, pos.z, 1, 1, 1, 1, corner.u(), corner.v(), 0, light, 1, 1, 1); + } + } + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java index 78789761..142b11ac 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -9,6 +9,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.client.render.RenderUtil; +import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.VertexConsumer; @@ -36,6 +37,10 @@ public class PlacedSpellRenderer implements SpellRenderer { @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + if (!(caster.asEntity() instanceof CastSpellEntity)) { + return; + } + for (Spell delegate : spell.getDelegates()) { matrices.push(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java index 347a66ba..55844dd9 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java @@ -7,10 +7,8 @@ import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; import com.minelittlepony.unicopia.util.ColorHelper; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.VertexConsumerProvider.Immediate; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.MathHelper; @@ -21,9 +19,6 @@ public class ShieldSpellRenderer implements SpellRenderer { double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; matrices.translate(0, height, 0); - Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers(); - immediate.draw(); - int color = ColorHelper.lerp(caster.getCorruption().getScaled(1) * (tickDelta / (1 + caster.asWorld().random.nextFloat())), spell.getType().getColor(), 0xFF000); float[] colors = ColorHelper.changeSaturation(Color.r(color), Color.g(color), Color.b(color), 4); float radius = 0.35F + spell.getRadius(tickDelta) + MathHelper.sin(animationProgress / 30F) * 0.01F; diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index e911a81a..9de95849 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -46,6 +46,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader static { register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new); register(SpellType.SHIELD, ShieldSpellRenderer::new); + register(SpellType.DARK_VORTEX, DarkVortexSpellRenderer::new); } @Nullable @@ -67,6 +68,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader public void render(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { var renderer = getRenderer(spell); if (renderer != null) { + MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers().draw(); renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java index df9c7e37..16a4100b 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java @@ -37,6 +37,7 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster type, World world) { super(type, world); + ignoreCameraFrustum = true; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java index 11b71c2e..2a2a8449 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java @@ -5,6 +5,7 @@ import java.util.Optional; import com.minelittlepony.common.util.animation.MotionCompositor; import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell; +import com.minelittlepony.unicopia.client.render.spell.DarkVortexSpellRenderer; import net.minecraft.util.math.Vec3d; @@ -62,6 +63,7 @@ public class PlayerCamera extends MotionCompositor { public double calculateFieldOfView(double fov) { fov += player.getMagicalReserves().getExertion().get() / 5F; fov += getEnergyAddition(); + fov += DarkVortexSpellRenderer.getCameraDistortion(); return fov; } From 134db120cdc584cbc0640f9e1cdb433ee1552312 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 13:17:45 +0000 Subject: [PATCH 51/98] Fix timed spells' timers not displaying correctly --- .../minelittlepony/unicopia/ability/magic/spell/TimedSpell.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimedSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimedSpell.java index 9e19b8ae..d81e4f39 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimedSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/TimedSpell.java @@ -29,7 +29,7 @@ public interface TimedSpell extends Spell { } public float getPercentTimeRemaining(float tickDelta) { - return MathHelper.lerp(tickDelta, prevDuration, duration) / maxDuration; + return MathHelper.lerp(tickDelta, prevDuration, duration) / (float)maxDuration; } public int getTicksRemaining() { From 8cb3ba298d085c75120b9ef272ad1f2fbdb2232a Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 13:19:01 +0000 Subject: [PATCH 52/98] Fixed dark vortex particles not going to its actual center --- .../magic/spell/effect/AttractiveSpell.java | 5 ++-- .../magic/spell/effect/DarkVortexSpell.java | 5 ++-- .../particle/CloudsEscapingParticle.java | 2 +- .../client/particle/MagicParticle.java | 6 ++--- .../client/render/model/PlaneModel.java | 12 ++++++++++ .../render/spell/DarkVortexSpellRenderer.java | 23 ++++--------------- .../render/spell/PlacedSpellRenderer.java | 22 ++---------------- .../unicopia/util/shape/Sphere.java | 15 ++++++++++++ 8 files changed, 44 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/model/PlaneModel.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java index a28dc8ee..9af5cddb 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AttractiveSpell.java @@ -61,10 +61,11 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp @Override public void generateParticles(Caster source) { double range = getDrawDropOffRange(source); + Vec3d origin = getOrigin(source); - source.spawnParticles(getOrigin(source), new Sphere(false, range), 7, p -> { + source.spawnParticles(origin, new Sphere(false, range), 7, p -> { source.addParticle( - new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.asEntity(), 0.4F) + new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F) .withChild(ParticleTypes.AMBIENT_ENTITY_EFFECT), p, Vec3d.ZERO diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index edf3b732..c8fb3a6d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -114,9 +114,10 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega if (getEventHorizonRadius() > 3) { double range = getDrawDropOffRange(source); - source.spawnParticles(getOrigin(source), new Sphere(false, range), 17, p -> { + Vec3d origin = getOrigin(source); + source.spawnParticles(origin, new Sphere(false, range), 17, p -> { source.addParticle( - new FollowingParticleEffect(UParticles.HEALTH_DRAIN, source.asEntity(), 0.4F) + new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F) .withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE), p, Vec3d.ZERO diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/CloudsEscapingParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/CloudsEscapingParticle.java index 50792cfe..32ff15f7 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/CloudsEscapingParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/CloudsEscapingParticle.java @@ -30,7 +30,7 @@ public class CloudsEscapingParticle extends GroundPoundParticle { ); double columnHeight = 1 + age / 30; - new Sphere(true, columnHeight, 1, 1, 1) + new Sphere(true, columnHeight) .translate(center) .randomPoints(random) .forEach(point -> { diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java index fd40922f..700963a6 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/MagicParticle.java @@ -22,9 +22,9 @@ public class MagicParticle extends SpriteBillboardParticle { velocityX = vX; velocityY = vY; velocityZ = vZ; - startX = x;// + random.nextGaussian()/3; - startY = y;// + random.nextGaussian()/3; - startZ = z;// + random.nextGaussian()/3; + startX = x + random.nextGaussian()/3; + startY = y + random.nextGaussian()/3; + startZ = z + random.nextGaussian()/3; scale = random.nextFloat() * 0.12F; maxAge = (int)(Math.random() * 10) + 20; diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/PlaneModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/PlaneModel.java new file mode 100644 index 00000000..bfbcfc24 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/PlaneModel.java @@ -0,0 +1,12 @@ +package com.minelittlepony.unicopia.client.render.model; + +public class PlaneModel extends BakedModel { + public static final PlaneModel INSTANCE = new PlaneModel(); + + private PlaneModel() { + addVertex(-1, -1, 0, 0, 0); + addVertex(-1, 1, 0, 1, 0); + addVertex( 1, 1, 0, 1, 1); + addVertex( 1, -1, 0, 0, 1); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java index 98b32575..0389cfca 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java @@ -1,12 +1,9 @@ package com.minelittlepony.unicopia.client.render.spell; -import org.joml.Vector3f; -import org.joml.Vector4f; - import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.DarkVortexSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; -import com.minelittlepony.unicopia.client.render.RenderUtil; +import com.minelittlepony.unicopia.client.render.model.PlaneModel; import com.minelittlepony.unicopia.client.render.model.SphereModel; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.RenderLayer; @@ -20,12 +17,13 @@ import net.minecraft.util.math.RotationAxis; public class DarkVortexSpellRenderer implements SpellRenderer { + private static final Identifier ECRETION_RING_TEXTURE = new Identifier("textures/misc/forcefield.png"); + private static float cameraDistortion; public static float getCameraDistortion() { cameraDistortion *= 0.9F; cameraDistortion = MathHelper.clamp(cameraDistortion, 0, 80); - System.out.println(cameraDistortion); return cameraDistortion; } @@ -79,20 +77,9 @@ public class DarkVortexSpellRenderer implements SpellRenderer { matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(45)); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 168)); + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(ECRETION_RING_TEXTURE)); - RenderUtil.Vertex[] CORNERS = new RenderUtil.Vertex[]{ - new RenderUtil.Vertex(new Vector3f(-1, -1, 0), 0, 0), - new RenderUtil.Vertex(new Vector3f(-1, 1, 0), 1, 0), - new RenderUtil.Vertex(new Vector3f( 1, 1, 0), 1, 1), - new RenderUtil.Vertex(new Vector3f( 1, -1, 0), 0, 1) - }; - - VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(new Identifier("textures/misc/forcefield.png"))); - - for (var corner : CORNERS) { - Vector4f pos = corner.position(matrices); - buffer.vertex(pos.x, pos.y, pos.z, 1, 1, 1, 1, corner.u(), corner.v(), 0, light, 1, 1, 1); - } + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); } matrices.pop(); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java index 142b11ac..4630912b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -1,14 +1,11 @@ package com.minelittlepony.unicopia.client.render.spell; -import org.joml.Vector3f; -import org.joml.Vector4f; - import com.minelittlepony.common.util.Color; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell; import com.minelittlepony.unicopia.ability.magic.spell.Spell; -import com.minelittlepony.unicopia.client.render.RenderUtil; +import com.minelittlepony.unicopia.client.render.model.PlaneModel; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import net.minecraft.client.render.RenderLayer; @@ -27,12 +24,6 @@ public class PlacedSpellRenderer implements SpellRenderer { Unicopia.id("textures/particles/runes_4.png"), Unicopia.id("textures/particles/runes_5.png") }; - private static final RenderUtil.Vertex[] CORNERS = new RenderUtil.Vertex[]{ - new RenderUtil.Vertex(new Vector3f(-1, -1, 0), 0, 0), - new RenderUtil.Vertex(new Vector3f(-1, 1, 0), 1, 0), - new RenderUtil.Vertex(new Vector3f( 1, 1, 0), 1, 1), - new RenderUtil.Vertex(new Vector3f( 1, -1, 0), 0, 1) - }; @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { @@ -50,8 +41,6 @@ public class PlacedSpellRenderer implements SpellRenderer { float scale = (spell.getAge(tickDelta) / 25F) * 3; matrices.scale(scale, scale, scale); - float alpha = scale; - float angle = (animationProgress / 9F) % 360; int color = delegate.getType().getColor(); @@ -70,7 +59,7 @@ public class PlacedSpellRenderer implements SpellRenderer { matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed)); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim)); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim)); - renderQuad(buffer, matrices, red, green, blue, alpha / ((float)(dim * 3) + 1), light); + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, red, green, blue, scale / ((float)(dim * 3) + 1)); matrices.pop(); } } @@ -80,11 +69,4 @@ public class PlacedSpellRenderer implements SpellRenderer { SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); } } - - protected final void renderQuad(VertexConsumer buffer, MatrixStack matrices, float red, float green, float blue, float alpha, int light) { - for (var corner : CORNERS) { - Vector4f pos = corner.position(matrices); - buffer.vertex(pos.x, pos.y, pos.z, red, green, blue, alpha, corner.u(), corner.v(), 0, light, 1, 1, 1); - } - } } diff --git a/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java b/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java index 7ebf61af..c151b47c 100644 --- a/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java +++ b/src/main/java/com/minelittlepony/unicopia/util/shape/Sphere.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.util.shape; +import org.spongepowered.include.com.google.common.base.Objects; + import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.random.Random; @@ -102,6 +104,19 @@ public class Sphere implements Shape { return stretch.multiply(rad); } + @Override + public boolean equals(Object other) { + return other instanceof Sphere o + && Objects.equal(stretch, o.stretch) + && hollow == o.hollow + && Double.compare(rad, o.rad) == 0; + } + + @Override + public int hashCode() { + return Objects.hashCode(stretch, hollow, rad); + } + public static double computeEllipsoidArea(double rad, Vec3d stretch) { double p = 1.6075; double result = Math.pow(rad * stretch.x, p) * Math.pow(rad * stretch.y, p); From e1d9faa223dd86c1bec6829351ce119adc0f5091 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 13:19:29 +0000 Subject: [PATCH 53/98] Switch to using a spell renderer for bubble spells --- .../magic/spell/effect/BubbleSpell.java | 32 +++++++-------- .../unicopia/client/URenderers.java | 2 + .../particle/FloatingBubbleParticle.java | 31 ++++++++++++++ .../render/spell/BubbleSpellRenderer.java | 41 +++++++++++++++++++ .../unicopia/particle/UParticles.java | 1 + .../assets/unicopia/particles/bubble.json | 5 +++ 6 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/particle/FloatingBubbleParticle.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java create mode 100644 src/main/resources/assets/unicopia/particles/bubble.json diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java index 4a9dc915..db9f7d5e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/BubbleSpell.java @@ -11,12 +11,10 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.particle.ParticleHandle; -import com.minelittlepony.unicopia.particle.SphereParticleEffect; import com.minelittlepony.unicopia.particle.UParticles; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; +import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; @@ -48,11 +46,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, .with(Trait.POWER, 1) .build(); - protected final ParticleHandle particlEffect = new ParticleHandle(); - private final Timer timer; private int struggles; + + private float prevRadius; private float radius; protected BubbleSpell(CustomisedSpellType type) { @@ -66,6 +64,10 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, return timer; } + public float getRadius(float tickDelta) { + return MathHelper.lerp(tickDelta, prevRadius, radius); + } + @Override public boolean apply(Caster source) { @@ -95,14 +97,19 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, public boolean tick(Caster source, Situation situation) { if (situation == Situation.PROJECTILE) { - - source.spawnParticles(ParticleTypes.BUBBLE, 2); + source.spawnParticles(UParticles.BUBBLE, 2); return true; } timer.tick(); - if (timer.getTicksRemaining() <= 0) { + boolean done = timer.getTicksRemaining() <= 0; + + source.spawnParticles(source.getOriginVector().add(0, 1, 0), new Sphere(true, radius * (done ? 0.25F : 0.5F)), done ? 13 : 1, pos -> { + source.addParticle(done ? ParticleTypes.BUBBLE_POP : UParticles.BUBBLE, pos, Vec3d.ZERO); + }); + + if (done) { return false; } @@ -116,7 +123,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, source.asEntity().fallDistance = 0; - Vec3d origin = source.getOriginVector(); + prevRadius = radius; if (source instanceof Pony pony && pony.sneakingChanged() && pony.asEntity().isSneaking()) { setDirty(); @@ -128,18 +135,11 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell, } } - particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0xFFFFFF, 0.3F, 0, new Vec3d(0, radius / 2F, 0)), origin, Vec3d.ZERO); - }).ifPresent(p -> { - p.setAttribute(Attachment.ATTR_RADIUS, radius); - }); - return !isDead(); } @Override protected void onDestroyed(Caster source) { - particlEffect.destroy(); if (source.asEntity() instanceof LivingEntity l) { MODIFIERS.forEach((attribute, modifier) -> { if (l.getAttributes().hasAttribute(attribute)) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 98046073..1d91a1d5 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.block.cloud.CloudChestBlock; import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle; import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle; import com.minelittlepony.unicopia.client.particle.DiskParticle; +import com.minelittlepony.unicopia.client.particle.FloatingBubbleParticle; import com.minelittlepony.unicopia.client.particle.GroundPoundParticle; import com.minelittlepony.unicopia.client.particle.HealthDrainParticle; import com.minelittlepony.unicopia.client.particle.LightningBoltParticle; @@ -65,6 +66,7 @@ public interface URenderers { static void bootstrap() { ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::new)); + ParticleFactoryRegistry.getInstance().register(UParticles.BUBBLE, createFactory(FloatingBubbleParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.RAIN_DROPS, createFactory(RaindropsParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create)); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/FloatingBubbleParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/FloatingBubbleParticle.java new file mode 100644 index 00000000..68d51be7 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/FloatingBubbleParticle.java @@ -0,0 +1,31 @@ +package com.minelittlepony.unicopia.client.particle; + +import net.minecraft.client.particle.ParticleTextureSheet; +import net.minecraft.client.particle.SpriteBillboardParticle; +import net.minecraft.client.particle.SpriteProvider; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.particle.ParticleTypes; + +public class FloatingBubbleParticle extends SpriteBillboardParticle { + public FloatingBubbleParticle(ParticleEffect effect, SpriteProvider provider, ClientWorld clientWorld, double x, double y, double z, double dX, double dY, double dZ) { + super(clientWorld, x, y, z, dX, dY, dZ); + setSprite(provider); + scale((float)clientWorld.random.nextTriangular(1F, 0.5F)); + this.velocityX *= -0.1F; + this.velocityY *= -0.1F; + this.velocityZ *= -0.1F; + this.maxAge *= 3; + } + + @Override + public ParticleTextureSheet getType() { + return ParticleTextureSheet.PARTICLE_SHEET_OPAQUE; + } + + @Override + public void markDead() { + super.markDead(); + world.addParticle(ParticleTypes.BUBBLE_POP, x, y, z, 0, 0, 0); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java new file mode 100644 index 00000000..d15863b7 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java @@ -0,0 +1,41 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.BubbleSpell; +import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.SphereModel; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.RotationAxis; + +public class BubbleSpellRenderer implements SpellRenderer { + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, BubbleSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + matrices.push(); + double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; + matrices.translate(0, height * 0.5F, 0); + + float radius = spell.getRadius(tickDelta) * 0.7F; + + VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicNoColor()); + + Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity(); + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-45)); + matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(45 + cameraEntity.getYaw(tickDelta))); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta))); + + new SphereModel(40, 40, DrawableUtil.PI * 0.25F).render(matrices, buffer, light, 0, radius - 0.1F, 0.9F, 0.9F, 1, 0.3F); + matrices.pop(); + + SphereModel.SPHERE.render(matrices, buffer, light, 0, radius, 0.9F, 0.9F, 1, 0.25F); + + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java index 186be3e0..f1e88314 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java @@ -12,6 +12,7 @@ public interface UParticles { ParticleType UNICORN_MAGIC = register("unicorn_magic", FabricParticleTypes.complex(MagicParticleEffect.FACTORY)); DefaultParticleType CHANGELING_MAGIC = register("changeling_magic", FabricParticleTypes.simple()); + DefaultParticleType BUBBLE = register("bubble", FabricParticleTypes.simple()); ParticleType RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); DefaultParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.simple()); diff --git a/src/main/resources/assets/unicopia/particles/bubble.json b/src/main/resources/assets/unicopia/particles/bubble.json new file mode 100644 index 00000000..21bb4eeb --- /dev/null +++ b/src/main/resources/assets/unicopia/particles/bubble.json @@ -0,0 +1,5 @@ +{ + "textures": [ + "minecraft:bubble" + ] +} From dd9bec2a0ccde9a06b3f6092a2430e170d060dd5 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 13:19:58 +0000 Subject: [PATCH 54/98] Added a visible icon for active spells in the world (shows timer countdown when applicable) --- .../spell/SpellEffectsRenderDispatcher.java | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index 9de95849..341abe8a 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -8,12 +8,18 @@ import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; +import com.minelittlepony.unicopia.client.gui.DrawableUtil; import com.minelittlepony.unicopia.entity.Living; +import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; +import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; +import com.minelittlepony.unicopia.util.ColorHelper; import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; import net.minecraft.client.MinecraftClient; @@ -22,6 +28,7 @@ import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.render.model.json.ModelTransformationMode; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.registry.Registries; import net.minecraft.resource.ResourceManager; @@ -31,6 +38,7 @@ import net.minecraft.util.Colors; import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.Vec3d; @@ -47,6 +55,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader register(SpellType.PLACED_SPELL, PlacedSpellRenderer::new); register(SpellType.SHIELD, ShieldSpellRenderer::new); register(SpellType.DARK_VORTEX, DarkVortexSpellRenderer::new); + register(SpellType.BUBBLE, BubbleSpellRenderer::new); } @Nullable @@ -67,9 +76,15 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader public void render(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { var renderer = getRenderer(spell); + if (renderer != null) { - MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers().draw(); + client.getBufferBuilders().getEntityVertexConsumers().draw(); + renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + + if (EquinePredicates.IS_CASTER.test(client.player)) { + renderGemstone(matrices, vertices, spell, caster, light, tickDelta, animationProgress); + } } } @@ -90,6 +105,38 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader renderers = REGISTRY.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().create())); } + private void renderGemstone(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster caster, int light, float tickDelta, float animationProgress) { + matrices.push(); + + if (!(caster.asEntity() instanceof MagicProjectileEntity)) { + + float y = -caster.asEntity().getHeight(); + if (caster.asEntity() instanceof CastSpellEntity) { + y = 1F; + } + + matrices.translate(0, y + MathHelper.sin(animationProgress / 3F) * 0.2F, 0); + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(animationProgress)); + + client.getItemRenderer().renderItem(spell.getType().withTraits(spell.getTraits()).getDefaultStack(), ModelTransformationMode.FIXED, light, 0, matrices, vertices, caster.asWorld(), 0); + matrices.pop(); + + if (spell instanceof TimedSpell timed && spell.getType() != SpellType.DARK_VORTEX) { + matrices.multiply(client.getEntityRenderDispatcher().getRotation().invert()); + float radius = 0.6F; + float timeRemaining = timed.getTimer().getPercentTimeRemaining(tickDelta); + + DrawableUtil.drawArc(matrices, radius, radius + 0.3F, 0, DrawableUtil.TAU * timeRemaining, + ColorHelper.lerp(MathHelper.clamp(timeRemaining * 4, 0, 1), 0xFF0000FF, 0xFFFFFFFF), + false + ); + } + } + + matrices.pop(); + } + private void renderSpellDebugInfo(MatrixStack matrices, VertexConsumerProvider vertices, Caster caster, int light) { matrices.push(); matrices.multiply(client.getEntityRenderDispatcher().getRotation()); From 39672e5028f2aa5ed3754f2d1e0190b7ca7c5b87 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 13:48:03 +0000 Subject: [PATCH 55/98] Fixed crash when equipping the bangle of comradery --- .../minelittlepony/unicopia/item/FriendshipBraceletItem.java | 2 +- .../java/com/minelittlepony/unicopia/item/WearableItem.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java b/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java index 326ddd43..da2018d0 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java @@ -78,7 +78,7 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem, @Override public EquipmentSlot getSlotType(ItemStack stack) { - return isSigned(stack) ? EquipmentSlot.CHEST : super.getSlotType(); + return isSigned(stack) ? EquipmentSlot.CHEST : super.getSlotType(stack); } private boolean checkSignature(ItemStack stack, PlayerEntity player) { diff --git a/src/main/java/com/minelittlepony/unicopia/item/WearableItem.java b/src/main/java/com/minelittlepony/unicopia/item/WearableItem.java index b745b9bb..1b7067da 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/WearableItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/WearableItem.java @@ -49,6 +49,7 @@ public abstract class WearableItem extends Item implements Equipment { return ArmorMaterials.LEATHER.getEquipSound(); } + @Deprecated @Override public final EquipmentSlot getSlotType() { return getSlotType(getDefaultStack()); From 34aa3b8ced535885cfc245013aadf02d9eb852f8 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 13:48:24 +0000 Subject: [PATCH 56/98] Fixed spells not rendering on non-pony entities --- .../render/AccessoryFeatureRenderer.java | 15 +++++--- .../client/MixinArmorFeatureRenderer.java | 18 +++------- .../client/MixinLivingEntityRenderer.java | 34 ++++++++++++++++++- .../client/MixinPlayerEntityRenderer.java | 23 ++----------- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java index 5ef86cf0..857e4c96 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer; +import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate; import com.minelittlepony.unicopia.client.render.spell.SpellEffectsRenderDispatcher; import net.minecraft.client.MinecraftClient; @@ -13,14 +14,14 @@ import net.minecraft.client.model.ModelPart; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.feature.FeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRendererContext; -import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.LivingEntity; import net.minecraft.util.Arm; public class AccessoryFeatureRenderer< T extends LivingEntity, - M extends BipedEntityModel> extends FeatureRenderer { + M extends EntityModel> extends FeatureRenderer { private static final List> REGISTRY = new ArrayList<>(); @@ -40,6 +41,10 @@ public class AccessoryFeatureRenderer< @Override public void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + if (MineLPDelegate.getInstance().getRace(entity).isEquine()) { + return; + } + features.forEach(feature -> feature.render(matrices, vertexConsumers, light, entity, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch)); Caster.of(entity).ifPresent(caster -> { @@ -60,7 +65,7 @@ public class AccessoryFeatureRenderer< } public interface FeatureFactory { - Feature create(FeatureRendererContext> context); + Feature create(FeatureRendererContext> context); } public interface Feature { @@ -75,11 +80,11 @@ public class AccessoryFeatureRenderer< public interface FeatureRoot< T extends LivingEntity, - M extends BipedEntityModel> { + M extends EntityModel> { AccessoryFeatureRenderer getAccessories(); @SuppressWarnings("unchecked") @Nullable - static > FeatureRoot of(T entity) { + static > FeatureRoot of(T entity) { var renderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity); if (renderer instanceof FeatureRoot) { return (FeatureRoot)renderer; diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinArmorFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinArmorFeatureRenderer.java index be01f778..2e6cc68b 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinArmorFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinArmorFeatureRenderer.java @@ -1,19 +1,9 @@ package com.minelittlepony.unicopia.mixin.client; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer; - -import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRenderer; -import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.render.entity.model.BipedEntityModel; -import net.minecraft.client.render.model.BakedModelManager; -import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.LivingEntity; @Mixin(ArmorFeatureRenderer.class) @@ -21,13 +11,13 @@ abstract class MixinArmorFeatureRenderer< T extends LivingEntity, M extends BipedEntityModel, A extends BipedEntityModel> - extends FeatureRenderer implements AccessoryFeatureRenderer.FeatureRoot { + extends FeatureRenderer/* implements AccessoryFeatureRenderer.FeatureRoot*/ { - private AccessoryFeatureRenderer accessories; + //private AccessoryFeatureRenderer accessories; MixinArmorFeatureRenderer() { super(null); } - @Override + /*@Override public AccessoryFeatureRenderer getAccessories() { return accessories; } @@ -40,5 +30,5 @@ abstract class MixinArmorFeatureRenderer< @Inject(method = "render", at = @At("RETURN")) private void onRender(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch, CallbackInfo info) { getAccessories().render(stack, renderContext, lightUv, entity, limbDistance, limbAngle, tickDelta, age, headYaw, headPitch); - } + }*/ } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinLivingEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinLivingEntityRenderer.java index 3e153574..6f915b70 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinLivingEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinLivingEntityRenderer.java @@ -1,17 +1,25 @@ package com.minelittlepony.unicopia.mixin.client; +import java.util.List; + +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer; import com.minelittlepony.unicopia.client.render.AnimalPoser; import com.minelittlepony.unicopia.client.render.PlayerPoser; +import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer.FeatureRoot; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.LivingEntityRenderer; +import net.minecraft.client.render.entity.feature.FeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.render.entity.model.BipedEntityModel; import net.minecraft.client.render.entity.model.EntityModel; @@ -21,8 +29,31 @@ import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.player.PlayerEntity; @Mixin(LivingEntityRenderer.class) -abstract class MixinLivingEntityRenderer> extends EntityRenderer implements FeatureRendererContext { +abstract class MixinLivingEntityRenderer> extends EntityRenderer + implements FeatureRendererContext, FeatureRoot { + @Shadow + private @Final List> features; + MixinLivingEntityRenderer() { super(null); } + @Nullable + private AccessoryFeatureRenderer accessories; + + @Override + @SuppressWarnings("unchecked") + public AccessoryFeatureRenderer getAccessories() { + if (accessories == null) { + accessories = features.stream() + .filter(a -> a instanceof FeatureRoot) + .map(a -> ((FeatureRoot)a).getAccessories()) + .findFirst() + .orElseGet(() -> { + var feature = new AccessoryFeatureRenderer<>(this); + features.add(feature); + return feature; + }); + } + return accessories; + } @Inject(method = "render", at = @At( @@ -36,6 +67,7 @@ abstract class MixinLivingEntityRenderer)getModel(), PlayerPoser.Context.THIRD_PERSON); } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java index 35503ffd..cd1eac71 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java @@ -1,13 +1,11 @@ package com.minelittlepony.unicopia.mixin.client; -import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer; import com.minelittlepony.unicopia.client.render.PlayerPoser; import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer.FeatureRoot; @@ -21,31 +19,14 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Arm; @Mixin(PlayerEntityRenderer.class) -abstract class MixinPlayerEntityRenderer - extends LivingEntityRenderer> - implements FeatureRoot> { - @Nullable - private AccessoryFeatureRenderer> accessories; - +abstract class MixinPlayerEntityRenderer extends LivingEntityRenderer> { MixinPlayerEntityRenderer() { super(null, null, 0); } - @Override @SuppressWarnings("unchecked") - public AccessoryFeatureRenderer> getAccessories() { - if (accessories == null) { - accessories = features.stream() - .filter(a -> a instanceof FeatureRoot) - .map(a -> ((FeatureRoot>)a).getAccessories()) - .findFirst() - .orElseGet(() -> new AccessoryFeatureRenderer<>(this)); - } - return accessories; - } - @Inject(method = "renderArm", at = @At("RETURN")) private void onRenderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, AbstractClientPlayerEntity player, ModelPart arm, ModelPart sleeve, CallbackInfo info) { Arm a = this.getModel().leftArm == arm ? Arm.LEFT : Arm.RIGHT; - getAccessories().renderArm(matrices, vertexConsumers, light, player, arm, a); + ((FeatureRoot>)this).getAccessories().renderArm(matrices, vertexConsumers, light, player, arm, a); } @Inject(method = "renderArm", From 225c67d5582d51c109f7cbfce63e036891ac6270 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 15:47:58 +0000 Subject: [PATCH 57/98] Tweak dark vortex rendering and fix its behaviour --- .../magic/spell/effect/DarkVortexSpell.java | 37 +++++++------ .../magic/spell/effect/TargetSelecter.java | 8 ++- .../unicopia/client/gui/UHud.java | 15 ++++++ .../render/spell/DarkVortexSpellRenderer.java | 50 +++++++++++------- .../unicopia/entity/player/PlayerCamera.java | 2 +- .../spells/dark_vortex/accretion_disk.png | Bin 0 -> 55384 bytes 6 files changed, 76 insertions(+), 36 deletions(-) create mode 100644 src/main/resources/assets/unicopia/textures/spells/dark_vortex/accretion_disk.png diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index c8fb3a6d..7c05d439 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; +import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; @@ -47,7 +48,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega private static final Vec3d SPHERE_OFFSET = new Vec3d(0, 2, 0); - private int age = 0; private float accumulatedMass = 0; protected DarkVortexSpell(CustomisedSpellType type) { @@ -79,7 +79,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega return true; } - if (++age % 20 == 0) { + if (source.asEntity().age % 20 == 0) { source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1); } @@ -112,16 +112,18 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega public void generateParticles(Caster source) { super.generateParticles(source); - if (getEventHorizonRadius() > 3) { + if (getEventHorizonRadius() > 0.3) { double range = getDrawDropOffRange(source); Vec3d origin = getOrigin(source); - source.spawnParticles(origin, new Sphere(false, range), 17, p -> { - source.addParticle( - new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F) - .withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE), - p, - Vec3d.ZERO - ); + source.spawnParticles(origin, new Sphere(false, range), 1, p -> { + if (!source.asWorld().isAir(BlockPos.ofFloored(p))) { + source.addParticle( + new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F) + .withChild(ParticleTypes.CAMPFIRE_SIGNAL_SMOKE), + p, + Vec3d.ZERO + ); + } }); } } @@ -181,8 +183,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega } private double getMass() { - float pulse = (float)Math.sin(age * 8) / 160F; - return Math.min(15, Math.min(0.5F, (float)Math.exp(age) / 8F - 90) + accumulatedMass / 10F) + pulse; + return Math.min(15, 0.1F + accumulatedMass / 10F); } @Override @@ -194,6 +195,11 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega if (distance <= getEventHorizonRadius() + 0.5) { target.setVelocity(target.getVelocity().multiply(distance / (2 * radius))); + if (distance < 1) { + target.setVelocity(target.getVelocity().multiply(distance)); + + } + Living.updateVelocity(target); @Nullable Entity master = source.getMaster(); @@ -213,7 +219,7 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega double massOfTarget = AttractionUtils.getMass(target); - if (massOfTarget != 0) { + if (!source.isClient() && massOfTarget != 0) { accumulatedMass += massOfTarget; setDirty(); } @@ -223,6 +229,9 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega target.discard(); source.asWorld().playSound(null, source.getOrigin(), USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.AMBIENT, 2, 0.02F); } + if (target.isAlive()) { + target.damage(source.asEntity().getDamageSources().outOfWorld(), Integer.MAX_VALUE); + } source.subtractEnergyCost(-massOfTarget * 10); source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F); @@ -238,14 +247,12 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); - compound.putInt("age", age); compound.putFloat("accumulatedMass", accumulatedMass); } @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); - age = compound.getInt("age"); accumulatedMass = compound.getFloat("accumulatedMass"); } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java index b2482157..d1730e78 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java @@ -10,7 +10,6 @@ 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; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.item.FriendshipBraceletItem; @@ -29,7 +28,7 @@ public class TargetSelecter { public Stream getEntities(Caster source, double radius, BiPredicate, Entity> filter) { targets.values().removeIf(Target::tick); return source.findAllEntitiesInRange(radius) - .filter(entity -> entity.isAlive() && !entity.isRemoved() && notOwnerOrFriend(spell, source, entity) && !SpellPredicate.IS_SHIELD_LIKE.isOn(entity)) + .filter(entity -> entity.isAlive() && !entity.isRemoved() && notOwnerOrFriend(spell, source, entity)) .filter(EquinePredicates.EXCEPT_MAGIC_IMMUNE) .filter(e -> filter.test(source, e)) .map(i -> { @@ -57,6 +56,11 @@ public class TargetSelecter { public static boolean isOwnerOrFriend(Affine affine, Caster source, Entity target) { Entity owner = source.getMaster(); + var equine = Pony.of(target); + if (equine.isPresent() && !affine.isFriendlyTogether(equine.get())) { + return false; + } + if (affine.isEnemy(source)) { return FriendshipBraceletItem.isComrade(source, target); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java b/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java index 8cde15c2..c731c559 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java @@ -7,6 +7,8 @@ import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.ability.*; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.spell.DarkVortexSpellRenderer; import com.minelittlepony.unicopia.client.sound.*; import com.minelittlepony.unicopia.entity.ItemTracker; import com.minelittlepony.unicopia.entity.effect.EffectUtils; @@ -193,6 +195,19 @@ public class UHud { protected void renderViewEffects(Pony pony, DrawContext context, int scaledWidth, int scaledHeight, float tickDelta) { + float vortexDistortion = DarkVortexSpellRenderer.getCameraDistortion(); + + if (vortexDistortion > 20) { + context.fill(RenderLayers.getEndPortal(), 0, 0, scaledWidth, scaledHeight, 0); + context.getMatrices().push(); + context.getMatrices().translate(scaledWidth / 2, scaledHeight / 2, 0); + DrawableUtil.drawArc(context.getMatrices(), 0, 20, 0, MathHelper.TAU, 0x000000FF, false); + context.getMatrices().pop(); + return; + } else if (vortexDistortion > 0) { + context.fill(0, 0, scaledWidth, scaledHeight, (int)((vortexDistortion / 20F) * 255) << 24); + } + boolean hasEffect = client.player.hasStatusEffect(UEffects.SUN_BLINDNESS); ItemStack glasses = GlassesItem.getForEntity(client.player); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java index 0389cfca..548854ae 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.client.render.spell; +import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.DarkVortexSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; @@ -17,7 +18,7 @@ import net.minecraft.util.math.RotationAxis; public class DarkVortexSpellRenderer implements SpellRenderer { - private static final Identifier ECRETION_RING_TEXTURE = new Identifier("textures/misc/forcefield.png"); + private static final Identifier ACCRETION_DISK_TEXTURE = Unicopia.id("textures/spells/dark_vortex/accretion_disk.png"); private static float cameraDistortion; @@ -29,16 +30,17 @@ public class DarkVortexSpellRenderer implements SpellRenderer { @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, DarkVortexSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { - matrices.push(); - double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; - matrices.translate(0, height + 2, 0); + Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity(); float radius = (float)spell.getEventHorizonRadius(); float absDistance = (float)cameraEntity.getEyePos().distanceTo(caster.getOriginVector().add(0, 2, 0)); - SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, Math.min(radius, absDistance / 2F), 0, 0, 0, 1); + matrices.push(); + matrices.translate(0, 2 + radius, 0); + + SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, Math.min(radius * 0.6F, absDistance * 0.1F), 0, 0, 0, 1); matrices.push(); @@ -46,6 +48,7 @@ public class DarkVortexSpellRenderer implements SpellRenderer { matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90)); matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90 + cameraEntity.getYaw(tickDelta))); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta))); + matrices.scale(0.7F, 1, 1); @@ -57,30 +60,41 @@ public class DarkVortexSpellRenderer implements SpellRenderer { matrices.scale(distance, distance, distance); - matrices.push(); - for (int i = 0; i < 10; i++) { - matrices.scale(0.96F, 1, 1); - float brightness = i / 10F; - SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicNoColor()), light, 1, radius * (1 + (0.25F * i)), brightness, brightness, brightness, 0.2F); + if (absDistance > radius) { + matrices.push(); + matrices.translate(0, -0.1F, 0); + for (int i = 0; i < 10; i++) { + matrices.scale(1, 1, 0.796F); + float brightness = i / 10F; + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicNoColor()), light, 1, radius * (1 + (0.25F * i)) * 0.7F, brightness, brightness, brightness, 0.2F); + } + matrices.pop(); } - matrices.pop(); SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getEndPortal()), light, 1, radius * 0.5F, 1, 0.5F, 0, 1); - matrices.pop(); - - if (radius > 1 && absDistance > radius) { - radius *= 1.1F; + if (radius > 0.3F && absDistance > radius) { + radius *= 3 + radius; matrices.scale(radius, radius, radius); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(animationProgress)); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(45)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 168)); - VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(ECRETION_RING_TEXTURE)); + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(ACCRETION_DISK_TEXTURE)); PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); + + matrices.push(); + matrices.scale(0.5F, 0.5F, 0.5F); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(33)); + + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); + matrices.pop(); + + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(45)); + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); } matrices.pop(); + matrices.pop(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java index 2a2a8449..26c6d4c7 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCamera.java @@ -63,7 +63,7 @@ public class PlayerCamera extends MotionCompositor { public double calculateFieldOfView(double fov) { fov += player.getMagicalReserves().getExertion().get() / 5F; fov += getEnergyAddition(); - fov += DarkVortexSpellRenderer.getCameraDistortion(); + fov += DarkVortexSpellRenderer.getCameraDistortion() * 2.5F; return fov; } diff --git a/src/main/resources/assets/unicopia/textures/spells/dark_vortex/accretion_disk.png b/src/main/resources/assets/unicopia/textures/spells/dark_vortex/accretion_disk.png new file mode 100644 index 0000000000000000000000000000000000000000..d1ffe0c5253680da875ca764aefae2f0e76c788e GIT binary patch literal 55384 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 UR1Y95jsoCy*~AJ}e$gZTfAvZM4gdfE literal 0 HcmV?d00001 From edb1e024d631fca15fee8490a028e3024419e8b7 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 17:23:11 +0000 Subject: [PATCH 58/98] Fix portals and move them to a spell renderer --- .../magic/spell/effect/PortalSpell.java | 17 ++-- .../unicopia/client/URenderers.java | 1 + .../render/spell/BubbleSpellRenderer.java | 4 +- .../render/spell/DarkVortexSpellRenderer.java | 18 ++-- .../render/spell/PlacedSpellRenderer.java | 87 +++++++++++-------- .../render/spell/PortalSpellRenderer.java | 51 +++++++++++ .../render/spell/ShieldSpellRenderer.java | 4 +- .../spell/SpellEffectsRenderDispatcher.java | 50 +---------- .../client/render/spell/SpellRenderer.java | 66 +++++++++++++- .../unicopia/entity/mob/UEntities.java | 2 +- 10 files changed, 197 insertions(+), 103 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index e55f388c..9eb227ca 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -42,8 +42,6 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme private boolean publishedPosition; - private final ParticleHandle particleEffect = new ParticleHandle(); - private float pitch; private float yaw; @@ -53,6 +51,10 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme super(type); } + public boolean isLinked() { + return teleportationTarget.getTarget().isPresent(); + } + @Override public boolean apply(Caster caster) { setOrientation(caster.asEntity().getPitch(), caster.asEntity().getYaw()); @@ -73,17 +75,9 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme source.spawnParticles(origin, particleArea, 5, pos -> { source.addParticle(effect, pos, Vec3d.ZERO); }); - - teleportationTarget.getTarget().ifPresentOrElse(target -> { - particleEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(new SphereParticleEffect(UParticles.DISK, getType().getColor(), 0.8F, 1.8F, new Vec3d(-pitch + 90, -yaw, 0)), source.getOriginVector(), Vec3d.ZERO); - }); - }, () -> { - particleEffect.destroy(); - }); } else { teleportationTarget.getTarget().ifPresent(target -> { - if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) != null) { + if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) == null) { Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid()); teleportationTarget.set(null); setDirty(); @@ -178,7 +172,6 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme @Override protected void onDestroyed(Caster caster) { - particleEffect.destroy(); Ether ether = Ether.get(caster.asWorld()); ether.remove(getType(), caster); getTarget(caster).ifPresent(e -> e.setTaken(false)); diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 1d91a1d5..dad19f19 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -63,6 +63,7 @@ import net.minecraft.world.BlockRenderView; public interface URenderers { BlockEntity CHEST_RENDER_ENTITY = new CloudChestBlock.TileData(BlockPos.ORIGIN, UBlocks.CLOUD_CHEST.getDefaultState()); + @SuppressWarnings("unchecked") static void bootstrap() { ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::new)); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java index d15863b7..48f0dbb9 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/BubbleSpellRenderer.java @@ -13,9 +13,11 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.Entity; import net.minecraft.util.math.RotationAxis; -public class BubbleSpellRenderer implements SpellRenderer { +public class BubbleSpellRenderer extends SpellRenderer { @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, BubbleSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + matrices.push(); double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; matrices.translate(0, height * 0.5F, 0); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java index 548854ae..2c03bc11 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/DarkVortexSpellRenderer.java @@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client.render.spell; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell; import com.minelittlepony.unicopia.ability.magic.spell.effect.DarkVortexSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.PlaneModel; @@ -16,7 +17,7 @@ import net.minecraft.util.Identifier; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; -public class DarkVortexSpellRenderer implements SpellRenderer { +public class DarkVortexSpellRenderer extends SpellRenderer { private static final Identifier ACCRETION_DISK_TEXTURE = Unicopia.id("textures/spells/dark_vortex/accretion_disk.png"); @@ -29,8 +30,13 @@ public class DarkVortexSpellRenderer implements SpellRenderer { } @Override - public void render(MatrixStack matrices, VertexConsumerProvider vertices, DarkVortexSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + public boolean shouldRenderEffectPass(int pass) { + return pass < 2; + } + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, DarkVortexSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity(); @@ -43,15 +49,12 @@ public class DarkVortexSpellRenderer implements SpellRenderer { SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, Math.min(radius * 0.6F, absDistance * 0.1F), 0, 0, 0, 1); matrices.push(); - - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90)); matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90 + cameraEntity.getYaw(tickDelta))); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-cameraEntity.getPitch(tickDelta))); matrices.scale(0.7F, 1, 1); - float distance = 1F / MathHelper.clamp((absDistance / (radius * 4)), 0.0000001F, 1); distance *= distance; if (absDistance < radius * 4) { @@ -97,4 +100,9 @@ public class DarkVortexSpellRenderer implements SpellRenderer { matrices.pop(); matrices.pop(); } + + @Override + protected void renderCountdown(MatrixStack matrices, TimedSpell spell, float tickDelta) { + + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java index 4630912b..c833c5fd 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -15,7 +15,7 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; import net.minecraft.util.math.RotationAxis; -public class PlacedSpellRenderer implements SpellRenderer { +public class PlacedSpellRenderer extends SpellRenderer { private static final Identifier[] TEXTURES = new Identifier[] { Unicopia.id("textures/particles/runes_0.png"), Unicopia.id("textures/particles/runes_1.png"), @@ -27,46 +27,65 @@ public class PlacedSpellRenderer implements SpellRenderer { @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { - - if (!(caster.asEntity() instanceof CastSpellEntity)) { + if (!(caster.asEntity() instanceof CastSpellEntity castSpell)) { return; } + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-castSpell.getYaw())); + for (Spell delegate : spell.getDelegates()) { + renderAmbientEffects(matrices, vertices, spell, delegate, caster, light, animationProgress, tickDelta); matrices.push(); - matrices.translate(0, 0.001, 0); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(spell.pitch)); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90 - spell.yaw)); - float scale = (spell.getAge(tickDelta) / 25F) * 3; - matrices.scale(scale, scale, scale); - - float angle = (animationProgress / 9F) % 360; - - int color = delegate.getType().getColor(); - - float red = Color.r(color); - float green = Color.g(color); - float blue = Color.b(color); - - for (int i = 0; i < TEXTURES.length; i++) { - VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(TEXTURES[i])); - - for (int dim = 0; dim < 3; dim++) { - float ringSpeed = (i % 2 == 0 ? i : -1) * i; - - matrices.push(); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed)); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim)); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim)); - PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, red, green, blue, scale / ((float)(dim * 3) + 1)); - matrices.pop(); - } - } - - matrices.pop(); - + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-spell.pitch)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw)); SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + matrices.pop(); } + + matrices.pop(); + } + + protected void renderAmbientEffects(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Spell delegate, Caster caster, int light, float animationProgress, float tickDelta) { + matrices.push(); + matrices.translate(0, 0.001, 0); + + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-spell.pitch)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); + + float scale = (spell.getAge(tickDelta) / 25F) * 3; + matrices.scale(scale, scale, scale); + + float angle = (animationProgress / 9F) % 360; + + int color = delegate.getType().getColor(); + + float red = Color.r(color); + float green = Color.g(color); + float blue = Color.b(color); + + SpellRenderer renderer = SpellEffectsRenderDispatcher.INSTANCE.getRenderer(delegate); + + for (int i = 0; i < TEXTURES.length; i++) { + if (!renderer.shouldRenderEffectPass(i)) { + continue; + } + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(TEXTURES[i])); + + for (int dim = 0; dim < 3; dim++) { + float ringSpeed = (i % 2 == 0 ? i : -1) * i; + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle * ringSpeed)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angle * ringSpeed * dim)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle * ringSpeed * dim)); + PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, red, green, blue, scale / ((float)(dim * 3) + 1)); + matrices.pop(); + } + } + + matrices.pop(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java new file mode 100644 index 00000000..4199c38d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java @@ -0,0 +1,51 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.SphereModel; + +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.RotationAxis; + +public class PortalSpellRenderer extends SpellRenderer { + + @Override + public boolean shouldRenderEffectPass(int pass) { + return pass == 0; + } + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, PortalSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + + if (!spell.isLinked()) { + return; + } + + int color = spell.getType().getColor(); + + float red = Color.r(color); + float green = Color.g(color); + float blue = Color.b(color); + + VertexConsumer buffer = vertices.getBuffer(RenderLayers.getEndGateway()); + + double thickness = 0.1; + + matrices.push(); + matrices.translate(0, thickness, 0); + SphereModel.DISK.render(matrices, buffer, light, 0, 2.5F, red, green, blue, 1); + matrices.pop(); + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); + matrices.translate(0, thickness, 0); + SphereModel.DISK.render(matrices, buffer, light, 0, 2.5F, red, green, blue, 1); + + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java index 55844dd9..8ce41089 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java @@ -12,9 +12,11 @@ import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.MathHelper; -public class ShieldSpellRenderer implements SpellRenderer { +public class ShieldSpellRenderer extends SpellRenderer { @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, ShieldSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + matrices.push(); double height = caster.asEntity().getEyeY() - caster.getOriginVector().y; matrices.translate(0, height, 0); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index 341abe8a..04a7363e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -8,19 +8,12 @@ import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; -import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; -import com.minelittlepony.unicopia.client.gui.DrawableUtil; import com.minelittlepony.unicopia.entity.Living; -import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; -import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; -import com.minelittlepony.unicopia.util.ColorHelper; - import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer.TextLayerType; @@ -28,7 +21,6 @@ import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.WorldRenderer; -import net.minecraft.client.render.model.json.ModelTransformationMode; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.registry.Registries; import net.minecraft.resource.ResourceManager; @@ -38,7 +30,6 @@ import net.minecraft.util.Colors; import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; import net.minecraft.util.math.Box; -import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.Vec3d; @@ -56,6 +47,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader register(SpellType.SHIELD, ShieldSpellRenderer::new); register(SpellType.DARK_VORTEX, DarkVortexSpellRenderer::new); register(SpellType.BUBBLE, BubbleSpellRenderer::new); + register(SpellType.PORTAL, PortalSpellRenderer::new); } @Nullable @@ -71,20 +63,16 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader @SuppressWarnings("unchecked") public SpellRenderer getRenderer(S spell) { - return (SpellRenderer)renderers.get(spell.getType()); + return (SpellRenderer)renderers.getOrDefault(spell.getType(), SpellRenderer.DEFAULT); } public void render(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { var renderer = getRenderer(spell); - if (renderer != null) { + if (renderer != SpellRenderer.DEFAULT) { client.getBufferBuilders().getEntityVertexConsumers().draw(); renderer.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); - - if (EquinePredicates.IS_CASTER.test(client.player)) { - renderGemstone(matrices, vertices, spell, caster, light, tickDelta, animationProgress); - } } } @@ -105,38 +93,6 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader renderers = REGISTRY.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().create())); } - private void renderGemstone(MatrixStack matrices, VertexConsumerProvider vertices, Spell spell, Caster caster, int light, float tickDelta, float animationProgress) { - matrices.push(); - - if (!(caster.asEntity() instanceof MagicProjectileEntity)) { - - float y = -caster.asEntity().getHeight(); - if (caster.asEntity() instanceof CastSpellEntity) { - y = 1F; - } - - matrices.translate(0, y + MathHelper.sin(animationProgress / 3F) * 0.2F, 0); - matrices.push(); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(animationProgress)); - - client.getItemRenderer().renderItem(spell.getType().withTraits(spell.getTraits()).getDefaultStack(), ModelTransformationMode.FIXED, light, 0, matrices, vertices, caster.asWorld(), 0); - matrices.pop(); - - if (spell instanceof TimedSpell timed && spell.getType() != SpellType.DARK_VORTEX) { - matrices.multiply(client.getEntityRenderDispatcher().getRotation().invert()); - float radius = 0.6F; - float timeRemaining = timed.getTimer().getPercentTimeRemaining(tickDelta); - - DrawableUtil.drawArc(matrices, radius, radius + 0.3F, 0, DrawableUtil.TAU * timeRemaining, - ColorHelper.lerp(MathHelper.clamp(timeRemaining * 4, 0, 1), 0xFF0000FF, 0xFFFFFFFF), - false - ); - } - } - - matrices.pop(); - } - private void renderSpellDebugInfo(MatrixStack matrices, VertexConsumerProvider vertices, Caster caster, int light) { matrices.push(); matrices.multiply(client.getEntityRenderDispatcher().getRotation()); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellRenderer.java index 8c7e6466..cdaf9f79 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellRenderer.java @@ -1,11 +1,73 @@ package com.minelittlepony.unicopia.client.render.spell; +import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell; +import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; +import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; +import com.minelittlepony.unicopia.util.ColorHelper; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.model.json.ModelTransformationMode; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; -public interface SpellRenderer { - void render(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch); +public class SpellRenderer { + public static final SpellRenderer DEFAULT = new SpellRenderer<>(); + + protected final MinecraftClient client = MinecraftClient.getInstance(); + + public boolean shouldRenderEffectPass(int pass) { + return true; + } + + public void render(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + if (caster.asEntity() == client.cameraEntity || (caster.asEntity() instanceof MagicProjectileEntity)) { + return; + } + + if (EquinePredicates.IS_CASTER.test(client.player)) { + renderGemstone(matrices, vertices, spell, caster, light, tickDelta, animationProgress); + } + } + + private void renderGemstone(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster caster, int light, float tickDelta, float animationProgress) { + matrices.push(); + + transformGemstone(matrices, vertices, spell, caster, animationProgress); + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(animationProgress)); + + client.getItemRenderer().renderItem(spell.getType().withTraits(spell.getTraits()).getDefaultStack(), ModelTransformationMode.FIXED, light, 0, matrices, vertices, caster.asWorld(), 0); + matrices.pop(); + + if (spell instanceof TimedSpell timed) { + renderCountdown(matrices, timed, tickDelta); + } + + matrices.pop(); + } + + protected void renderCountdown(MatrixStack matrices, TimedSpell spell, float tickDelta) { + matrices.multiply(client.getEntityRenderDispatcher().getRotation().invert()); + float radius = 0.6F; + float timeRemaining = spell.getTimer().getPercentTimeRemaining(tickDelta); + + DrawableUtil.drawArc(matrices, radius, radius + 0.3F, 0, DrawableUtil.TAU * timeRemaining, + ColorHelper.lerp(MathHelper.clamp(timeRemaining * 4, 0, 1), 0xFF0000FF, 0xFFFFFFFF), + false + ); + } + + protected void transformGemstone(MatrixStack matrices, VertexConsumerProvider vertices, T spell, Caster caster, float animationProgress) { + float y = -caster.asEntity().getHeight(); + if (caster.asEntity() instanceof CastSpellEntity) { + y = 1F; + } + matrices.translate(0, y + MathHelper.sin(animationProgress / 3F) * 0.2F, 0); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java index 49904028..0aa7e069 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java @@ -41,7 +41,7 @@ public interface UEntities { .dimensions(EntityDimensions.fixed(1, 1))); EntityType CAST_SPELL = register("cast_spell", FabricEntityTypeBuilder.create(SpawnGroup.MISC, CastSpellEntity::new) .trackRangeBlocks(200) - .dimensions(EntityDimensions.fixed(1, 0.4F))); + .dimensions(EntityDimensions.fixed(1, 1))); EntityType TWITTERMITE = register("twittermite", FabricEntityTypeBuilder.create(SpawnGroup.MISC, FairyEntity::new) .trackRangeBlocks(200) .dimensions(EntityDimensions.fixed(0.1F, 0.1F))); From eb1b767319d7962de053456425caf49d15aa86ac Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 22 Jan 2024 23:28:03 +0000 Subject: [PATCH 59/98] Clean up deprecated particle handling --- .../unicopia/InteractionManager.java | 6 + .../ability/PegasusRainboomAbility.java | 5 - .../magic/spell/RainboomAbilitySpell.java | 32 ++--- .../client/ClientInteractionManager.java | 10 +- .../AbstractGeometryBasedParticle.java | 12 ++ .../particle/ClientBoundParticleSpawner.java | 57 ++++++++ .../client/particle/RainbowTrailParticle.java | 126 ++++++------------ .../client/particle/RunesParticle.java | 68 +--------- .../client/particle/SphereParticle.java | 74 +--------- .../client/render/bezier/BezierSegment.java | 16 ++- .../unicopia/client/render/bezier/Ribbon.java | 49 ------- .../unicopia/client/render/bezier/Trail.java | 67 ++++++++++ .../render/spell/RainboomSpellRenderer.java | 13 ++ .../spell/SpellEffectsRenderDispatcher.java | 1 + .../unicopia/particle/ParticleHandle.java | 120 ----------------- .../unicopia/particle/ParticleSpawner.java | 2 + .../particle/TargetBoundParticleEffect.java | 55 ++++++++ .../unicopia/particle/UParticles.java | 2 +- 18 files changed, 292 insertions(+), 423 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/particle/ClientBoundParticleSpawner.java delete mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/spell/RainboomSpellRenderer.java delete mode 100644 src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java create mode 100644 src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java diff --git a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java index 05a101d2..736cf4a3 100644 --- a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java @@ -1,11 +1,13 @@ package com.minelittlepony.unicopia; import java.util.Map; +import java.util.UUID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity; +import com.minelittlepony.unicopia.particle.ParticleSpawner; import com.mojang.authlib.GameProfile; import net.minecraft.entity.Entity; @@ -32,6 +34,10 @@ public class InteractionManager { return INSTANCE; } + public ParticleSpawner createBoundParticle(UUID id) { + return ParticleSpawner.EMPTY; + } + public Map readChapters(PacketByteBuf buf) { throw new RuntimeException("Method not supported"); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java index 087e1cb0..bbe7256e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java @@ -10,10 +10,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.MagicParticleEffect; -import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; -import com.minelittlepony.unicopia.particle.UParticles; - -import net.minecraft.util.math.Vec3d; /** * Pegasus ability to perform rainbooms @@ -72,7 +68,6 @@ public class PegasusRainboomAbility implements Ability { } if (player.consumeSuperMove()) { - player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO); SpellType.RAINBOOM.withTraits().apply(player, CastingMethod.INNATE); } return true; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java index d7b7b562..6c6d1099 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java @@ -1,12 +1,16 @@ package com.minelittlepony.unicopia.ability.magic.spell; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.*; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.particle.ParticleHandle; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; +import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; +import com.minelittlepony.unicopia.particle.ParticleSpawner; +import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect; import com.minelittlepony.unicopia.server.world.ModificationType; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.util.shape.Shape; @@ -26,7 +30,8 @@ public class RainboomAbilitySpell extends AbstractSpell { private static final int RADIUS = 5; private static final Shape EFFECT_RANGE = new Sphere(false, RADIUS); - private final ParticleHandle particlEffect = new ParticleHandle(); + @Nullable + private ParticleSpawner boundParticle; private int age; @@ -35,11 +40,6 @@ public class RainboomAbilitySpell extends AbstractSpell { setHidden(true); } - @Override - protected void onDestroyed(Caster source) { - particlEffect.destroy(); - } - @Override public boolean tick(Caster source, Situation situation) { @@ -47,14 +47,15 @@ public class RainboomAbilitySpell extends AbstractSpell { return false; } - particlEffect.update(getUuid(), source, spawner -> { - spawner.addParticle(UParticles.RAINBOOM_TRAIL, source.getOriginVector(), Vec3d.ZERO); - }).ifPresent(attachment -> { - attachment.setAttribute(Attachment.ATTR_BOUND, 1); - }); - if (source.isClient()) { - //source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO); + if (boundParticle == null) { + boundParticle = InteractionManager.INSTANCE.createBoundParticle(getUuid()); + } + boundParticle.addParticle(new TargetBoundParticleEffect(UParticles.RAINBOOM_TRAIL, source.asEntity()), source.getOriginVector(), Vec3d.ZERO); + + if (age == 0) { + source.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO); + } } source.findAllEntitiesInRange(RADIUS).forEach(e -> { @@ -92,5 +93,6 @@ public class RainboomAbilitySpell extends AbstractSpell { public void fromNBT(NbtCompound compound) { super.fromNBT(compound); age = compound.getInt("age"); + boundParticle = null; } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java index 68cf31a5..3a4105b9 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java @@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client; import java.lang.ref.WeakReference; import java.util.Map; +import java.util.UUID; import java.util.function.Predicate; import java.util.function.Supplier; @@ -14,10 +15,12 @@ import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.client.gui.DismissSpellScreen; import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters; +import com.minelittlepony.unicopia.client.particle.ClientBoundParticleSpawner; import com.minelittlepony.unicopia.client.sound.*; import com.minelittlepony.unicopia.entity.player.PlayerPhysics; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity; +import com.minelittlepony.unicopia.particle.ParticleSpawner; import com.mojang.authlib.GameProfile; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; @@ -47,7 +50,7 @@ public class ClientInteractionManager extends InteractionManager { @Override public Map readChapters(PacketByteBuf buffer) { - return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter); + return buffer.readMap(PacketByteBuf::readIdentifier, ClientChapters::loadChapter); } @Override @@ -144,4 +147,9 @@ public class ClientInteractionManager extends InteractionManager { public int getViewMode() { return client.options.getPerspective().ordinal(); } + + @Override + public ParticleSpawner createBoundParticle(UUID id) { + return new ClientBoundParticleSpawner(id); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java index fffcade8..297c2e65 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java @@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.client.particle; import org.joml.Vector3f; +import com.minelittlepony.unicopia.client.render.RenderUtil; + import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleTextureSheet; import net.minecraft.client.render.BufferBuilder; @@ -36,6 +38,16 @@ public abstract class AbstractGeometryBasedParticle extends Particle { te.draw(); } + protected final void renderQuad(Tessellator te, BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta) { + int light = getBrightness(tickDelta); + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); + for (RenderUtil.Vertex corner : corners) { + buffer.vertex(corner.position().x, corner.position().y, corner.position().z).texture(corner.u(), corner.v()).color(red, green, blue, alpha).light(light).next(); + } + te.draw(); + } + + protected final void renderQuad(VertexConsumer buffer, Vector3f[] corners, float alpha, float tickDelta) { int light = getBrightness(tickDelta); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/ClientBoundParticleSpawner.java b/src/main/java/com/minelittlepony/unicopia/client/particle/ClientBoundParticleSpawner.java new file mode 100644 index 00000000..4fdc8e3a --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/ClientBoundParticleSpawner.java @@ -0,0 +1,57 @@ +package com.minelittlepony.unicopia.client.particle; + +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.function.BooleanSupplier; + +import com.minelittlepony.unicopia.particle.ParticleSpawner; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.particle.Particle; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +/** + * A connection class for updating and persisting an attached particle effect. + */ +public class ClientBoundParticleSpawner implements ParticleSpawner { + private static final Map SPAWNED_PARTICLES = new HashMap<>(); + + private final UUID id; + private WeakReference attachment = new WeakReference<>(null); + + private final MinecraftClient client = MinecraftClient.getInstance(); + + public ClientBoundParticleSpawner(UUID id) { + this.id = id; + } + + @Override + public void addParticle(ParticleEffect effect, Vec3d pos, Vec3d vel) { + BooleanSupplier a = attachment.get(); + if ((a == null || !a.getAsBoolean())) { + SPAWNED_PARTICLES.values().removeIf(set -> !set.getAsBoolean()); + attachment = new WeakReference<>(SPAWNED_PARTICLES.computeIfAbsent(id, i -> { + return new Entry( + new WeakReference<>(client.world), + new WeakReference<>(client.particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z)) + ); + })); + } + } + + private record Entry (WeakReference world, WeakReference particle) implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + if (world.get() == null || world.get() != MinecraftClient.getInstance().world) { + return false; + } + + Particle particle = this.particle.get(); + return particle != null && particle.isAlive(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java index 6093fa45..978fede1 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java @@ -1,39 +1,46 @@ package com.minelittlepony.unicopia.client.particle; -import java.util.ArrayList; import java.util.List; -import java.util.Optional; +import org.jetbrains.annotations.Nullable; -import org.joml.Vector3f; - -import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.client.render.bezier.BezierSegment; -import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; -import com.minelittlepony.unicopia.particle.ParticleHandle.Link; +import com.minelittlepony.unicopia.client.render.bezier.Trail; +import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Tessellator; import net.minecraft.client.world.ClientWorld; -import net.minecraft.particle.DefaultParticleType; +import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; +import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; -public class RainbowTrailParticle extends AbstractBillboardParticle implements Attachment { +public class RainbowTrailParticle extends AbstractBillboardParticle { private static final Identifier TEXTURE = Unicopia.id("textures/particles/rainboom_trail.png"); - private final List segments = new ArrayList<>(); + private final Trail trail; - private Optional link = Optional.empty(); + @Nullable + private Entity target; + private boolean isAbility; - private boolean bound; - - public RainbowTrailParticle(DefaultParticleType effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { + public RainbowTrailParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { super(world, x, y, z, velocityX, velocityY, velocityZ); - segments.add(new Segment(new Vec3d(x, y, z))); + trail = new Trail(new Vec3d(x, y, z)); setMaxAge(300); + this.velocityX = velocityX; + this.velocityY = velocityY; + this.velocityZ = velocityZ; + + if (effect.getTargetId() <= 0) { + this.target = world.getOtherEntities(null, Box.from(trail.pos)).get(0); + } else { + this.target = world.getEntityById(effect.getTargetId()); + } + isAbility = Caster.of(target).filter(caster -> SpellType.RAINBOOM.isOn(caster)).isPresent(); } @Override @@ -42,98 +49,45 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A } @Override - public boolean isStillAlive() { - return age < getMaxAge() && (!dead || !segments.isEmpty()); - } - - @Override - public void attach(Link link) { - this.link = Optional.of(link); - bound = true; - } - - @Override - public void detach() { - link = Optional.empty(); - } - - @Override - public void setAttribute(int key, Number value) { - + public boolean isAlive() { + return age < getMaxAge() && (!dead || !trail.getSegments().isEmpty()); } @Override protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) { - float alpha = 1 - (float)age / maxAge; + float alpha = this.alpha * (1 - (float)age / maxAge); + + List segments = trail.getSegments(); for (int i = 0; i < segments.size() - 1; i++) { BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1)); float scale = getScale(tickDelta); corners.forEachCorner(corner -> { - corner.mul(scale); - corner.add(x, y, z); + corner.position().mul(scale).add(x, y, z); }); renderQuad(te, buffer, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta); } } - private void follow(EntityConvertable caster) { - Vec3d next = caster.asEntity().getPos(); - - if (segments.isEmpty()) { - segments.add(new Segment(next)); - } else { - Vec3d last = segments.get(segments.size() - 1).position; - if (next.distanceTo(last) > 0.2) { - segments.add(new Segment(next)); - } - } - } - @Override public void tick() { super.tick(); - if (link.isPresent()) { - age = 0; - link.flatMap(Link::get).ifPresent(this::follow); - } else if (!dead && !bound) { - follow(Pony.of(MinecraftClient.getInstance().player)); + if (target != null && target.isAlive()) { + if (isAbility) { + age = 0; + } + trail.update(target.getEyePos()); + + if (isAbility && Caster.of(target).filter(caster -> SpellType.RAINBOOM.isOn(caster)).isEmpty()) { + target = null; + } } - if (segments.size() > 1) { - segments.removeIf(Segment::tick); - } - if (segments.isEmpty()) { + if (trail.tick()) { markDead(); } } - - private final class Segment { - Vec3d position; - Vector3f offset; - - int age; - int maxAge; - - Segment(Vec3d position) { - this.position = position; - this.offset = new Vector3f((float)(position.getX() - x), (float)(position.getY() - y), (float)(position.getZ() - z)); - this.maxAge = 90; - } - - float getAlpha() { - return alpha * (1 - ((float)age / maxAge)); - } - - boolean tick() { - return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge; - } - - BezierSegment getPlane(Segment to) { - return new BezierSegment(offset, to.offset, 1); - } - } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java index 87ee30c2..21294ca3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java @@ -1,27 +1,20 @@ package com.minelittlepony.unicopia.client.particle; -import java.util.Optional; - import org.joml.Quaternionf; import org.joml.Vector3f; -import com.minelittlepony.common.util.Color; -import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; -import com.minelittlepony.unicopia.particle.ParticleHandle.Link; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Tessellator; import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; import net.minecraft.util.math.*; @Deprecated -public class RunesParticle extends OrientedBillboardParticle implements Attachment { +public class RunesParticle extends OrientedBillboardParticle { private static final Identifier[] TEXTURES = new Identifier[] { Unicopia.id("textures/particles/runes_0.png"), @@ -40,10 +33,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme private float prevRotationAngle; private float rotationAngle; - private Optional link = Optional.empty(); - - private int stasisAge = -1; - public RunesParticle(OrientedBillboardParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { super(effect, world, x, y, z, velocityX, velocityY, velocityZ); setMaxAge(70); @@ -53,52 +42,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme blue = world.random.nextFloat(); } - @Override - public boolean isStillAlive() { - return age < (maxAge - 1); - } - - @Override - public void attach(Link link) { - this.link = Optional.of(link); - velocityX = 0; - velocityY = 0; - velocityZ = 0; - Vec3d pos = link.get().map(EntityConvertable::asEntity).map(Entity::getPos).orElse(Vec3d.ZERO); - setPos(pos.x, pos.y + 0.25, pos.z); - } - - @Override - public void detach() { - link = Optional.empty(); - if (targetSize > 1) { - this.targetSize = 0; - } - } - - @Override - public void setAttribute(int key, Number value) { - if (key == ATTR_COLOR) { - int tint = value.intValue(); - red = Color.r(tint); - green = Color.g(tint); - blue = Color.b(tint); - } - if (key == ATTR_OPACITY) { - alpha = value.floatValue(); - } - if (key == ATTR_RADIUS) { - targetSize = value.floatValue(); - } - if (key == ATTR_PITCH) { - rotation = new Quaternionf(0, 0, 0, 1); - rotation.mul(RotationAxis.POSITIVE_Y.rotationDegrees(value.floatValue())); - } - if (key == ATTR_YAW) { - rotation.mul(RotationAxis.POSITIVE_X.rotationDegrees(180 - value.floatValue())); - } - } - @Override public float getScale(float tickDelta) { return MathHelper.lerp(tickDelta, prevBaseSize, baseSize) * super.getScale(tickDelta); @@ -166,15 +109,6 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme public void tick() { super.tick(); - link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> { - if (getAlphaScale() >= 0.9F) { - if (stasisAge < 0) { - stasisAge = age; - } - age = stasisAge; - } - }, this::detach); - prevBaseSize = baseSize; if (baseSize < targetSize) { baseSize += 0.1F; diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java index 911b10bd..7bbb555e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java @@ -10,22 +10,13 @@ import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; - -import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; import com.minelittlepony.unicopia.particle.SphereParticleEffect; -import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; -import com.minelittlepony.unicopia.particle.ParticleHandle.Link; import com.minelittlepony.unicopia.util.ColorHelper; import com.mojang.blaze3d.systems.RenderSystem; -import java.util.Optional; - -import com.minelittlepony.common.util.Color; - -public class SphereParticle extends Particle implements Attachment { +public class SphereParticle extends Particle { protected float prevRadius; protected float radius; @@ -34,12 +25,6 @@ public class SphereParticle extends Particle implements Attachment { protected float lerpIncrement; protected float toRadius; - private Optional link = Optional.empty(); - - private final SphereParticleEffect parameters; - - private boolean bound; - public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) { this(parameters, w, x, y, z); @@ -50,7 +35,6 @@ public class SphereParticle extends Particle implements Attachment { public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z) { super(w, x, y, z); - this.parameters = parameters; this.radius = parameters.radius(); this.red = parameters.color().x / 255F; this.green = parameters.color().y / 255F; @@ -60,43 +44,6 @@ public class SphereParticle extends Particle implements Attachment { setMaxAge(10); } - @Override - public boolean isStillAlive() { - return age < (maxAge - 1); - } - - @Override - public void attach(Link link) { - setMaxAge(50000); - this.link = Optional.of(link); - } - - @Override - public void detach() { - markDead(); - } - - @Override - public void setAttribute(int key, Number value) { - if (key == ATTR_RADIUS) { - toRadius = value.floatValue(); - steps = 20; - lerpIncrement = (toRadius - radius) / steps; - } - if (key == ATTR_COLOR) { - int tint = value.intValue(); - red = Color.r(tint); - green = Color.g(tint); - blue = Color.b(tint); - } - if (key == ATTR_OPACITY) { - alpha = value.floatValue(); - } - if (key == ATTR_BOUND) { - bound = value.intValue() == 1; - } - } - @Override public ParticleTextureSheet getType() { return ParticleTextureSheet.CUSTOM; @@ -106,24 +53,7 @@ public class SphereParticle extends Particle implements Attachment { public void tick() { super.tick(); - if (link.isPresent()) { - link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> { - if (!bound) { - Vec3d offset = parameters.offset(); - setPos(e.getX() + offset.getX(), e.getY() + offset.getY(), e.getZ() + offset.getZ()); - - prevPosX = e.lastRenderX + offset.getX(); - prevPosY = e.lastRenderY + offset.getY(); - prevPosZ = e.lastRenderZ + offset.getZ(); - } - }, this::detach); - - if (steps-- > 0) { - radius += lerpIncrement; - } - } else { - radius *= 0.9998281; - } + radius *= 0.9998281; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java index f38b0182..d8ce8d35 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java @@ -4,20 +4,22 @@ import java.util.function.Consumer; import org.joml.Vector3f; +import com.minelittlepony.unicopia.client.render.RenderUtil; + public record BezierSegment( - Vector3f[] corners + RenderUtil.Vertex[] corners ) { public BezierSegment(Vector3f from, Vector3f to, float height) { - this(new Vector3f[] { - new Vector3f(from.x, from.y - height/2F, from.z), // bottom left - new Vector3f(from.x, from.y + height/2F, from.z), // top left - new Vector3f(to.x, to.y + height/2F, to.z), // top right - new Vector3f(to.x, to.y - height/2F, to.z) // bottom right + this(new RenderUtil.Vertex[] { + new RenderUtil.Vertex(new Vector3f(from.x, from.y - height/2F, from.z), 0, 0), // bottom left + new RenderUtil.Vertex(new Vector3f(from.x, from.y + height/2F, from.z), 1, 0), // top left + new RenderUtil.Vertex(new Vector3f(to.x, to.y + height/2F, to.z), 1, 1), // top right + new RenderUtil.Vertex(new Vector3f(to.x, to.y - height/2F, to.z), 0, 1) // bottom right }); } - public void forEachCorner(Consumer transformer) { + public void forEachCorner(Consumer transformer) { for (var corner : corners) { transformer.accept(corner); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java deleted file mode 100644 index 361a437a..00000000 --- a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Ribbon.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.minelittlepony.unicopia.client.render.bezier; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import org.joml.Vector3f; - -import net.minecraft.util.math.Vec3d; - -public class Ribbon implements Iterable { - public float width; - public float angle; - - private final List nodes = new ArrayList<>(); - private final List segments = new ArrayList<>(); - private Node lastNode; - - public Ribbon(Vec3d position, Vector3f bottom, Vector3f top, float angle) { - this.angle = angle; - lastNode = new Node(position, position.toVector3f().add(bottom), position.toVector3f().add(top)); - nodes.add(lastNode); - } - - public void addNode(Vec3d position, float angle) { - Vector3f directionVector = position.subtract(lastNode.position()).toVector3f(); - - Vector3f bottom = lastNode.bottom().add(directionVector).rotateAxis(angle, directionVector.x, directionVector.y, directionVector.z); - Vector3f top = lastNode.top().add(directionVector).rotateAxis(angle, directionVector.x, directionVector.y, directionVector.z); - - Node oldNode = lastNode; - lastNode = new Node(position, bottom, top); - nodes.add(lastNode); - segments.add(new BezierSegment(new Vector3f[] { - oldNode.bottom(), - oldNode.top(), - lastNode.bottom(), - lastNode.top() - })); - } - - @Override - public Iterator iterator() { - return segments.iterator(); - } - - record Node(Vec3d position, Vector3f bottom, Vector3f top) { - - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java new file mode 100644 index 00000000..f130fbcc --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java @@ -0,0 +1,67 @@ +package com.minelittlepony.unicopia.client.render.bezier; + +import java.util.ArrayList; +import java.util.List; +import org.joml.Vector3f; + +import net.minecraft.util.math.Vec3d; + +public class Trail { + + private final List segments = new ArrayList<>(); + + public final Vec3d pos; + + public Trail(Vec3d pos) { + this.pos = pos; + segments.add(new Segment(pos)); + } + + public List getSegments() { + return segments; + } + + public void update(Vec3d newPosition) { + if (segments.isEmpty()) { + segments.add(new Segment(newPosition)); + } else { + Vec3d last = segments.get(segments.size() - 1).position; + if (newPosition.distanceTo(last) > 0.2) { + segments.add(new Segment(newPosition)); + } + } + } + + public boolean tick() { + if (segments.size() > 1) { + segments.removeIf(Segment::tick); + } + return segments.isEmpty(); + } + + public final class Segment { + Vec3d position; + Vector3f offset; + + int age; + int maxAge; + + Segment(Vec3d position) { + this.position = position; + this.offset = position.subtract(pos).toVector3f(); + this.maxAge = 90; + } + + public float getAlpha() { + return (1 - ((float)age / maxAge)); + } + + boolean tick() { + return segments.indexOf(this) < segments.size() - 1 && age++ >= maxAge; + } + + public BezierSegment getPlane(Segment to) { + return new BezierSegment(offset, to.offset, 1); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/RainboomSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/RainboomSpellRenderer.java new file mode 100644 index 00000000..0f7e0b9c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/RainboomSpellRenderer.java @@ -0,0 +1,13 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.RainboomAbilitySpell; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; + +public class RainboomSpellRenderer extends SpellRenderer { + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, RainboomAbilitySpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index 04a7363e..cbfdd7dc 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -48,6 +48,7 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader register(SpellType.DARK_VORTEX, DarkVortexSpellRenderer::new); register(SpellType.BUBBLE, BubbleSpellRenderer::new); register(SpellType.PORTAL, PortalSpellRenderer::new); + register(SpellType.RAINBOOM, RainboomSpellRenderer::new); } @Nullable diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java deleted file mode 100644 index b099f347..00000000 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.minelittlepony.unicopia.particle; - -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.WeakHashMap; -import java.util.function.Consumer; - -import com.minelittlepony.unicopia.EntityConvertable; -import com.minelittlepony.unicopia.ability.magic.Caster; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.particle.Particle; -import net.minecraft.world.World; - -/** - * A connection class for updating and persisting an attached particle effect. - */ -@Deprecated -public class ParticleHandle { - private final Map loadedEffects = new WeakHashMap<>(); - - public Optional update(UUID id, ParticleSource source, Consumer constructor) { - return update(id, "prime", source, constructor); - } - - public Optional update(UUID id, String partName, ParticleSource source, Consumer constructor) { - return get(partName).or(() -> { - if (source.asEntity().getWorld().isClient) { - new ClientHandle().addParticle(id, partName, source, constructor); - } - return get(partName); - }); - } - - public void destroy() { - loadedEffects.values().forEach(Attachment::detach); - loadedEffects.clear(); - } - - private Optional get(String partName) { - return Optional.ofNullable(loadedEffects.get(partName)).filter(Attachment::isStillAlive); - } - - private final class ClientHandle { - private static final Map> SPAWNED_PARTICLES = new HashMap<>(); - - private Particle pp; - - @Environment(EnvType.CLIENT) - private void addParticle(UUID id, String partName, ParticleSource source, Consumer constructor) { - SPAWNED_PARTICLES.values().removeIf(set -> { - set.values().removeIf(particle -> particle.get() == null); - return set.isEmpty(); - }); - - Entry p = SPAWNED_PARTICLES.computeIfAbsent(id, i -> new WeakHashMap<>()).computeIfAbsent(partName, i -> { - constructor.accept((effect, pos, vel) -> { - pp = MinecraftClient.getInstance().particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z); - if (pp instanceof Attachment) { - ((Attachment) pp).attach(new Link(id, source)); - } - }); - return new Entry(new WeakReference<>(MinecraftClient.getInstance().world), new WeakReference<>(pp)); - }); - - if (p.get() instanceof Attachment) { - loadedEffects.put(partName, (Attachment)p.get()); - } - } - - record Entry (WeakReference world, WeakReference particle) { - public Particle get() { - if (world.get() == null || world.get() != MinecraftClient.getInstance().world) { - return null; - } - - Particle particle = this.particle.get(); - - return particle == null || !particle.isAlive() ? null : particle; - } - } - } - - public interface Attachment { - int ATTR_RADIUS = 0; - int ATTR_COLOR = 1; - int ATTR_OPACITY = 2; - int ATTR_PITCH = 3; - int ATTR_YAW = 4; - int ATTR_BOUND = 5; - - boolean isStillAlive(); - - void attach(Link link); - - void detach(); - - void setAttribute(int key, Number value); - } - - public static final class Link { - private Optional>> caster = Optional.empty(); - private UUID effect; - - private Link(UUID effect, EntityConvertable caster) { - this.caster = Optional.of(new WeakReference<>(caster)); - this.effect = effect; - } - - public Optional> get() { - caster = caster.filter(r -> r.get() != null && (!(r.get() instanceof Caster c) || c.getSpellSlot().contains(effect)) && r.get().asEntity().isAlive()); - return caster.map(WeakReference::get); - } - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleSpawner.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleSpawner.java index e19436e2..f1adf5a0 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleSpawner.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/ParticleSpawner.java @@ -4,5 +4,7 @@ import net.minecraft.particle.ParticleEffect; import net.minecraft.util.math.Vec3d; public interface ParticleSpawner { + ParticleSpawner EMPTY = (effect, pos, vel) -> {}; + void addParticle(ParticleEffect effect, Vec3d position, Vec3d velocity); } diff --git a/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java b/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java new file mode 100644 index 00000000..15171015 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java @@ -0,0 +1,55 @@ +package com.minelittlepony.unicopia.particle; + + +import java.util.Locale; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.entity.Entity; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.particle.ParticleType; +import net.minecraft.registry.Registries; + +public class TargetBoundParticleEffect implements ParticleEffect { + @SuppressWarnings("deprecation") + public static final Factory FACTORY = ParticleFactoryHelper.of(TargetBoundParticleEffect::new, TargetBoundParticleEffect::new); + + private final ParticleType type; + private final int targetId; + + protected TargetBoundParticleEffect(ParticleType type, StringReader reader) throws CommandSyntaxException { + this.type = type; + this.targetId = -1; + } + + protected TargetBoundParticleEffect(ParticleType type, PacketByteBuf buf) { + this.type = type; + this.targetId = buf.readInt(); + } + + public TargetBoundParticleEffect(ParticleType type, Entity target) { + this.type = type; + this.targetId = target.getId(); + } + + public int getTargetId() { + return targetId; + } + + @Override + public ParticleType getType() { + return type; + } + + @Override + public void write(PacketByteBuf buf) { + buf.writeInt(targetId); + } + + @Override + public String asString() { + return String.format(Locale.ROOT, "%s", Registries.PARTICLE_TYPE.getId(getType()), targetId); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java index f1e88314..17554070 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java @@ -15,7 +15,7 @@ public interface UParticles { DefaultParticleType BUBBLE = register("bubble", FabricParticleTypes.simple()); ParticleType RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); - DefaultParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.simple()); + ParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY)); @Deprecated ParticleType MAGIC_RUNES = register("magic_runes", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); From 56e1f5ffeb2eaaf4056cb4eefa280924d7f31213 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 24 Jan 2024 19:44:30 +0000 Subject: [PATCH 60/98] Fixed portals breaking when relogging and fixed various portal placements --- .../magic/spell/effect/PortalSpell.java | 81 ++++++++++++++++--- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index 9eb227ca..255be78e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.*; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.entity.EntityReference; +import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; import com.minelittlepony.unicopia.particle.*; import com.minelittlepony.unicopia.server.world.Ether; @@ -19,11 +20,16 @@ import com.minelittlepony.unicopia.util.shape.*; import net.minecraft.block.Block; import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.packet.s2c.play.PositionFlag; import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleTypes; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.WorldEvents; @@ -38,6 +44,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme @Nullable private UUID targetPortalId; + private float targetPortalPitch; + private float targetPortalYaw; private final EntityReference teleportationTarget = new EntityReference<>(); private boolean publishedPosition; @@ -55,6 +63,35 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme return teleportationTarget.getTarget().isPresent(); } + public Optional> getTarget() { + return teleportationTarget.getTarget(); + } + + public float getPitch() { + return pitch; + } + + public float getYaw() { + return yaw; + } + + public float getTargetPitch() { + return targetPortalPitch; + } + + public float getTargetYaw() { + return targetPortalYaw; + } + + public float getYawDifference() { + return MathHelper.wrapDegrees(180 + targetPortalYaw - yaw); + } + + @SuppressWarnings("unchecked") + private Optional> getTarget(Caster source) { + return getTarget().map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target, targetPortalId)); + } + @Override public boolean apply(Caster caster) { setOrientation(caster.asEntity().getPitch(), caster.asEntity().getYaw()); @@ -68,7 +105,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme if (source.isClient()) { Vec3d origin = source.getOriginVector(); - ParticleEffect effect = teleportationTarget.getTarget() + ParticleEffect effect = getTarget() .map(target -> (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK)) .orElse(ParticleTypes.ELECTRIC_SPARK); @@ -76,7 +113,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme source.addParticle(effect, pos, Vec3d.ZERO); }); } else { - teleportationTarget.getTarget().ifPresent(target -> { + getTarget().ifPresent(target -> { if (Ether.get(source.asWorld()).get(getType(), target, targetPortalId) == null) { Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid()); teleportationTarget.set(null); @@ -94,6 +131,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme var entry = Ether.get(source.asWorld()).getOrCreate(this, source); entry.pitch = pitch; entry.yaw = yaw; + Ether.get(source.asWorld()).markDirty(); } return !isDead(); @@ -101,24 +139,41 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme private void tickWithTargetLink(Caster source, Ether.Entry destination) { + if (!MathHelper.approximatelyEquals(targetPortalPitch, destination.pitch)) { + targetPortalPitch = destination.pitch; + setDirty(); + } + if (!MathHelper.approximatelyEquals(targetPortalYaw, destination.yaw)) { + targetPortalYaw = destination.yaw; + setDirty(); + } + destination.entity.getTarget().ifPresent(target -> { source.findAllEntitiesInRange(1).forEach(entity -> { if (!entity.hasPortalCooldown() && entity.timeUntilRegen <= 0) { Vec3d offset = entity.getPos().subtract(source.getOriginVector()); - float yawDifference = pitch < 15 ? (180 - yaw + destination.yaw) : 0; - Vec3d dest = target.pos().add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.05, 0); + float yawDifference = pitch < 15 ? getYawDifference() : 0; + Vec3d dest = target.pos().add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.1, 0); + MinecraftClient.getInstance().player.sendMessage(Text.literal(yawDifference + "")); + + if (entity.getWorld().isTopSolid(BlockPos.ofFloored(dest).up(), entity)) { + dest = dest.add(0, 1, 0); + } entity.resetPortalCooldown(); entity.timeUntilRegen = 100; - entity.setYaw(entity.getYaw() + yawDifference); + float yaw = MathHelper.wrapDegrees(entity.getYaw() + yawDifference); + entity.setVelocity(entity.getVelocity().rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)); entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1); - entity.teleport(dest.x, dest.y, dest.z); + entity.teleport((ServerWorld)entity.getWorld(), dest.x, dest.y, dest.z, PositionFlag.VALUES, yaw, entity.getPitch()); entity.getWorld().playSoundFromEntity(null, entity, USounds.ENTITY_PLAYER_UNICORN_TELEPORT, entity.getSoundCategory(), 1, 1); setDirty(); + Living.updateVelocity(entity); + if (!source.subtractEnergyCost(Math.sqrt(entity.getPos().subtract(dest).length()))) { setDead(); } @@ -145,12 +200,6 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme }); } - @SuppressWarnings("unchecked") - private Optional> getTarget(Caster source) { - return teleportationTarget.getTarget() - .map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target, targetPortalId)); - } - @Override public void setOrientation(float pitch, float yaw) { this.pitch = pitch; @@ -167,7 +216,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme LivingEntity caster = source.getMaster(); Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos()); parent.setOrientation(pitch, yaw); - entity.setPos(targetPos.x, caster.getY() + 1.5, targetPos.z); + entity.setPos(targetPos.x, caster.getEyePos().y, targetPos.z); } @Override @@ -180,15 +229,21 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); + if (targetPortalId != null) { + compound.putUuid("targetPortalId", targetPortalId); + } compound.putBoolean("publishedPosition", publishedPosition); compound.put("teleportationTarget", teleportationTarget.toNBT()); compound.putFloat("pitch", pitch); compound.putFloat("yaw", yaw); + compound.putFloat("targetPortalPitch", targetPortalPitch); + compound.putFloat("targetPortalYaw", targetPortalYaw); } @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); + targetPortalId = compound.containsUuid("targetPortalId") ? compound.getUuid("targetPortalId") : null; publishedPosition = compound.getBoolean("publishedPosition"); teleportationTarget.fromNBT(compound.getCompound("teleportationTarget")); pitch = compound.getFloat("pitch"); From fce8623f446bc93622a2ddd4f20df07741fc6db6 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 24 Jan 2024 19:44:46 +0000 Subject: [PATCH 61/98] Add svg source for the black hole's texture --- assets/accretion_disk.svg | 80 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 assets/accretion_disk.svg diff --git a/assets/accretion_disk.svg b/assets/accretion_disk.svg new file mode 100644 index 00000000..ee302045 --- /dev/null +++ b/assets/accretion_disk.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + From 8785a3f5aa5e029690474ecac44390ace1e93ce7 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 24 Jan 2024 19:44:57 +0000 Subject: [PATCH 62/98] Add portal rendering (wip) --- .../unicopia/client/render/RenderUtil.java | 13 +- .../client/render/model/SphereModel.java | 2 +- .../render/model/TexturedSphereModel.java | 41 +++ .../render/spell/PlacedSpellRenderer.java | 2 + .../render/spell/PortalSpellRenderer.java | 268 +++++++++++++++++- .../mixin/client/MixinMinecraftClient.java | 14 + .../mixin/client/MixinWorldRenderer.java | 9 +- src/main/resources/unicopia.mixin.json | 1 + 8 files changed, 336 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/model/TexturedSphereModel.java create mode 100644 src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java index fc981555..a9cbbd1e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java @@ -17,18 +17,27 @@ public class RenderUtil { new Vertex(new Vector3f(1, 1, 0), 0, 0), new Vertex(new Vector3f(1, 0, 0), 0, 1) }; + public static final Vertex[] FRAME_BUFFER_VERTICES = new Vertex[] { + new Vertex(new Vector3f(0, 1, 0), 0, 0), + new Vertex(new Vector3f(1, 1, 0), 1, 0), + new Vertex(new Vector3f(1, 0, 0), 1, 1), + new Vertex(new Vector3f(0, 0, 0), 0, 1) + }; public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) { + renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1); + } + + public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light, float uScale, float vScale) { buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); for (Vertex vertex : UNIT_FACE) { Vector4f position = vertex.position(matrices); - buffer.vertex(position.x, position.y, position.z).texture(vertex.u(), vertex.v()).color(r, g, b, a).light(light).next(); + buffer.vertex(position.x, position.y, position.z).texture(vertex.u() * uScale, vertex.v() * vScale).color(r, g, b, a).light(light).next(); } te.draw(); } public record Vertex(Vector3f position, float u, float v) { - public Vector4f position(MatrixStack matrices) { matrices.peek().getPositionMatrix().transform(TEMP_VECTOR.set(position, 1)); return TEMP_VECTOR; diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/SphereModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/SphereModel.java index 1d94ae8a..f1202b56 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/model/SphereModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/SphereModel.java @@ -20,7 +20,7 @@ public class SphereModel extends BakedModel { compileVertices(azimuthRange, zenithIncrement, azimuthIncrement, this::addVertex); } - private static void compileVertices(double azimuthRange, double zenithIncrement, double azimuthIncrement, Consumer collector) { + static void compileVertices(double azimuthRange, double zenithIncrement, double azimuthIncrement, Consumer collector) { Vector4f vector = new Vector4f(); for (double zenith = 0; zenith < DrawableUtil.PI; zenith += zenithIncrement) { for (double azimuth = 0; azimuth < azimuthRange; azimuth += azimuthIncrement) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/TexturedSphereModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/TexturedSphereModel.java new file mode 100644 index 00000000..60c0b74e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/TexturedSphereModel.java @@ -0,0 +1,41 @@ +package com.minelittlepony.unicopia.client.render.model; + +import org.joml.Vector4f; + +import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.client.render.RenderUtil; + +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.util.math.MatrixStack; + +public class TexturedSphereModel extends BakedModel { + public static final TexturedSphereModel DISK = new TexturedSphereModel(40, 2, DrawableUtil.PI); + + public TexturedSphereModel(double rings, double sectors, double azimuthRange) { + double zenithIncrement = DrawableUtil.PI / rings; + double azimuthIncrement = DrawableUtil.TAU / sectors; + SphereModel.compileVertices(azimuthRange, zenithIncrement, azimuthIncrement, this::addVertex); + } + + @Override + protected void addVertex(Vector4f vertex) { + addVertex(vertex.x, vertex.y, vertex.z, vertex.x, vertex.z); + } + + public final void render(MatrixStack matrices, VertexConsumer buffer, float scale, float r, float g, float b, float a, float uScale, float vScale) { + scale = Math.abs(scale); + if (scale < 0.001F) { + return; + } + + matrices.push(); + matrices.scale(scale, scale, scale); + uScale *= 0.5F; + vScale *= 0.5F; + for (RenderUtil.Vertex vertex : vertices) { + Vector4f pos = vertex.position(matrices); + buffer.vertex(pos.x, pos.y, pos.z).texture((vertex.u() + 1) * uScale, (vertex.v() + 1) * vScale).color(r, g, b, a).next(); + } + matrices.pop(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java index c833c5fd..698375c9 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -27,6 +27,8 @@ public class PlacedSpellRenderer extends SpellRenderer { @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + + if (!(caster.asEntity() instanceof CastSpellEntity castSpell)) { return; } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java index 4199c38d..aef30589 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java @@ -1,17 +1,57 @@ package com.minelittlepony.unicopia.client.render.spell; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix3f; +import org.joml.Matrix4f; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.minelittlepony.common.util.Color; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; +import com.minelittlepony.unicopia.client.render.model.TexturedSphereModel; +import com.minelittlepony.unicopia.entity.EntityReference; +import com.minelittlepony.unicopia.entity.mob.UEntities; +import com.minelittlepony.unicopia.mixin.client.MixinMinecraftClient; +import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.systems.VertexSorter; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.option.Perspective; +import net.minecraft.client.render.BackgroundRenderer; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.util.Window; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import net.minecraft.screen.PlayerScreenHandler; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; +import net.minecraft.util.math.Vec3d; public class PortalSpellRenderer extends SpellRenderer { + private static final LoadingCache FRAME_BUFFERS = CacheBuilder.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .removalListener(n -> n.getValue().close()) + .build(CacheLoader.from(uuid -> new PortalFrameBuffer())); @Override public boolean shouldRenderEffectPass(int pass) { @@ -32,20 +72,228 @@ public class PortalSpellRenderer extends SpellRenderer { float green = Color.g(color); float blue = Color.b(color); - VertexConsumer buffer = vertices.getBuffer(RenderLayers.getEndGateway()); - - double thickness = 0.1; + VertexConsumer buff = vertices.getBuffer(RenderLayers.getEndGateway()); matrices.push(); - matrices.translate(0, thickness, 0); - SphereModel.DISK.render(matrices, buffer, light, 0, 2.5F, red, green, blue, 1); + matrices.translate(0, 0.02, 0); + SphereModel.DISK.render(matrices, buff, light, 0, 2F, red, green, blue, 1); matrices.pop(); - matrices.push(); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); - matrices.translate(0, thickness, 0); - SphereModel.DISK.render(matrices, buffer, light, 0, 2.5F, red, green, blue, 1); + if (caster.asEntity().distanceTo(client.cameraEntity) > 50) { + return; // don't bother rendering if too far away + } - matrices.pop(); + spell.getTarget().ifPresent(target -> { + try { + float grown = Math.min(caster.asEntity().age, 20) / 20F; + matrices.push(); + matrices.translate(0, 0.01, 0); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-spell.getYaw())); + matrices.scale(grown, 1, grown); + PortalFrameBuffer buffer = FRAME_BUFFERS.get(target.uuid()); + buffer.build(spell, caster, target); + buffer.draw(matrices); + matrices.pop(); + } catch (ExecutionException e) { } + }); + } + + static class PortalFrameBuffer implements AutoCloseable { + @Nullable + private SimpleFramebuffer framebuffer; + private boolean closed; + + private final MinecraftClient client = MinecraftClient.getInstance(); + + private boolean pendingDraw; + + private static int recursionCount; + + public void draw(MatrixStack matrices) { + if (closed || framebuffer == null) { + return; + } + float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth; + float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight; + + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + + RenderSystem.enableDepthTest(); + RenderSystem.disableCull(); + RenderSystem.setShaderTexture(0, framebuffer.getColorAttachment()); + RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); + + Matrix4f textureMatrix = matrices.peek().getPositionMatrix(); + + RenderSystem.setTextureMatrix(textureMatrix); + + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); + TexturedSphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale); + tessellator.draw(); + client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); + RenderSystem.enableCull(); + RenderSystem.resetTextureMatrix(); + } + + public void build(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { + + if (System.currentTimeMillis() % 100 < 50) { + return; + } + + if (pendingDraw && recursionCount > 0) { + innerBuild(spell, caster, target); + return; + } + + if (pendingDraw) { + return; + } + pendingDraw = true; + if (recursionCount > 0) { + innerBuild(spell, caster, target); + } else { + ((MixinMinecraftClient)client).getRenderTaskQueue().add(() -> innerBuild(spell, caster, target)); + } + } + + private void innerBuild(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { + synchronized (client) { + pendingDraw = false; + + if (recursionCount > 2) { + return; + } + recursionCount++; + + try { + if (closed || client.interactionManager == null) { + close(); + return; + } + + var fov = client.options.getFov(); + int originalFov = fov.getValue(); + fov.setValue(110); + + Vec3d offset = new Vec3d(0, -1.2, -0.2); + float yaw = spell.getYawDifference();// spell.getYawDifference(); + + offset = offset.rotateY(yaw * MathHelper.RADIANS_PER_DEGREE); + + Entity cameraEntity = UEntities.CAST_SPELL.create(caster.asWorld()); + cameraEntity.setPosition(target.pos().add(offset)); + cameraEntity.setPitch(spell.getTargetPitch()); + cameraEntity.setYaw(yaw); + + drawWorld(cameraEntity, 400, 400); + + fov.setValue(originalFov); + } finally { + recursionCount--; + } + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void drawWorld(Entity cameraEntity, int width, int height) { + Entity oldCameraEntity = client.cameraEntity; + client.cameraEntity = cameraEntity; + + Window window = client.getWindow(); + + Perspective perspective = client.options.getPerspective(); + client.options.setPerspective(Perspective.FIRST_PERSON); + + int i = window.getFramebufferWidth(); + int j = window.getFramebufferHeight(); + + width = i; + height = j; + + client.getFramebuffer().endWrite(); + + if (framebuffer == null) { + framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + } + + window.setFramebufferWidth(width); + window.setFramebufferHeight(height); + + MatrixStack view = RenderSystem.getModelViewStack(); + view.push(); + view.loadIdentity(); + RenderSystem.applyModelViewMatrix(); + Matrix4f proj = RenderSystem.getProjectionMatrix(); + Matrix3f invView = RenderSystem.getInverseViewRotationMatrix(); + + int fbo = client.getFramebuffer().fbo; + client.getFramebuffer().fbo = framebuffer.fbo; + + framebuffer.setClearColor(0, 0, 0, 0); + framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC); + + RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT | GlConst.GL_COLOR_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); + framebuffer.beginWrite(true); + BackgroundRenderer.clearFog(); + + Camera camera = client.gameRenderer.getCamera(); + + ObjectArrayList chunkInfos = ((WorldRendererDuck)client.worldRenderer).unicopia_getChunkInfos(); + List backup = new ArrayList<>(chunkInfos); + + client.gameRenderer.setRenderHand(false); + MatrixStack matrices = new MatrixStack(); + + matrices.scale((float)width / height, 1, 1); + + client.gameRenderer.renderWorld(1, 0, matrices); + if (recursionCount <= 1) { + client.gameRenderer.setRenderHand(true); + } + framebuffer.endWrite(); + + chunkInfos.clear(); + chunkInfos.addAll((List)backup); + + view.pop(); + RenderSystem.applyModelViewMatrix(); + + client.getFramebuffer().fbo = fbo; + window.setFramebufferWidth(i); + window.setFramebufferHeight(j); + + client.options.setPerspective(perspective); + client.cameraEntity = oldCameraEntity; + + RenderSystem.setProjectionMatrix(proj, VertexSorter.BY_Z); + RenderSystem.setInverseViewRotationMatrix(invView); + + if (recursionCount <= 1) { + camera.update(client.world, + client.getCameraEntity() == null ? client.player : client.getCameraEntity(), + perspective.isFirstPerson(), + perspective.isFrontView(), + 1 + ); + } + + client.getFramebuffer().beginWrite(true); + } + + @Override + public void close() { + closed = true; + if (framebuffer != null) { + SimpleFramebuffer fb = framebuffer; + framebuffer = null; + fb.delete(); + } + } + } + + public interface WorldRendererDuck { + ObjectArrayList unicopia_getChunkInfos(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java new file mode 100644 index 00000000..f210faea --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java @@ -0,0 +1,14 @@ +package com.minelittlepony.unicopia.mixin.client; + +import java.util.Queue; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.MinecraftClient; + +@Mixin(MinecraftClient.class) +public interface MixinMinecraftClient { + @Accessor("renderTaskQueue") + Queue getRenderTaskQueue(); +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinWorldRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinWorldRenderer.java index edcc852d..bf5dc7b5 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinWorldRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinWorldRenderer.java @@ -15,7 +15,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.minelittlepony.unicopia.client.ClientBlockDestructionManager; import com.minelittlepony.unicopia.client.UnicopiaClient; +import com.minelittlepony.unicopia.client.render.spell.PortalSpellRenderer; + import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.client.render.BlockBreakingInfo; import net.minecraft.client.render.Camera; import net.minecraft.client.render.WorldRenderer; @@ -25,7 +28,7 @@ import net.minecraft.resource.SynchronousResourceReloader; import net.minecraft.util.math.RotationAxis; @Mixin(value = WorldRenderer.class, priority = 1001) -abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCloseable, ClientBlockDestructionManager.Source { +abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCloseable, ClientBlockDestructionManager.Source, PortalSpellRenderer.WorldRendererDuck { private final ClientBlockDestructionManager destructions = new ClientBlockDestructionManager(); @@ -41,6 +44,10 @@ abstract class MixinWorldRenderer implements SynchronousResourceReloader, AutoCl return destructions; } + @Override + @Accessor("chunkInfos") + public abstract ObjectArrayList unicopia_getChunkInfos(); + @Override @Accessor("ticks") public abstract int getTicks(); diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json index d8654fc3..dc5da585 100644 --- a/src/main/resources/unicopia.mixin.json +++ b/src/main/resources/unicopia.mixin.json @@ -73,6 +73,7 @@ "client.MixinItemStack", "client.MixinKeyboardInput", "client.MixinLivingEntityRenderer", + "client.MixinMinecraftClient", "client.MixinModelPart", "client.MixinMouse", "client.MixinPlayerEntityRenderer", From 5356c5c7ec4ef4c9b62f8b0c25ce28331fd6a360 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 24 Jan 2024 22:06:40 +0000 Subject: [PATCH 63/98] Disable fancy portal rendering since they don't work quite right --- .../magic/spell/effect/PortalSpell.java | 2 + .../render/spell/PortalSpellRenderer.java | 95 ++++++++++++------- 2 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index 255be78e..dfdd204f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -248,6 +248,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme teleportationTarget.fromNBT(compound.getCompound("teleportationTarget")); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); + targetPortalPitch = compound.getFloat("targetPortalPitch"); + targetPortalYaw = compound.getFloat("targetPortalYaw"); particleArea = PARTICLE_AREA.rotate( pitch * MathHelper.RADIANS_PER_DEGREE, (180 - yaw) * MathHelper.RADIANS_PER_DEGREE diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java index aef30589..ebdf5fc3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java @@ -23,6 +23,7 @@ import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.mixin.client.MixinMinecraftClient; import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.VertexSorter; @@ -48,6 +49,8 @@ import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.Vec3d; public class PortalSpellRenderer extends SpellRenderer { + static boolean FANCY_PORTAlS = false; + private static final LoadingCache FRAME_BUFFERS = CacheBuilder.newBuilder() .expireAfterAccess(10, TimeUnit.SECONDS) .removalListener(n -> n.getValue().close()) @@ -79,6 +82,20 @@ public class PortalSpellRenderer extends SpellRenderer { SphereModel.DISK.render(matrices, buff, light, 0, 2F, red, green, blue, 1); matrices.pop(); + if (!FANCY_PORTAlS) { + matrices.push(); + matrices.translate(0, -0.02, 0); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); + SphereModel.DISK.render(matrices, buff, light, 0, 2F, red, green, blue, 1); + matrices.pop(); + return; + } + + // Fancy portal rendering is disabled for now + // Need to fix: + // 1. Transparent parts of the sky (because the game sets the clear to (0,0,0,0) + // 2. Chunk flickering at long distances between portals + if (caster.asEntity().distanceTo(client.cameraEntity) > 50) { return; // don't bother rendering if too far away } @@ -87,12 +104,12 @@ public class PortalSpellRenderer extends SpellRenderer { try { float grown = Math.min(caster.asEntity().age, 20) / 20F; matrices.push(); - matrices.translate(0, 0.01, 0); + matrices.translate(0, -0.01, 0); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-spell.getYaw())); matrices.scale(grown, 1, grown); PortalFrameBuffer buffer = FRAME_BUFFERS.get(target.uuid()); buffer.build(spell, caster, target); - buffer.draw(matrices); + buffer.draw(matrices, vertices); matrices.pop(); } catch (ExecutionException e) { } }); @@ -101,6 +118,8 @@ public class PortalSpellRenderer extends SpellRenderer { static class PortalFrameBuffer implements AutoCloseable { @Nullable private SimpleFramebuffer framebuffer; + @Nullable + private SimpleFramebuffer backgroundBuffer; private boolean closed; private final MinecraftClient client = MinecraftClient.getInstance(); @@ -109,31 +128,32 @@ public class PortalSpellRenderer extends SpellRenderer { private static int recursionCount; - public void draw(MatrixStack matrices) { - if (closed || framebuffer == null) { - return; + public void draw(MatrixStack matrices, VertexConsumerProvider vertices) { + Vec3d skyColor = client.world.getSkyColor(client.gameRenderer.getCamera().getPos(), client.getTickDelta()); + BackgroundRenderer.setFogBlack(); + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicShield()), 0, 0, 2, (float)skyColor.x, (float)skyColor.y, (float)skyColor.z, 1); + matrices.translate(0, -0.001, 0); + + if (!(closed || framebuffer == null)) { + RenderSystem.assertOnRenderThread(); + GlStateManager._colorMask(true, true, true, false); + GlStateManager._enableDepthTest(); + GlStateManager._disableCull(); + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth; + float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight; + RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); + RenderSystem._setShaderTexture(0, framebuffer.getColorAttachment()); + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); + TexturedSphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale); + tessellator.draw(); + + client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); + GlStateManager._enableCull(); + GlStateManager._colorMask(true, true, true, true); + GlStateManager._depthMask(true); } - float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth; - float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight; - - Tessellator tessellator = RenderSystem.renderThreadTesselator(); - BufferBuilder buffer = tessellator.getBuffer(); - - RenderSystem.enableDepthTest(); - RenderSystem.disableCull(); - RenderSystem.setShaderTexture(0, framebuffer.getColorAttachment()); - RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); - - Matrix4f textureMatrix = matrices.peek().getPositionMatrix(); - - RenderSystem.setTextureMatrix(textureMatrix); - - buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); - TexturedSphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale); - tessellator.draw(); - client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); - RenderSystem.enableCull(); - RenderSystem.resetTextureMatrix(); } public void build(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { @@ -177,15 +197,15 @@ public class PortalSpellRenderer extends SpellRenderer { int originalFov = fov.getValue(); fov.setValue(110); - Vec3d offset = new Vec3d(0, -1.2, -0.2); - float yaw = spell.getYawDifference();// spell.getYawDifference(); + Vec3d offset = new Vec3d(0, 1.8, 0); + float yaw = spell.getYawDifference(); offset = offset.rotateY(yaw * MathHelper.RADIANS_PER_DEGREE); Entity cameraEntity = UEntities.CAST_SPELL.create(caster.asWorld()); cameraEntity.setPosition(target.pos().add(offset)); cameraEntity.setPitch(spell.getTargetPitch()); - cameraEntity.setYaw(yaw); + cameraEntity.setYaw(spell.getTargetYaw() + 180); drawWorld(cameraEntity, 400, 400); @@ -216,6 +236,8 @@ public class PortalSpellRenderer extends SpellRenderer { if (framebuffer == null) { framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + framebuffer.setClearColor(0, 0, 0, 0); + framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC); } window.setFramebufferWidth(width); @@ -231,12 +253,10 @@ public class PortalSpellRenderer extends SpellRenderer { int fbo = client.getFramebuffer().fbo; client.getFramebuffer().fbo = framebuffer.fbo; - framebuffer.setClearColor(0, 0, 0, 0); - framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC); - RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT | GlConst.GL_COLOR_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); framebuffer.beginWrite(true); BackgroundRenderer.clearFog(); + RenderSystem.enableCull(); Camera camera = client.gameRenderer.getCamera(); @@ -254,13 +274,15 @@ public class PortalSpellRenderer extends SpellRenderer { } framebuffer.endWrite(); + client.getFramebuffer().fbo = fbo; + client.getFramebuffer().beginWrite(true); + chunkInfos.clear(); chunkInfos.addAll((List)backup); view.pop(); RenderSystem.applyModelViewMatrix(); - client.getFramebuffer().fbo = fbo; window.setFramebufferWidth(i); window.setFramebufferHeight(j); @@ -278,8 +300,6 @@ public class PortalSpellRenderer extends SpellRenderer { 1 ); } - - client.getFramebuffer().beginWrite(true); } @Override @@ -290,6 +310,11 @@ public class PortalSpellRenderer extends SpellRenderer { framebuffer = null; fb.delete(); } + if (backgroundBuffer != null) { + SimpleFramebuffer fb = backgroundBuffer; + backgroundBuffer = null; + fb.delete(); + } } } From a19637a8e46670a70a433f86c67a26e5cb2e9218 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 25 Jan 2024 19:09:57 +0000 Subject: [PATCH 64/98] Allow spells to animate post-removal --- .../ability/UnicornDispellAbility.java | 7 +++- .../magic/spell/AbstractDelegatingSpell.java | 17 ++++++++- .../ability/magic/spell/PlaceableSpell.java | 37 +++++++++++++++++++ .../unicopia/ability/magic/spell/Spell.java | 11 +++++- .../magic/spell/effect/AbstractSpell.java | 15 +++++++- .../render/spell/PlacedSpellRenderer.java | 2 +- .../unicopia/entity/mob/CastSpellEntity.java | 4 ++ .../unicopia/network/datasync/EffectSync.java | 8 +++- 8 files changed, 95 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java index 99dda711..d965f9da 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java @@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.entity.player.Pony; @@ -92,7 +93,11 @@ public class UnicornDispellAbility implements Ability { public boolean apply(Pony player, Pos data) { player.setAnimation(Animation.WOLOLO, Animation.Recipient.ANYONE); Caster.stream(VecHelper.findInRange(player.asEntity(), player.asWorld(), data.vec(), 3, EquinePredicates.IS_PLACED_SPELL).stream()).forEach(target -> { - target.getSpellSlot().clear(); + target.getSpellSlot().forEach(spell -> { + spell.setDead(); + spell.tickDying(target); + return Operation.ofBoolean(!spell.isDead()); + }, true); }); return true; } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java index f6219d62..68a006ed 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/AbstractDelegatingSpell.java @@ -69,11 +69,20 @@ public abstract class AbstractDelegatingSpell implements Spell, getDelegates().forEach(Spell::setDead); } + @Override + public void tickDying(Caster caster) { + } + @Override public boolean isDead() { return getDelegates().isEmpty() || getDelegates().stream().allMatch(Spell::isDead); } + @Override + public boolean isDying() { + return false; + } + @Override public boolean isDirty() { return dirty || getDelegates().stream().anyMatch(Spell::isDirty); @@ -110,7 +119,13 @@ public abstract class AbstractDelegatingSpell implements Spell, @Override public boolean tick(Caster source, Situation situation) { - return execute(getDelegates().stream(), a -> a.tick(source, situation)); + return execute(getDelegates().stream(), a -> { + if (a.isDying()) { + a.tickDying(source); + return !a.isDead(); + } + return a.tick(source, situation); + }); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java index bba345b4..ed03506d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/PlaceableSpell.java @@ -59,6 +59,10 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS private int prevAge; private int age; + private boolean dead; + private int prevDeathTicks; + private int deathTicks; + private Optional position = Optional.empty(); public PlaceableSpell(CustomisedSpellType type) { @@ -74,6 +78,29 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS return MathHelper.lerp(tickDelta, prevAge, age); } + public float getScale(float tickDelta) { + float add = MathHelper.clamp(getAge(tickDelta) / 25F, 0, 1); + float subtract = dead ? 1 - (MathHelper.lerp(tickDelta, prevDeathTicks, deathTicks) / 20F) : 0; + return MathHelper.clamp(add - subtract, 0, 1); + } + + @Override + public boolean isDying() { + return dead && deathTicks > 0; + } + + @Override + public void setDead() { + super.setDead(); + dead = true; + deathTicks = 20; + } + + @Override + public boolean isDead() { + return dead && deathTicks <= 0; + } + @Override public Collection getDelegates() { return List.of(spell); @@ -119,6 +146,12 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS return !isDead(); } + @Override + public void tickDying(Caster caster) { + prevDeathTicks = deathTicks; + deathTicks--; + } + private void checkDetachment(Caster source, EntityValues target) { if (getWorld(source).map(Ether::get).map(ether -> ether.get(getType(), target, placedSpellId)).isEmpty()) { setDead(); @@ -198,6 +231,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public void toNBT(NbtCompound compound) { super.toNBT(compound); + compound.putBoolean("dead", dead); + compound.putInt("deathTicks", deathTicks); compound.putInt("age", age); compound.putFloat("pitch", pitch); compound.putFloat("yaw", yaw); @@ -217,6 +252,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public void fromNBT(NbtCompound compound) { super.fromNBT(compound); + dead = compound.getBoolean("dead"); + deathTicks = compound.getInt("deathTicks"); age = compound.getInt("age"); pitch = compound.getFloat("pitch"); yaw = compound.getFloat("yaw"); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java index d6bb1ccf..55a5f3bf 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/Spell.java @@ -61,6 +61,8 @@ public interface Spell extends NbtSerialisable, Affine { */ boolean isDead(); + boolean isDying(); + /** * Returns true if this effect has changes that need to be sent to the client. */ @@ -68,6 +70,7 @@ public interface Spell extends NbtSerialisable, Affine { /** * Applies this spell to the supplied caster. + * @param caster The caster to apply the spell to */ default boolean apply(Caster caster) { caster.getSpellSlot().put(this); @@ -76,7 +79,7 @@ public interface Spell extends NbtSerialisable, Affine { /** * Gets the default form of this spell used to apply to a caster. - * @param caster + * @param caster The caster currently fueling this spell */ default Spell prepareForCast(Caster caster, CastingMethod method) { return this; @@ -89,6 +92,12 @@ public interface Spell extends NbtSerialisable, Affine { */ boolean tick(Caster caster, Situation situation); + /** + * Called on spells that are actively dying to update any post-death animations before removal. + * @param caster The caster currently fueling this spell + */ + void tickDying(Caster caster); + /** * Marks this effect as dirty. */ diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java index a8c1b706..8b64d9c3 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/AbstractSpell.java @@ -12,6 +12,7 @@ import net.minecraft.nbt.NbtCompound; public abstract class AbstractSpell implements Spell { private boolean dead; + private boolean dying; private boolean dirty; private boolean hidden; private boolean destroyed; @@ -45,7 +46,7 @@ public abstract class AbstractSpell implements Spell { @Override public final void setDead() { - dead = true; + dying = true; setDirty(); } @@ -54,6 +55,11 @@ public abstract class AbstractSpell implements Spell { return dead; } + @Override + public final boolean isDying() { + return dying; + } + @Override public final boolean isDirty() { return dirty; @@ -82,6 +88,11 @@ public abstract class AbstractSpell implements Spell { protected void onDestroyed(Caster caster) { } + @Override + public void tickDying(Caster caster) { + dead = true; + } + @Override public final void destroy(Caster caster) { if (destroyed) { @@ -94,6 +105,7 @@ public abstract class AbstractSpell implements Spell { @Override public void toNBT(NbtCompound compound) { + compound.putBoolean("dying", dying); compound.putBoolean("dead", dead); compound.putBoolean("hidden", hidden); compound.putUuid("uuid", uuid); @@ -106,6 +118,7 @@ public abstract class AbstractSpell implements Spell { if (compound.contains("uuid")) { uuid = compound.getUuid("uuid"); } + dying = compound.getBoolean("dying"); dead = compound.getBoolean("dead"); hidden = compound.getBoolean("hidden"); if (compound.contains("traits")) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java index 698375c9..7c4d09c3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -57,7 +57,7 @@ public class PlacedSpellRenderer extends SpellRenderer { matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw)); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); - float scale = (spell.getAge(tickDelta) / 25F) * 3; + float scale = spell.getScale(tickDelta) * 3; matrices.scale(scale, scale, scale); float angle = (animationProgress / 9F) % 360; diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java index 16a4100b..9677a123 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java @@ -72,6 +72,10 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster getMasterReference() { return owner; diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java index 2a4568e9..b2cad93c 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java @@ -44,7 +44,13 @@ public class EffectSync implements SpellContainer, NbtSerialisable { } public boolean tick(Situation situation) { - return tick(spell -> Operation.ofBoolean(spell.tick(owner, situation))); + return tick(spell -> { + if (spell.isDying()) { + spell.tickDying(owner); + return Operation.ofBoolean(!spell.isDead()); + } + return Operation.ofBoolean(spell.tick(owner, situation)); + }); } public boolean tick(Function tickAction) { From 1a17ff7dd0731e6dcdcc30b10e0eb9d7f79e6b41 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 25 Jan 2024 22:56:22 +0000 Subject: [PATCH 65/98] Fix portal rendering issues :D --- .../render/spell/PortalFrameBuffer.java | 287 +++++++++++++++++ .../render/spell/PortalSpellRenderer.java | 302 ++---------------- .../mixin/client/MixinMinecraftClient.java | 6 + 3 files changed, 326 insertions(+), 269 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java new file mode 100644 index 00000000..dffa297b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java @@ -0,0 +1,287 @@ +package com.minelittlepony.unicopia.client.render.spell; + +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix3f; +import org.joml.Matrix4f; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; +import com.minelittlepony.unicopia.client.render.RenderLayers; +import com.minelittlepony.unicopia.client.render.model.SphereModel; +import com.minelittlepony.unicopia.client.render.model.TexturedSphereModel; +import com.minelittlepony.unicopia.entity.EntityReference; +import com.minelittlepony.unicopia.entity.mob.UEntities; +import com.minelittlepony.unicopia.mixin.client.MixinMinecraftClient; +import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.systems.VertexSorter; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.option.Perspective; +import net.minecraft.client.render.BackgroundRenderer; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.Frustum; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.util.Window; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.screen.PlayerScreenHandler; +import net.minecraft.util.math.Vec3d; + +class PortalFrameBuffer implements AutoCloseable { + private static final LoadingCache CACHE = CacheBuilder.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .removalListener(n -> n.getValue().close()) + .build(CacheLoader.from(PortalFrameBuffer::new)); + + private static int recursionCount; + + @Nullable + public static PortalFrameBuffer unpool(UUID id) { + try { + return CACHE.get(id); + } catch (ExecutionException e) { + return null; + } + } + + @Nullable + private SimpleFramebuffer framebuffer; + @Nullable + private SimpleFramebuffer backgroundBuffer; + @Nullable + private WorldRenderer renderer; + @Nullable + private ClientWorld world; + + private boolean closed; + + private final MinecraftClient client = MinecraftClient.getInstance(); + + private boolean pendingDraw; + + private final UUID id; + + @Nullable + private Frustum frustum; + + PortalFrameBuffer(UUID id) { + this.id = id; + } + + public void draw(MatrixStack matrices, VertexConsumerProvider vertices) { + matrices.translate(0, -0.001, 0); + + if (!(closed || framebuffer == null)) { + RenderSystem.assertOnRenderThread(); + GlStateManager._colorMask(true, true, true, false); + GlStateManager._enableDepthTest(); + GlStateManager._disableCull(); + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth; + float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight; + RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); + RenderSystem._setShaderTexture(0, framebuffer.getColorAttachment()); + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); + TexturedSphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale); + tessellator.draw(); + + client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); + GlStateManager._enableCull(); + GlStateManager._colorMask(true, true, true, true); + GlStateManager._depthMask(true); + } else { + Vec3d skyColor = client.world.getSkyColor(client.gameRenderer.getCamera().getPos(), client.getTickDelta()); + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicShield()), 0, 0, 2, (float)skyColor.x, (float)skyColor.y, (float)skyColor.z, 1); + } + } + + public void build(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { + + if (framebuffer != null && System.currentTimeMillis() % 100 != 0) { + return; + } + + if (pendingDraw && recursionCount > 0) { + innerBuild(spell, caster, target); + return; + } + + if (pendingDraw) { + return; + } + pendingDraw = true; + if (recursionCount > 0) { + innerBuild(spell, caster, target); + } else { + ((MixinMinecraftClient)client).getRenderTaskQueue().add(() -> innerBuild(spell, caster, target)); + } + } + + private void innerBuild(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { + synchronized (client) { + pendingDraw = false; + + if (recursionCount > 2) { + return; + } + recursionCount++; + + try { + if (closed || client.interactionManager == null) { + close(); + return; + } + + var fov = client.options.getFov(); + int originalFov = fov.getValue(); + fov.setValue(110); + + Entity cameraEntity = UEntities.CAST_SPELL.create(caster.asWorld()); + cameraEntity.setPosition(target.pos()); + cameraEntity.setPitch(spell.getTargetPitch()); + cameraEntity.setYaw(spell.getTargetYaw() + 180); + + drawWorld(cameraEntity, 400, 400); + + fov.setValue(originalFov); + } finally { + recursionCount--; + } + } + } + + private void drawWorld(Entity cameraEntity, int width, int height) { + Entity oldCameraEntity = client.cameraEntity; + Window window = client.getWindow(); + + int i = window.getFramebufferWidth(); + int j = window.getFramebufferHeight(); + + width = i; + height = j; + + Perspective perspective = client.options.getPerspective(); + MatrixStack view = RenderSystem.getModelViewStack(); + + Matrix4f proj = RenderSystem.getProjectionMatrix(); + Matrix3f invView = RenderSystem.getInverseViewRotationMatrix(); + + int fbo = client.getFramebuffer().fbo; + Camera camera = client.gameRenderer.getCamera(); + + WorldRenderer globalRenderer = client.worldRenderer; + try { + client.cameraEntity = cameraEntity; + client.getFramebuffer().endWrite(); + + if (framebuffer == null) { + framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); + framebuffer.setClearColor(0, 0, 0, 0); + framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC); + } + + view.push(); + view.loadIdentity(); + RenderSystem.applyModelViewMatrix(); + + window.setFramebufferWidth(width); + window.setFramebufferHeight(height); + client.getFramebuffer().fbo = framebuffer.fbo; + + client.options.setPerspective(Perspective.FIRST_PERSON); + + RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT | GlConst.GL_COLOR_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); + framebuffer.beginWrite(true); + BackgroundRenderer.clearFog(); + RenderSystem.enableCull(); + + if (renderer == null) { + renderer = new WorldRenderer(client, client.getEntityRenderDispatcher(), client.getBlockEntityRenderDispatcher(), client.getBufferBuilders()); + } + if (client.world != world) { + world = client.world; + renderer.setWorld(client.world); + } + ((MixinMinecraftClient)client).setWorldRenderer(renderer); + + renderer.scheduleBlockRenders((int)cameraEntity.getX() / 16, (int)cameraEntity.getY() / 16, (int)cameraEntity.getZ() / 16); + + client.gameRenderer.setRenderHand(false); + MatrixStack matrices = new MatrixStack(); + + matrices.scale((float)width / height, 1, 1); + + client.gameRenderer.renderWorld(1, 0, matrices); + + // Strip transparency + RenderSystem.colorMask(false, false, false, true); + RenderSystem.clearColor(1, 1, 1, 1); + RenderSystem.clear(GlConst.GL_COLOR_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); + RenderSystem.colorMask(true, true, true, true); + + framebuffer.endWrite(); + } finally { + ((MixinMinecraftClient)client).setWorldRenderer(globalRenderer); + + client.getFramebuffer().fbo = fbo; + client.getFramebuffer().beginWrite(true); + + view.pop(); + RenderSystem.applyModelViewMatrix(); + RenderSystem.setProjectionMatrix(proj, VertexSorter.BY_Z); + RenderSystem.setInverseViewRotationMatrix(invView); + + window.setFramebufferWidth(i); + window.setFramebufferHeight(j); + + client.options.setPerspective(perspective); + client.cameraEntity = oldCameraEntity; + + if (recursionCount <= 1) { + client.gameRenderer.setRenderHand(true); + camera.update(client.world, + client.getCameraEntity() == null ? client.player : client.getCameraEntity(), + perspective.isFirstPerson(), + perspective.isFrontView(), + 1 + ); + } + } + } + + @Override + public void close() { + closed = true; + if (framebuffer != null) { + SimpleFramebuffer fb = framebuffer; + framebuffer = null; + fb.delete(); + } + if (backgroundBuffer != null) { + SimpleFramebuffer fb = backgroundBuffer; + backgroundBuffer = null; + fb.delete(); + } + if (renderer != null) { + renderer.close(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java index ebdf5fc3..4324ef30 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java @@ -1,60 +1,21 @@ package com.minelittlepony.unicopia.client.render.spell; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - -import org.jetbrains.annotations.Nullable; -import org.joml.Matrix3f; -import org.joml.Matrix4f; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.minelittlepony.common.util.Color; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; -import com.minelittlepony.unicopia.client.render.model.TexturedSphereModel; import com.minelittlepony.unicopia.entity.EntityReference; -import com.minelittlepony.unicopia.entity.mob.UEntities; -import com.minelittlepony.unicopia.mixin.client.MixinMinecraftClient; -import com.mojang.blaze3d.platform.GlConst; -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.systems.VertexSorter; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gl.SimpleFramebuffer; -import net.minecraft.client.option.Perspective; -import net.minecraft.client.render.BackgroundRenderer; -import net.minecraft.client.render.BufferBuilder; -import net.minecraft.client.render.Camera; -import net.minecraft.client.render.GameRenderer; -import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.Frustum; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.VertexFormat; -import net.minecraft.client.render.VertexFormats; -import net.minecraft.client.util.Window; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.entity.Entity; -import net.minecraft.screen.PlayerScreenHandler; -import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; -import net.minecraft.util.math.Vec3d; public class PortalSpellRenderer extends SpellRenderer { - static boolean FANCY_PORTAlS = false; - - private static final LoadingCache FRAME_BUFFERS = CacheBuilder.newBuilder() - .expireAfterAccess(10, TimeUnit.SECONDS) - .removalListener(n -> n.getValue().close()) - .build(CacheLoader.from(uuid -> new PortalFrameBuffer())); + static boolean FANCY_PORTAlS = true; @Override public boolean shouldRenderEffectPass(int pass) { @@ -62,31 +23,21 @@ public class PortalSpellRenderer extends SpellRenderer { } @Override - public void render(MatrixStack matrices, VertexConsumerProvider vertices, PortalSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { - super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); - - if (!spell.isLinked()) { - return; - } - - int color = spell.getType().getColor(); - - float red = Color.r(color); - float green = Color.g(color); - float blue = Color.b(color); + public void render(MatrixStack matrices, VertexConsumerProvider vertices, PortalSpell spell, Caster caster, int light, float strength, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { + super.render(matrices, vertices, spell, caster, light, strength, limbDistance, tickDelta, animationProgress, headYaw, headPitch); VertexConsumer buff = vertices.getBuffer(RenderLayers.getEndGateway()); matrices.push(); matrices.translate(0, 0.02, 0); - SphereModel.DISK.render(matrices, buff, light, 0, 2F, red, green, blue, 1); + SphereModel.DISK.render(matrices, buff, light, 0, 2F * strength, 1, 1, 1, 1); matrices.pop(); - if (!FANCY_PORTAlS) { + if (!FANCY_PORTAlS || !spell.isLinked()) { matrices.push(); matrices.translate(0, -0.02, 0); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); - SphereModel.DISK.render(matrices, buff, light, 0, 2F, red, green, blue, 1); + SphereModel.DISK.render(matrices, buff, light, 0, 2F * strength, 1, 1, 1, 1); matrices.pop(); return; } @@ -94,231 +45,44 @@ public class PortalSpellRenderer extends SpellRenderer { // Fancy portal rendering is disabled for now // Need to fix: // 1. Transparent parts of the sky (because the game sets the clear to (0,0,0,0) - // 2. Chunk flickering at long distances between portals if (caster.asEntity().distanceTo(client.cameraEntity) > 50) { return; // don't bother rendering if too far away } + matrices.push(); + matrices.scale(strength, strength, strength); + spell.getTarget().ifPresent(target -> { - try { - float grown = Math.min(caster.asEntity().age, 20) / 20F; - matrices.push(); - matrices.translate(0, -0.01, 0); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-spell.getYaw())); - matrices.scale(grown, 1, grown); - PortalFrameBuffer buffer = FRAME_BUFFERS.get(target.uuid()); - buffer.build(spell, caster, target); + float grown = Math.min(caster.asEntity().age, 20) / 20F; + matrices.push(); + matrices.translate(0, -0.01, 0); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-spell.getYaw())); + matrices.scale(grown, 1, grown); + boolean inRange = MinecraftClient.getInstance().player.getPos().distanceTo(target.pos()) < MinecraftClient.getInstance().gameRenderer.getViewDistance(); + + PortalFrameBuffer buffer = PortalFrameBuffer.unpool(target.uuid()); + if (buffer != null) { + if (inRange) { + buffer.build(spell, caster, target); + } buffer.draw(matrices, vertices); - matrices.pop(); - } catch (ExecutionException e) { } + } + if (!inRange) { + buffer = PortalFrameBuffer.unpool(caster.asEntity().getUuid()); + if (buffer != null) { + buffer.build(spell, caster, new EntityReference.EntityValues<>(caster.asEntity())); + } + } + matrices.pop(); }); - } - static class PortalFrameBuffer implements AutoCloseable { - @Nullable - private SimpleFramebuffer framebuffer; - @Nullable - private SimpleFramebuffer backgroundBuffer; - private boolean closed; - - private final MinecraftClient client = MinecraftClient.getInstance(); - - private boolean pendingDraw; - - private static int recursionCount; - - public void draw(MatrixStack matrices, VertexConsumerProvider vertices) { - Vec3d skyColor = client.world.getSkyColor(client.gameRenderer.getCamera().getPos(), client.getTickDelta()); - BackgroundRenderer.setFogBlack(); - SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getMagicShield()), 0, 0, 2, (float)skyColor.x, (float)skyColor.y, (float)skyColor.z, 1); - matrices.translate(0, -0.001, 0); - - if (!(closed || framebuffer == null)) { - RenderSystem.assertOnRenderThread(); - GlStateManager._colorMask(true, true, true, false); - GlStateManager._enableDepthTest(); - GlStateManager._disableCull(); - Tessellator tessellator = RenderSystem.renderThreadTesselator(); - BufferBuilder buffer = tessellator.getBuffer(); - float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth; - float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight; - RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); - RenderSystem._setShaderTexture(0, framebuffer.getColorAttachment()); - buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); - TexturedSphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale); - tessellator.draw(); - - client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); - GlStateManager._enableCull(); - GlStateManager._colorMask(true, true, true, true); - GlStateManager._depthMask(true); - } - } - - public void build(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { - - if (System.currentTimeMillis() % 100 < 50) { - return; - } - - if (pendingDraw && recursionCount > 0) { - innerBuild(spell, caster, target); - return; - } - - if (pendingDraw) { - return; - } - pendingDraw = true; - if (recursionCount > 0) { - innerBuild(spell, caster, target); - } else { - ((MixinMinecraftClient)client).getRenderTaskQueue().add(() -> innerBuild(spell, caster, target)); - } - } - - private void innerBuild(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { - synchronized (client) { - pendingDraw = false; - - if (recursionCount > 2) { - return; - } - recursionCount++; - - try { - if (closed || client.interactionManager == null) { - close(); - return; - } - - var fov = client.options.getFov(); - int originalFov = fov.getValue(); - fov.setValue(110); - - Vec3d offset = new Vec3d(0, 1.8, 0); - float yaw = spell.getYawDifference(); - - offset = offset.rotateY(yaw * MathHelper.RADIANS_PER_DEGREE); - - Entity cameraEntity = UEntities.CAST_SPELL.create(caster.asWorld()); - cameraEntity.setPosition(target.pos().add(offset)); - cameraEntity.setPitch(spell.getTargetPitch()); - cameraEntity.setYaw(spell.getTargetYaw() + 180); - - drawWorld(cameraEntity, 400, 400); - - fov.setValue(originalFov); - } finally { - recursionCount--; - } - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private void drawWorld(Entity cameraEntity, int width, int height) { - Entity oldCameraEntity = client.cameraEntity; - client.cameraEntity = cameraEntity; - - Window window = client.getWindow(); - - Perspective perspective = client.options.getPerspective(); - client.options.setPerspective(Perspective.FIRST_PERSON); - - int i = window.getFramebufferWidth(); - int j = window.getFramebufferHeight(); - - width = i; - height = j; - - client.getFramebuffer().endWrite(); - - if (framebuffer == null) { - framebuffer = new SimpleFramebuffer(width, height, true, MinecraftClient.IS_SYSTEM_MAC); - framebuffer.setClearColor(0, 0, 0, 0); - framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC); - } - - window.setFramebufferWidth(width); - window.setFramebufferHeight(height); - - MatrixStack view = RenderSystem.getModelViewStack(); - view.push(); - view.loadIdentity(); - RenderSystem.applyModelViewMatrix(); - Matrix4f proj = RenderSystem.getProjectionMatrix(); - Matrix3f invView = RenderSystem.getInverseViewRotationMatrix(); - - int fbo = client.getFramebuffer().fbo; - client.getFramebuffer().fbo = framebuffer.fbo; - - RenderSystem.clear(GlConst.GL_DEPTH_BUFFER_BIT | GlConst.GL_COLOR_BUFFER_BIT, MinecraftClient.IS_SYSTEM_MAC); - framebuffer.beginWrite(true); - BackgroundRenderer.clearFog(); - RenderSystem.enableCull(); - - Camera camera = client.gameRenderer.getCamera(); - - ObjectArrayList chunkInfos = ((WorldRendererDuck)client.worldRenderer).unicopia_getChunkInfos(); - List backup = new ArrayList<>(chunkInfos); - - client.gameRenderer.setRenderHand(false); - MatrixStack matrices = new MatrixStack(); - - matrices.scale((float)width / height, 1, 1); - - client.gameRenderer.renderWorld(1, 0, matrices); - if (recursionCount <= 1) { - client.gameRenderer.setRenderHand(true); - } - framebuffer.endWrite(); - - client.getFramebuffer().fbo = fbo; - client.getFramebuffer().beginWrite(true); - - chunkInfos.clear(); - chunkInfos.addAll((List)backup); - - view.pop(); - RenderSystem.applyModelViewMatrix(); - - window.setFramebufferWidth(i); - window.setFramebufferHeight(j); - - client.options.setPerspective(perspective); - client.cameraEntity = oldCameraEntity; - - RenderSystem.setProjectionMatrix(proj, VertexSorter.BY_Z); - RenderSystem.setInverseViewRotationMatrix(invView); - - if (recursionCount <= 1) { - camera.update(client.world, - client.getCameraEntity() == null ? client.player : client.getCameraEntity(), - perspective.isFirstPerson(), - perspective.isFrontView(), - 1 - ); - } - } - - @Override - public void close() { - closed = true; - if (framebuffer != null) { - SimpleFramebuffer fb = framebuffer; - framebuffer = null; - fb.delete(); - } - if (backgroundBuffer != null) { - SimpleFramebuffer fb = backgroundBuffer; - backgroundBuffer = null; - fb.delete(); - } - } + matrices.pop(); } public interface WorldRendererDuck { ObjectArrayList unicopia_getChunkInfos(); + + Frustum unicopia_getFrustum(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java index f210faea..848ef7d0 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinMinecraftClient.java @@ -3,12 +3,18 @@ package com.minelittlepony.unicopia.mixin.client; import java.util.Queue; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.WorldRenderer; @Mixin(MinecraftClient.class) public interface MixinMinecraftClient { @Accessor("renderTaskQueue") Queue getRenderTaskQueue(); + + @Mutable + @Accessor("worldRenderer") + void setWorldRenderer(WorldRenderer worldRenderer); } From 4c821a521cd91c86630defd120f1fd92548ad878 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 25 Jan 2024 22:57:23 +0000 Subject: [PATCH 66/98] Change cast spells to be easier to reach and change the center to be the center of the entity not its base position --- .../minelittlepony/unicopia/EntityConvertable.java | 2 +- .../ability/magic/spell/effect/PortalSpell.java | 2 +- .../client/render/spell/PlacedSpellRenderer.java | 10 +++++++--- .../unicopia/entity/mob/CastSpellEntity.java | 13 +++++++++++-- .../unicopia/entity/mob/UEntities.java | 2 +- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/EntityConvertable.java b/src/main/java/com/minelittlepony/unicopia/EntityConvertable.java index d21ac152..6a0ffce7 100644 --- a/src/main/java/com/minelittlepony/unicopia/EntityConvertable.java +++ b/src/main/java/com/minelittlepony/unicopia/EntityConvertable.java @@ -25,7 +25,7 @@ public interface EntityConvertable extends WorldConvertable { * Gets the center position where this caster is located. */ default Vec3d getOriginVector() { - return asEntity().getPos(); + return asEntity().getPos().add(0, asEntity().getHeight() * 0.5F, 0); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index dfdd204f..c067300c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -216,7 +216,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme LivingEntity caster = source.getMaster(); Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos()); parent.setOrientation(pitch, yaw); - entity.setPos(targetPos.x, caster.getEyePos().y, targetPos.z); + entity.setPos(targetPos.x, caster.getEyePos().y - (entity.getHeight() * 0.5F), targetPos.z); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java index 7c4d09c3..fbdfce47 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PlacedSpellRenderer.java @@ -27,8 +27,6 @@ public class PlacedSpellRenderer extends SpellRenderer { @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, PlaceableSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { - - if (!(caster.asEntity() instanceof CastSpellEntity castSpell)) { return; } @@ -36,13 +34,16 @@ public class PlacedSpellRenderer extends SpellRenderer { matrices.push(); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-castSpell.getYaw())); + for (Spell delegate : spell.getDelegates()) { renderAmbientEffects(matrices, vertices, spell, delegate, caster, light, animationProgress, tickDelta); matrices.push(); + float height = caster.asEntity().getHeight(); + matrices.translate(0, (-spell.pitch / 90F) * height * 0.5F, 0); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-spell.pitch)); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw)); - SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertices, delegate, caster, light, spell.getScale(tickDelta), limbDistance, tickDelta, animationProgress, headYaw, headPitch); matrices.pop(); } @@ -53,6 +54,9 @@ public class PlacedSpellRenderer extends SpellRenderer { matrices.push(); matrices.translate(0, 0.001, 0); + float height = caster.asEntity().getHeight(); + matrices.translate(0, (-spell.pitch / 90F) * height * 0.5F, 0); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-spell.pitch)); matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw)); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java index 9677a123..92b1a909 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/CastSpellEntity.java @@ -6,6 +6,7 @@ import com.minelittlepony.unicopia.ability.magic.Levelled; import com.minelittlepony.unicopia.ability.magic.SpellContainer; import com.minelittlepony.unicopia.ability.magic.spell.Situation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; +import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.EntityPhysics; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.MagicImmune; @@ -13,6 +14,8 @@ import com.minelittlepony.unicopia.entity.Physics; import com.minelittlepony.unicopia.network.datasync.EffectSync; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityDimensions; +import net.minecraft.entity.EntityPose; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.data.DataTracker; @@ -72,8 +75,9 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster spell.getScale(1)).orElse(1F)); } @Override @@ -117,6 +121,11 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster CAST_SPELL = register("cast_spell", FabricEntityTypeBuilder.create(SpawnGroup.MISC, CastSpellEntity::new) .trackRangeBlocks(200) - .dimensions(EntityDimensions.fixed(1, 1))); + .dimensions(EntityDimensions.changing(4, 4))); EntityType TWITTERMITE = register("twittermite", FabricEntityTypeBuilder.create(SpawnGroup.MISC, FairyEntity::new) .trackRangeBlocks(200) .dimensions(EntityDimensions.fixed(0.1F, 0.1F))); From 885bf6d1c48828e0dbb29e2593d285d327d50a1c Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 25 Jan 2024 22:58:02 +0000 Subject: [PATCH 67/98] Only send entities through a portal if they're facing into it --- .../ability/magic/spell/effect/PortalSpell.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index c067300c..fcac287e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -20,7 +20,6 @@ import com.minelittlepony.unicopia.util.shape.*; import net.minecraft.block.Block; import net.minecraft.block.Blocks; -import net.minecraft.client.MinecraftClient; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.NbtCompound; @@ -28,7 +27,6 @@ import net.minecraft.network.packet.s2c.play.PositionFlag; import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleTypes; import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -110,7 +108,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme .orElse(ParticleTypes.ELECTRIC_SPARK); source.spawnParticles(origin, particleArea, 5, pos -> { - source.addParticle(effect, pos, Vec3d.ZERO); + source.addParticle(ParticleTypes.ELECTRIC_SPARK, pos, Vec3d.ZERO); }); } else { getTarget().ifPresent(target -> { @@ -150,18 +148,22 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme destination.entity.getTarget().ifPresent(target -> { source.findAllEntitiesInRange(1).forEach(entity -> { - if (!entity.hasPortalCooldown() && entity.timeUntilRegen <= 0) { + if (!entity.hasPortalCooldown()) { + + float approachYaw = Math.abs(MathHelper.wrapDegrees(entity.getYaw() - this.yaw)); + if (approachYaw > 80) { + return; + } + Vec3d offset = entity.getPos().subtract(source.getOriginVector()); float yawDifference = pitch < 15 ? getYawDifference() : 0; Vec3d dest = target.pos().add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.1, 0); - MinecraftClient.getInstance().player.sendMessage(Text.literal(yawDifference + "")); if (entity.getWorld().isTopSolid(BlockPos.ofFloored(dest).up(), entity)) { dest = dest.add(0, 1, 0); } entity.resetPortalCooldown(); - entity.timeUntilRegen = 100; float yaw = MathHelper.wrapDegrees(entity.getYaw() + yawDifference); From bbe9eb00680897089e46fe6f690c6a83c2191598 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 25 Jan 2024 22:58:40 +0000 Subject: [PATCH 68/98] Fix shields not rendering in first person and change shields to be a semi-circle (since the rest will just clip into the ground) --- .../magic/spell/effect/ShieldSpell.java | 15 ++++++++++++- .../render/AccessoryFeatureRenderer.java | 4 ++++ .../render/spell/ShieldSpellRenderer.java | 21 ++++++++++++++++--- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java index 023da1c7..5ad19d2d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java @@ -49,6 +49,9 @@ public class ShieldSpell extends AbstractSpell { private float rangeMultiplier; private float targetRangeMultiplier; + private int prevTicksDying; + private int ticksDying; + protected ShieldSpell(CustomisedSpellType type) { super(type); } @@ -102,6 +105,14 @@ public class ShieldSpell extends AbstractSpell { return !isDead(); } + @Override + public void tickDying(Caster caster) { + prevTicksDying = ticksDying; + if (ticksDying++ > 25) { + super.tickDying(caster); + } + } + protected void consumeManage(Caster source, long costMultiplier, float knowledge) { double cost = 2 - source.getLevel().getScaled(2); @@ -115,7 +126,9 @@ public class ShieldSpell extends AbstractSpell { } public float getRadius(float tickDelta) { - return MathHelper.lerp(tickDelta, prevRadius, radius); + float base = MathHelper.lerp(tickDelta, prevRadius, radius); + float scale = MathHelper.clamp(MathHelper.lerp(tickDelta, prevTicksDying, ticksDying), 0, 1); + return base * scale; } /** diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java index 857e4c96..16261ff6 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java @@ -57,6 +57,10 @@ public class AccessoryFeatureRenderer< } public boolean beforeRenderArms(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, int light) { + Caster caster = Caster.of(entity).orElse(null); + if (caster != null) { + SpellEffectsRenderDispatcher.INSTANCE.render(matrices, vertexConsumers, light, caster, 0, 0, tickDelta, entity.age + tickDelta, 0, 0); + } boolean cancelled = false; for (var feature : features) { cancelled |= feature.beforeRenderArms(sender, tickDelta, matrices, vertexConsumers, entity, light); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java index 8ce41089..260b322c 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/ShieldSpellRenderer.java @@ -3,16 +3,21 @@ package com.minelittlepony.unicopia.client.render.spell; import com.minelittlepony.common.util.Color; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.ShieldSpell; +import com.minelittlepony.unicopia.client.gui.DrawableUtil; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; import com.minelittlepony.unicopia.util.ColorHelper; +import net.minecraft.client.option.Perspective; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; public class ShieldSpellRenderer extends SpellRenderer { + private final SphereModel model = new SphereModel(40, 40, DrawableUtil.PI); + @Override public void render(MatrixStack matrices, VertexConsumerProvider vertices, ShieldSpell spell, Caster caster, int light, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); @@ -27,11 +32,21 @@ public class ShieldSpellRenderer extends SpellRenderer { VertexConsumer buffer = vertices.getBuffer(RenderLayers.getMagicShield()); + boolean firstPerson = caster.asEntity() == client.player && client.options.getPerspective() == Perspective.FIRST_PERSON; + float thickness = 0.02F * MathHelper.sin(animationProgress / 30F); float alpha = 1 - Math.abs(MathHelper.sin(animationProgress / 20F)) * 0.2F; - SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness, colors[0], colors[1], colors[2], alpha * 0.08F); - SphereModel.SPHERE.render(matrices, buffer, light, 1, radius - thickness, colors[0], colors[1], colors[2], alpha * 0.05F); - SphereModel.SPHERE.render(matrices, buffer, light, 1, radius + thickness * 2, colors[0], colors[1], colors[2], alpha * 0.05F); + + if (firstPerson) { + matrices.translate(0, -1.75F, 0); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(client.cameraEntity.getPitch(tickDelta))); + model.render(matrices, buffer, light, 1, radius, colors[0], colors[1], colors[2], alpha * 0.2F); + } else { + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); + model.render(matrices, buffer, light, 1, radius + thickness, colors[0], colors[1], colors[2], alpha * 0.08F); + model.render(matrices, buffer, light, 1, radius - thickness, colors[0], colors[1], colors[2], alpha * 0.05F); + model.render(matrices, buffer, light, 1, radius + thickness * 2, colors[0], colors[1], colors[2], alpha * 0.05F); + } matrices.pop(); } From 4151b66e99cccc3241567ec2fc89f2147ef79a6a Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 26 Jan 2024 15:57:36 +0000 Subject: [PATCH 69/98] Fix shields targetting the entity it belongs to --- .../com/minelittlepony/unicopia/Owned.java | 21 ++++++--- .../unicopia/ability/magic/Affine.java | 4 ++ .../magic/spell/effect/DarkVortexSpell.java | 1 - .../magic/spell/effect/FireBoltSpell.java | 4 +- .../magic/spell/effect/PortalSpell.java | 9 +--- .../magic/spell/effect/ShieldSpell.java | 7 +-- .../magic/spell/effect/TargetSelecter.java | 45 +++++++------------ .../unicopia/entity/Creature.java | 2 +- .../unicopia/entity/player/Pony.java | 5 +++ .../player/dummy/DummyClientPlayerEntity.java | 9 ++++ .../player/dummy/DummyPlayerEntity.java | 9 ++++ .../unicopia/item/FriendshipBraceletItem.java | 14 +++--- 12 files changed, 71 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Owned.java b/src/main/java/com/minelittlepony/unicopia/Owned.java index 37642a1d..337a59ed 100644 --- a/src/main/java/com/minelittlepony/unicopia/Owned.java +++ b/src/main/java/com/minelittlepony/unicopia/Owned.java @@ -5,6 +5,8 @@ import java.util.UUID; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.item.FriendshipBraceletItem; + import net.minecraft.entity.Entity; /** @@ -27,14 +29,23 @@ public interface Owned { * Since {@link Owned#getMaster()} will only return if the owner is loaded, use this to perform checks * in the owner's absence. */ - default Optional getMasterId() { - return Optional.of(getMaster()).map(Entity::getUuid); + Optional getMasterId(); + + default boolean isOwnerOrFriend(Entity target) { + return FriendshipBraceletItem.isComrade(this, target) || isOwnerOrVehicle(target); + } + + default boolean isOwnerOrVehicle(@Nullable Entity target) { + if (isOwnedBy(target)) { + return true; + } + + Entity owner = getMaster(); + return target != null && owner != null && owner.isConnectedThroughVehicle(target); } default boolean isOwnedBy(@Nullable Object owner) { - return owner instanceof Entity e - && getMasterId().isPresent() - && e.getUuid().equals(getMasterId().get()); + return owner instanceof Entity e && e.getUuid().equals(getMasterId().orElse(null)); } default boolean hasCommonOwner(Owned sibling) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java index 007da627..7cdfdaef 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/Affine.java @@ -19,4 +19,8 @@ public interface Affine { default boolean isFriendlyTogether(Affine other) { return getAffinity() != Affinity.BAD && other.getAffinity() != Affinity.BAD; } + + default boolean applyInversion(Affine other, boolean friendly) { + return isEnemy(other) ? !friendly : friendly; + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java index 7c05d439..02eb96d3 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/DarkVortexSpell.java @@ -97,7 +97,6 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega } } - @Override public boolean isFriendlyTogether(Affine other) { return accumulatedMass < 4; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java index c2324a5f..a76ef43b 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/FireBoltSpell.java @@ -48,7 +48,7 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell, if (caster instanceof MagicProjectileEntity && getTraits().get(Trait.FOCUS) >= 50) { caster.findAllEntitiesInRange( getTraits().get(Trait.FOCUS) - 49, - EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.notOwnerOrFriend(this, caster)) + EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster)) ).findFirst().ifPresent(target -> { ((MagicProjectileEntity)caster).setHomingTarget(target); }); @@ -60,7 +60,7 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell, if (getTraits().get(Trait.FOCUS) >= 50 && target.getOrEmpty(caster.asWorld()).isEmpty()) { target.set(caster.findAllEntitiesInRange( getTraits().get(Trait.FOCUS) - 49, - EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.notOwnerOrFriend(this, caster)) + EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.validTarget(this, caster)) ).findFirst().orElse(null)); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index fcac287e..fa2963f5 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -24,7 +24,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.packet.s2c.play.PositionFlag; -import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleTypes; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; @@ -101,13 +100,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme if (situation == Situation.GROUND) { if (source.isClient()) { - Vec3d origin = source.getOriginVector(); - - ParticleEffect effect = getTarget() - .map(target -> (ParticleEffect)new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK)) - .orElse(ParticleTypes.ELECTRIC_SPARK); - - source.spawnParticles(origin, particleArea, 5, pos -> { + source.spawnParticles(particleArea, 5, pos -> { source.addParticle(ParticleTypes.ELECTRIC_SPARK, pos, Vec3d.ZERO); }); } else { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java index 5ad19d2d..04831ef6 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/ShieldSpell.java @@ -41,7 +41,7 @@ public class ShieldSpell extends AbstractSpell { .with(Trait.AIR, 9) .build(); - private final TargetSelecter targetSelecter = new TargetSelecter(this); + private final TargetSelecter targetSelecter = new TargetSelecter(this).setFilter(this::isValidTarget); private float prevRadius; private float radius; @@ -174,11 +174,8 @@ public class ShieldSpell extends AbstractSpell { } protected long applyEntities(Caster source) { - double radius = this.radius; - Vec3d origin = getOrigin(source); - - targetSelecter.getEntities(source, radius, this::isValidTarget).forEach(i -> { + targetSelecter.getEntities(source, radius).forEach(i -> { try { applyRadialEffect(source, i, i.getPos().distanceTo(origin), radius); } catch (Throwable e) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java index d1730e78..87119eab 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TargetSelecter.java @@ -11,26 +11,31 @@ 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.spell.Spell; -import com.minelittlepony.unicopia.entity.player.Pony; -import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import net.minecraft.entity.Entity; +import net.minecraft.predicate.entity.EntityPredicates; public class TargetSelecter { - private final Map targets = new TreeMap<>(); private final Spell spell; + private BiPredicate, Entity> filter = (a, b) -> true; + public TargetSelecter(Spell spell) { this.spell = spell; } - public Stream getEntities(Caster source, double radius, BiPredicate, Entity> filter) { + public TargetSelecter setFilter(BiPredicate, Entity> filter) { + this.filter = filter; + return this; + } + + public Stream getEntities(Caster source, double radius) { targets.values().removeIf(Target::tick); return source.findAllEntitiesInRange(radius) - .filter(entity -> entity.isAlive() && !entity.isRemoved() && notOwnerOrFriend(spell, source, entity)) + .filter(EntityPredicates.VALID_ENTITY) .filter(EquinePredicates.EXCEPT_MAGIC_IMMUNE) - .filter(e -> filter.test(source, e)) + .filter(entity -> entity != source.asEntity() && validTarget(spell, source, entity) && filter.test(source, entity)) .map(i -> { targets.computeIfAbsent(i.getUuid(), Target::new); return i; @@ -41,35 +46,19 @@ public class TargetSelecter { return targets.values().stream().filter(Target::canHurt).count(); } - public static Predicate notOwnerOrFriend(Affine affine, Caster source) { - return target -> notOwnerOrFriend(affine, source, target); + public static Predicate validTarget(Affine affine, Caster source) { + return target -> validTarget(affine, source, target); } - public static Predicate isOwnerOrFriend(Affine affine, Caster source) { - return target -> isOwnerOrFriend(affine, source, target); - } - - public static boolean notOwnerOrFriend(Affine affine, Caster source, Entity target) { + public static boolean validTarget(Affine affine, Caster source, Entity target) { return !isOwnerOrFriend(affine, source, target); } - public static boolean isOwnerOrFriend(Affine affine, Caster source, Entity target) { - Entity owner = source.getMaster(); - - var equine = Pony.of(target); - if (equine.isPresent() && !affine.isFriendlyTogether(equine.get())) { - return false; - } - - if (affine.isEnemy(source)) { - return FriendshipBraceletItem.isComrade(source, target); - } - - return FriendshipBraceletItem.isComrade(source, target) - || (owner != null && (Pony.equal(target, owner) || owner.isConnectedThroughVehicle(target))); + public static boolean isOwnerOrFriend(Affine affine, Caster source, Entity target) { + return affine.applyInversion(source, source.isOwnerOrFriend(target)); } - static final class Target { + private static final class Target { private int cooldown = 20; Target(UUID id) { } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java index eec0a798..82039a7f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/Creature.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/Creature.java @@ -60,7 +60,7 @@ public class Creature extends Living implements WeaklyOwned.Mutabl private boolean discordedChanged = true; private int smittenTicks; - private final Predicate targetPredicate = TargetSelecter.notOwnerOrFriend(() -> getOriginatingCaster().getAffinity(), this).and(e -> { + private final Predicate targetPredicate = TargetSelecter.validTarget(() -> getOriginatingCaster().getAffinity(), this).and(e -> { return Equine.of(e) .filter(eq -> eq instanceof Creature) .filter(eq -> isDiscorded() != ((Creature)eq).hasCommonOwner(this)) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 666ca9c2..7b1f6ccf 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -362,6 +362,11 @@ public class Pony extends Living implements Copyable, Update return asEntity(); } + @Override + public Optional getMasterId() { + return Optional.of(asEntity().getUuid()); + } + public void onSpawn() { if (entity.getWorld() instanceof ServerWorld sw && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE) { boolean mustAvoidSun = getObservedSpecies() == Race.BAT && MeteorlogicalUtil.isPositionExposedToSun(sw, getOrigin()); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java index f7ee0cde..926581a9 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyClientPlayerEntity.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.entity.player.dummy; +import java.util.Optional; +import java.util.UUID; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,6 +16,7 @@ import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.PlayerListEntry; import net.minecraft.client.render.entity.PlayerModelPart; import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; @@ -79,4 +83,9 @@ public class DummyClientPlayerEntity extends AbstractClientPlayerEntity implemen public void setMaster(PlayerEntity owner) { this.owner = owner; } + + @Override + public Optional getMasterId() { + return Optional.ofNullable(owner).map(Entity::getUuid); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java index 798dc6e1..c5ce8f09 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/dummy/DummyPlayerEntity.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.entity.player.dummy; +import java.util.Optional; +import java.util.UUID; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.InteractionManager; @@ -7,6 +10,7 @@ import com.minelittlepony.unicopia.Owned; import com.mojang.authlib.GameProfile; import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; @@ -42,6 +46,11 @@ public class DummyPlayerEntity extends PlayerEntity implements Owned getMasterId() { + return Optional.ofNullable(owner).map(Entity::getUuid); + } + @Override public boolean shouldRenderName() { return !InteractionManager.instance().isClientPlayer(getMaster()); diff --git a/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java b/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java index da2018d0..4062ae4a 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/FriendshipBraceletItem.java @@ -7,6 +7,7 @@ import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquinePredicates; +import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate; @@ -113,15 +114,10 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem, && ((FriendshipBraceletItem)stack.getItem()).checkSignature(stack, player); } - public static boolean isComrade(Caster caster, Entity entity) { - if (entity instanceof LivingEntity) { - return caster.getMasterId() - .filter(id -> getWornBangles((LivingEntity)entity) - .anyMatch(stack -> isSignedBy(stack, id)) - ) - .isPresent(); - } - return false; + public static boolean isComrade(Owned caster, Entity entity) { + return entity instanceof LivingEntity l && caster.getMasterId() + .filter(id -> getWornBangles(l).anyMatch(stack -> isSignedBy(stack, id))) + .isPresent(); } public static Stream getPartyMembers(Caster caster, double radius) { From 0910ad72cad26e91d134b7b01eb0eaeb8556b3ed Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 26 Jan 2024 15:58:22 +0000 Subject: [PATCH 70/98] Fixed dismissing spells from the gui killing them without going through the normal removal process --- .../unicopia/ability/magic/SpellContainer.java | 7 +++++++ .../client/render/spell/PortalFrameBuffer.java | 6 +----- .../unicopia/network/MsgRemoveSpell.java | 4 ++-- .../unicopia/network/datasync/EffectSync.java | 10 ++++++++++ .../network/datasync/NetworkedReferenceSet.java | 8 +++++++- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java index bbe1ad70..e703911d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/SpellContainer.java @@ -39,6 +39,13 @@ public interface SpellContainer { */ void put(@Nullable Spell effect); + /** + * Cleanly removes a spell from this spell container. + * + * @param spellid ID of the spell to remove. + */ + void remove(UUID spellid); + /** * Removes all active effects that match or contain a matching effect. * diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java index dffa297b..81a99039 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java @@ -76,14 +76,10 @@ class PortalFrameBuffer implements AutoCloseable { private boolean pendingDraw; - private final UUID id; - @Nullable private Frustum frustum; - PortalFrameBuffer(UUID id) { - this.id = id; - } + PortalFrameBuffer(UUID id) { } public void draw(MatrixStack matrices, VertexConsumerProvider vertices) { matrices.translate(0, -0.001, 0); diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgRemoveSpell.java b/src/main/java/com/minelittlepony/unicopia/network/MsgRemoveSpell.java index 37b1e0a4..771b2c73 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgRemoveSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgRemoveSpell.java @@ -10,7 +10,7 @@ import net.minecraft.network.PacketByteBuf; import net.minecraft.server.network.ServerPlayerEntity; /** - * Sent to the server when a player activates an ability. + * Sent to the server when a player dismisses a spell from their dismiss spell screen */ public record MsgRemoveSpell (UUID id) implements HandledPacket { MsgRemoveSpell(PacketByteBuf buffer) { @@ -30,7 +30,7 @@ public record MsgRemoveSpell (UUID id) implements HandledPacket spell.getUuid().equals(id), true); + player.getSpellSlot().remove(id); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java index b2cad93c..83507e6b 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/EffectSync.java @@ -93,6 +93,16 @@ public class EffectSync implements SpellContainer, NbtSerialisable { } } + @Override + public void remove(UUID id) { + Spell spell = spells.getReference(id); + spell.setDead(); + spell.tickDying(owner); + if (spell.isDead()) { + spells.removeReference(id); + } + } + @Override public boolean removeWhere(Predicate test, boolean update) { return reduce(update, (initial, effect) -> { diff --git a/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java b/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java index df54150f..90582e52 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java +++ b/src/main/java/com/minelittlepony/unicopia/network/datasync/NetworkedReferenceSet.java @@ -83,7 +83,13 @@ public class NetworkedReferenceSet { } } - private synchronized void removeReference(UUID id) { + @Nullable + synchronized T getReference(UUID id) { + NetworkedReference i = values.get(id); + return i == null ? null : i.getReference().orElse(null); + } + + synchronized void removeReference(UUID id) { dirty |= ids.remove(id); NetworkedReference i = values.remove(id); if (i != null) { From 7c7ea1e555a9455715952cb3bf9d0ead756322b7 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 26 Jan 2024 16:10:59 +0000 Subject: [PATCH 71/98] Fix build --- .../minelittlepony/unicopia/client/URenderers.java | 2 +- .../client/render/AccessoryFeatureRenderer.java | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index dad19f19..c09bbf8e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -60,10 +60,10 @@ import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import net.minecraft.world.BlockRenderView; +@SuppressWarnings("deprecation") public interface URenderers { BlockEntity CHEST_RENDER_ENTITY = new CloudChestBlock.TileData(BlockPos.ORIGIN, UBlocks.CLOUD_CHEST.getDefaultState()); - @SuppressWarnings("unchecked") static void bootstrap() { ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::new)); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java index 16261ff6..6b6cf861 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java @@ -14,6 +14,7 @@ import net.minecraft.client.model.ModelPart; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.feature.FeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRendererContext; +import net.minecraft.client.render.entity.model.BipedEntityModel; import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.LivingEntity; @@ -23,9 +24,10 @@ public class AccessoryFeatureRenderer< T extends LivingEntity, M extends EntityModel> extends FeatureRenderer { - private static final List> REGISTRY = new ArrayList<>(); + private static final List> REGISTRY = new ArrayList<>(); - public static void register(FeatureFactory...factories) { + @SafeVarargs + public static void register(FeatureFactory>...factories) { for (var factory : factories) { REGISTRY.add(factory); } @@ -36,7 +38,7 @@ public class AccessoryFeatureRenderer< @SuppressWarnings("unchecked") public AccessoryFeatureRenderer(FeatureRendererContext context) { super(context); - features = REGISTRY.stream().map(f -> ((FeatureFactory)f).create(context)).toList(); + features = REGISTRY.stream().map(f -> ((FeatureFactory)f).create(context)).toList(); } @Override @@ -68,8 +70,8 @@ public class AccessoryFeatureRenderer< return cancelled; } - public interface FeatureFactory { - Feature create(FeatureRendererContext> context); + public interface FeatureFactory> { + Feature create(FeatureRendererContext context); } public interface Feature { From 8b7a55d764beb297603d58fb1630583470448467 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 26 Jan 2024 23:30:22 +0000 Subject: [PATCH 72/98] Combine TexturedSphereModel into SphereModel --- .../client/render/model/BakedModel.java | 17 +++++++- .../render/model/TexturedSphereModel.java | 41 ------------------- .../render/spell/PortalFrameBuffer.java | 3 +- 3 files changed, 17 insertions(+), 44 deletions(-) delete mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/model/TexturedSphereModel.java diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java index 694571c3..d2c03914 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java @@ -15,7 +15,7 @@ public class BakedModel { protected final List vertices = new ArrayList<>(); protected void addVertex(Vector4f vertex) { - addVertex(vertex.x, vertex.y, vertex.z, 0, 0); + addVertex(vertex.x, vertex.y, vertex.z, (vertex.x + 1) * 0.5F, (vertex.z + 1) * 0.5F); } protected void addVertex(float x, float y, float z, float u, float v) { @@ -36,4 +36,19 @@ public class BakedModel { } matrices.pop(); } + + public final void render(MatrixStack matrices, VertexConsumer buffer, float scale, float r, float g, float b, float a, float uScale, float vScale) { + scale = Math.abs(scale); + if (scale < 0.001F) { + return; + } + + matrices.push(); + matrices.scale(scale, scale, scale); + for (RenderUtil.Vertex vertex : vertices) { + Vector4f pos = vertex.position(matrices); + buffer.vertex(pos.x, pos.y, pos.z).texture(vertex.u() * uScale, vertex.v() * vScale).color(r, g, b, a).next(); + } + matrices.pop(); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/TexturedSphereModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/TexturedSphereModel.java deleted file mode 100644 index 60c0b74e..00000000 --- a/src/main/java/com/minelittlepony/unicopia/client/render/model/TexturedSphereModel.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.minelittlepony.unicopia.client.render.model; - -import org.joml.Vector4f; - -import com.minelittlepony.unicopia.client.gui.DrawableUtil; -import com.minelittlepony.unicopia.client.render.RenderUtil; - -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.util.math.MatrixStack; - -public class TexturedSphereModel extends BakedModel { - public static final TexturedSphereModel DISK = new TexturedSphereModel(40, 2, DrawableUtil.PI); - - public TexturedSphereModel(double rings, double sectors, double azimuthRange) { - double zenithIncrement = DrawableUtil.PI / rings; - double azimuthIncrement = DrawableUtil.TAU / sectors; - SphereModel.compileVertices(azimuthRange, zenithIncrement, azimuthIncrement, this::addVertex); - } - - @Override - protected void addVertex(Vector4f vertex) { - addVertex(vertex.x, vertex.y, vertex.z, vertex.x, vertex.z); - } - - public final void render(MatrixStack matrices, VertexConsumer buffer, float scale, float r, float g, float b, float a, float uScale, float vScale) { - scale = Math.abs(scale); - if (scale < 0.001F) { - return; - } - - matrices.push(); - matrices.scale(scale, scale, scale); - uScale *= 0.5F; - vScale *= 0.5F; - for (RenderUtil.Vertex vertex : vertices) { - Vector4f pos = vertex.position(matrices); - buffer.vertex(pos.x, pos.y, pos.z).texture((vertex.u() + 1) * uScale, (vertex.v() + 1) * vScale).color(r, g, b, a).next(); - } - matrices.pop(); - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java index 81a99039..f864c098 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java @@ -15,7 +15,6 @@ import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; -import com.minelittlepony.unicopia.client.render.model.TexturedSphereModel; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.mixin.client.MixinMinecraftClient; @@ -96,7 +95,7 @@ class PortalFrameBuffer implements AutoCloseable { RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); RenderSystem._setShaderTexture(0, framebuffer.getColorAttachment()); buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); - TexturedSphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale); + SphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale); tessellator.draw(); client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); From cd9597f117f3b264b85f704da26d4d6809f5e6fd Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 27 Jan 2024 00:21:27 +0000 Subject: [PATCH 73/98] Fix portals placing in the ground --- .../unicopia/ability/magic/spell/effect/PortalSpell.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java index fa2963f5..82a66ad9 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/PortalSpell.java @@ -211,7 +211,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme LivingEntity caster = source.getMaster(); Vec3d targetPos = caster.getRotationVector().multiply(3).add(caster.getEyePos()); parent.setOrientation(pitch, yaw); - entity.setPos(targetPos.x, caster.getEyePos().y - (entity.getHeight() * 0.5F), targetPos.z); + entity.setPos(targetPos.x, Math.abs(pitch) > 15 ? targetPos.y : caster.getPos().y, targetPos.z); } @Override From da3aec8d8359d82e60bad58ea00e125fbd20ebf0 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 27 Jan 2024 00:22:27 +0000 Subject: [PATCH 74/98] Don't render debug info for the first person entity --- .../client/render/spell/SpellEffectsRenderDispatcher.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index cbfdd7dc..37c4c51b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java @@ -17,6 +17,7 @@ import com.minelittlepony.unicopia.entity.Living; import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer.TextLayerType; +import net.minecraft.client.option.Perspective; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; @@ -83,7 +84,9 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader return Operation.SKIP; }, false); - if (client.getEntityRenderDispatcher().shouldRenderHitboxes() && !client.hasReducedDebugInfo()) { + if (client.getEntityRenderDispatcher().shouldRenderHitboxes() + && !client.hasReducedDebugInfo() + && !(caster.asEntity() == client.cameraEntity && client.options.getPerspective() == Perspective.FIRST_PERSON)) { renderHotspot(matrices, vertices, caster, animationProgress); renderSpellDebugInfo(matrices, vertices, caster, light); } From 80500a74c8d21750b6434c7c51ffb506803238c9 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 27 Jan 2024 04:08:56 +0000 Subject: [PATCH 75/98] Improve portal rendering --- .../unicopia/client/URenderers.java | 2 + .../AbstractGeometryBasedParticle.java | 2 +- .../unicopia/client/render/RenderUtil.java | 38 ++++++++++++------- .../client/render/bezier/BezierSegment.java | 8 ++-- .../client/render/model/BakedModel.java | 34 ++++++++++++----- .../client/render/shader/UShaders.java | 29 ++++++++++++++ .../render/spell/PortalFrameBuffer.java | 35 ++++++++++------- .../render/spell/PortalSpellRenderer.java | 4 +- .../core/rendertype_portal_surface.fsh | 20 ++++++++++ .../core/rendertype_portal_surface.json | 20 ++++++++++ .../core/rendertype_portal_surface.vsh | 19 ++++++++++ 11 files changed, 169 insertions(+), 42 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java create mode 100644 src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh create mode 100644 src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json create mode 100644 src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.vsh diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index c09bbf8e..ebe72254 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -23,6 +23,7 @@ import com.minelittlepony.unicopia.client.particle.ShockwaveParticle; import com.minelittlepony.unicopia.client.particle.SphereParticle; import com.minelittlepony.unicopia.client.render.*; import com.minelittlepony.unicopia.client.render.entity.*; +import com.minelittlepony.unicopia.client.render.shader.UShaders; import com.minelittlepony.unicopia.client.render.spell.SpellRendererFactory; import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.item.ChameleonItem; @@ -128,6 +129,7 @@ public interface URenderers { TerraformBoatClientHelper.registerModelLayers(Unicopia.id("palm"), false); SpellRendererFactory.bootstrap(); + UShaders.bootstrap(); } private static void register(DynamicItemRenderer renderer, ItemConvertible...items) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java index 297c2e65..1714d93a 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java @@ -42,7 +42,7 @@ public abstract class AbstractGeometryBasedParticle extends Particle { int light = getBrightness(tickDelta); buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); for (RenderUtil.Vertex corner : corners) { - buffer.vertex(corner.position().x, corner.position().y, corner.position().z).texture(corner.u(), corner.v()).color(red, green, blue, alpha).light(light).next(); + buffer.vertex(corner.position().x, corner.position().y, corner.position().z).texture(corner.texture().x, corner.texture().y).color(red, green, blue, alpha).light(light).next(); } te.draw(); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java index a9cbbd1e..cb43a128 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.client.render; +import org.joml.Matrix4f; import org.joml.Vector3f; import org.joml.Vector4f; @@ -11,17 +12,18 @@ import net.minecraft.client.util.math.MatrixStack; public class RenderUtil { public static final Vector4f TEMP_VECTOR = new Vector4f(); + private static final Vector4f TEMP_UV_VECTOR = new Vector4f(); public static final Vertex[] UNIT_FACE = new Vertex[] { - new Vertex(new Vector3f(0, 0, 0), 1, 1), - new Vertex(new Vector3f(0, 1, 0), 1, 0), - new Vertex(new Vector3f(1, 1, 0), 0, 0), - new Vertex(new Vector3f(1, 0, 0), 0, 1) + new Vertex(0, 0, 0, 1, 1), + new Vertex(0, 1, 0, 1, 0), + new Vertex(1, 1, 0, 0, 0), + new Vertex(1, 0, 0, 0, 1) }; public static final Vertex[] FRAME_BUFFER_VERTICES = new Vertex[] { - new Vertex(new Vector3f(0, 1, 0), 0, 0), - new Vertex(new Vector3f(1, 1, 0), 1, 0), - new Vertex(new Vector3f(1, 0, 0), 1, 1), - new Vertex(new Vector3f(0, 0, 0), 0, 1) + new Vertex(0, 1, 0, 0, 0), + new Vertex(1, 1, 0, 1, 0), + new Vertex(1, 0, 0, 1, 1), + new Vertex(0, 0, 0, 0, 1) }; public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) { @@ -30,17 +32,25 @@ public class RenderUtil { public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light, float uScale, float vScale) { buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); + Matrix4f positionmatrix = matrices.peek().getPositionMatrix(); for (Vertex vertex : UNIT_FACE) { - Vector4f position = vertex.position(matrices); - buffer.vertex(position.x, position.y, position.z).texture(vertex.u() * uScale, vertex.v() * vScale).color(r, g, b, a).light(light).next(); + Vector4f position = vertex.position(positionmatrix); + buffer.vertex(position.x, position.y, position.z).texture(vertex.texture().x * uScale, vertex.texture().y * vScale).color(r, g, b, a).light(light).next(); } te.draw(); } - public record Vertex(Vector3f position, float u, float v) { - public Vector4f position(MatrixStack matrices) { - matrices.peek().getPositionMatrix().transform(TEMP_VECTOR.set(position, 1)); - return TEMP_VECTOR; + public record Vertex(Vector3f position, Vector3f texture) { + public Vertex(float x, float y, float z, float u, float v) { + this(new Vector3f(x, y, z), new Vector3f(u, v, 1)); + } + + public Vector4f position(Matrix4f mat) { + return mat.transform(TEMP_VECTOR.set(position, 1)); + } + + public Vector4f texture(Matrix4f mat) { + return mat.transform(TEMP_UV_VECTOR.set(texture, 1)); } } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java index d8ce8d35..bb30b1f5 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/BezierSegment.java @@ -12,10 +12,10 @@ public record BezierSegment( public BezierSegment(Vector3f from, Vector3f to, float height) { this(new RenderUtil.Vertex[] { - new RenderUtil.Vertex(new Vector3f(from.x, from.y - height/2F, from.z), 0, 0), // bottom left - new RenderUtil.Vertex(new Vector3f(from.x, from.y + height/2F, from.z), 1, 0), // top left - new RenderUtil.Vertex(new Vector3f(to.x, to.y + height/2F, to.z), 1, 1), // top right - new RenderUtil.Vertex(new Vector3f(to.x, to.y - height/2F, to.z), 0, 1) // bottom right + new RenderUtil.Vertex(from.x, from.y - height/2F, from.z, 0, 0), // bottom left + new RenderUtil.Vertex(from.x, from.y + height/2F, from.z, 1, 0), // top left + new RenderUtil.Vertex(to.x, to.y + height/2F, to.z, 1, 1), // top right + new RenderUtil.Vertex(to.x, to.y - height/2F, to.z, 0, 1) // bottom right }); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java index d2c03914..3bceca54 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/BakedModel.java @@ -1,25 +1,35 @@ package com.minelittlepony.unicopia.client.render.model; -import java.util.ArrayList; import java.util.List; -import org.joml.Vector3f; +import org.joml.Matrix4f; import org.joml.Vector4f; import com.minelittlepony.unicopia.client.render.RenderUtil; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.util.math.MatrixStack; public class BakedModel { - protected final List vertices = new ArrayList<>(); + protected final List vertices = new ObjectArrayList<>(); + + private final Matrix4f textureMatrix = new Matrix4f(); + + public Matrix4f getTextureMatrix() { + return textureMatrix; + } + + public void scaleUV(float uScale, float vScale) { + getTextureMatrix().scale(uScale, vScale, 1); + } protected void addVertex(Vector4f vertex) { addVertex(vertex.x, vertex.y, vertex.z, (vertex.x + 1) * 0.5F, (vertex.z + 1) * 0.5F); } protected void addVertex(float x, float y, float z, float u, float v) { - vertices.add(new RenderUtil.Vertex(new Vector3f(x, y, z), u, v)); + vertices.add(new RenderUtil.Vertex(x, y, z, u, v)); } public final void render(MatrixStack matrices, VertexConsumer buffer, int light, int overlay, float scale, float r, float g, float b, float a) { @@ -30,14 +40,17 @@ public class BakedModel { matrices.push(); matrices.scale(scale, scale, scale); + Matrix4f positionmatrix = matrices.peek().getPositionMatrix(); for (RenderUtil.Vertex vertex : vertices) { - Vector4f pos = vertex.position(matrices); - buffer.vertex(pos.x, pos.y, pos.z, r, g, b, a, vertex.u(), vertex.v(), overlay, light, 0, 0, 0); + Vector4f pos = vertex.position(positionmatrix); + Vector4f tex = vertex.texture(textureMatrix); + buffer.vertex(pos.x, pos.y, pos.z, r, g, b, a, tex.x, tex.y, overlay, light, 0, 0, 0); } matrices.pop(); + textureMatrix.identity(); } - public final void render(MatrixStack matrices, VertexConsumer buffer, float scale, float r, float g, float b, float a, float uScale, float vScale) { + public final void render(MatrixStack matrices, VertexConsumer buffer, float scale, float r, float g, float b, float a) { scale = Math.abs(scale); if (scale < 0.001F) { return; @@ -45,10 +58,13 @@ public class BakedModel { matrices.push(); matrices.scale(scale, scale, scale); + Matrix4f positionmatrix = matrices.peek().getPositionMatrix(); for (RenderUtil.Vertex vertex : vertices) { - Vector4f pos = vertex.position(matrices); - buffer.vertex(pos.x, pos.y, pos.z).texture(vertex.u() * uScale, vertex.v() * vScale).color(r, g, b, a).next(); + Vector4f pos = vertex.position(positionmatrix); + Vector4f tex = vertex.texture(textureMatrix); + buffer.vertex(pos.x, pos.y, pos.z).texture(tex.x, tex.y).color(r, g, b, a).next(); } matrices.pop(); + textureMatrix.identity(); } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java b/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java new file mode 100644 index 00000000..fd86786d --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/shader/UShaders.java @@ -0,0 +1,29 @@ +package com.minelittlepony.unicopia.client.render.shader; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.Unicopia; +import net.fabricmc.fabric.api.client.rendering.v1.CoreShaderRegistrationCallback; +import net.minecraft.client.gl.ShaderProgram; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; + +public final class UShaders { + @Nullable + private static Supplier renderTypePortalSurfaceProgram = register("rendertype_portal_surface", VertexFormats.POSITION_COLOR); + + public static ShaderProgram getRenderTypePortalSurfaceProgram() { + return renderTypePortalSurfaceProgram.get(); + } + + public static void bootstrap() { } + + static Supplier register(String name, VertexFormat format) { + AtomicReference holder = new AtomicReference<>(); + CoreShaderRegistrationCallback.EVENT.register(context -> context.register(Unicopia.id(name), format, holder::set)); + return holder::get; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java index f864c098..161d667e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java @@ -15,6 +15,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; +import com.minelittlepony.unicopia.client.render.shader.UShaders; import com.minelittlepony.unicopia.entity.EntityReference; import com.minelittlepony.unicopia.entity.mob.UEntities; import com.minelittlepony.unicopia.mixin.client.MixinMinecraftClient; @@ -30,7 +31,6 @@ import net.minecraft.client.render.BackgroundRenderer; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; -import net.minecraft.client.render.GameRenderer; import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexFormat; @@ -41,6 +41,7 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; import net.minecraft.screen.PlayerScreenHandler; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; class PortalFrameBuffer implements AutoCloseable { @@ -92,10 +93,14 @@ class PortalFrameBuffer implements AutoCloseable { BufferBuilder buffer = tessellator.getBuffer(); float uScale = (float)framebuffer.viewportWidth / (float)framebuffer.textureWidth; float vScale = (float)framebuffer.viewportHeight / (float)framebuffer.textureHeight; - RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); + RenderSystem.setShader(UShaders::getRenderTypePortalSurfaceProgram); + //RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); RenderSystem._setShaderTexture(0, framebuffer.getColorAttachment()); buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); - SphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1, uScale, vScale); + SphereModel.DISK.scaleUV(uScale, vScale); + + RenderSystem.setTextureMatrix(SphereModel.DISK.getTextureMatrix()); + SphereModel.DISK.render(matrices, buffer, 2F, 1, 1, 1, 1); tessellator.draw(); client.getTextureManager().bindTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); @@ -110,11 +115,11 @@ class PortalFrameBuffer implements AutoCloseable { public void build(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { - if (framebuffer != null && System.currentTimeMillis() % 100 != 0) { + if (framebuffer != null && System.currentTimeMillis() % 1 != 0) { return; } - if (pendingDraw && recursionCount > 0) { + if (pendingDraw && recursionCount > 2) { innerBuild(spell, caster, target); return; } @@ -134,7 +139,7 @@ class PortalFrameBuffer implements AutoCloseable { synchronized (client) { pendingDraw = false; - if (recursionCount > 2) { + if (recursionCount > 0) { return; } recursionCount++; @@ -149,10 +154,17 @@ class PortalFrameBuffer implements AutoCloseable { int originalFov = fov.getValue(); fov.setValue(110); + Camera camera = client.gameRenderer.getCamera(); + Entity cameraEntity = UEntities.CAST_SPELL.create(caster.asWorld()); - cameraEntity.setPosition(target.pos()); - cameraEntity.setPitch(spell.getTargetPitch()); - cameraEntity.setYaw(spell.getTargetYaw() + 180); + Vec3d offset = new Vec3d(0, -0.2F, -0.2F).rotateY(-spell.getTargetYaw() * MathHelper.RADIANS_PER_DEGREE); + + float yaw = spell.getTargetYaw() + camera.getYaw() - spell.getYaw() + 180; + float pitch = spell.getTargetPitch() + (camera.getPitch() - spell.getPitch()) * 1.65F; + + cameraEntity.setPosition(target.pos().add(offset)); + cameraEntity.setPitch(pitch); + cameraEntity.setYaw(yaw); drawWorld(cameraEntity, 400, 400); @@ -220,11 +232,8 @@ class PortalFrameBuffer implements AutoCloseable { renderer.scheduleBlockRenders((int)cameraEntity.getX() / 16, (int)cameraEntity.getY() / 16, (int)cameraEntity.getZ() / 16); client.gameRenderer.setRenderHand(false); - MatrixStack matrices = new MatrixStack(); - matrices.scale((float)width / height, 1, 1); - - client.gameRenderer.renderWorld(1, 0, matrices); + client.gameRenderer.renderWorld(1, 0, new MatrixStack()); // Strip transparency RenderSystem.colorMask(false, false, false, true); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java index 4324ef30..a509d1fc 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java @@ -5,7 +5,6 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; import com.minelittlepony.unicopia.entity.EntityReference; - import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.Frustum; @@ -49,6 +48,9 @@ public class PortalSpellRenderer extends SpellRenderer { if (caster.asEntity().distanceTo(client.cameraEntity) > 50) { return; // don't bother rendering if too far away } + if (client.cameraEntity == caster.asEntity()) { + return; + } matrices.push(); matrices.scale(strength, strength, strength); diff --git a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh new file mode 100644 index 00000000..09b197aa --- /dev/null +++ b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh @@ -0,0 +1,20 @@ +#version 150 + +#moj_import + +uniform sampler2D Sampler0; +uniform sampler2D Sampler1; + +uniform vec4 ColorModulator; +uniform float GameTime; +uniform int EndPortalLayers; + +in vec4 texProj0; +in vec4 vertexColor; + +out vec4 fragColor; + +void main() { + vec4 scale = vec4(0.25, 0.25, 0.25, 0.25); + fragColor = textureProj(Sampler0, texProj0 * scale); +} diff --git a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json new file mode 100644 index 00000000..4c978b4f --- /dev/null +++ b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json @@ -0,0 +1,20 @@ +{ + "blend": { + "func": "add", + "srcrgb": "srcalpha", + "dstrgb": "1-srcalpha" + }, + "vertex": "unicopia:rendertype_portal_surface", + "fragment": "unicopia:rendertype_portal_surface", + "attributes": [], + "samplers": [ + { "name": "Sampler0" }, + { "name": "Sampler1" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "GameTime", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "EndPortalLayers", "type": "int", "count": 1, "values": [ 15 ] } + ] +} diff --git a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.vsh b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.vsh new file mode 100644 index 00000000..42aadd7b --- /dev/null +++ b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.vsh @@ -0,0 +1,19 @@ +#version 150 + +#moj_import + +in vec3 Position; +in vec4 Color; + +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; + +out vec4 texProj0; +out vec4 vertexColor; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0); + + vertexColor = Color; + texProj0 = projection_from_position(gl_Position); +} From e385eb564037886281a7be10ca06717e3c64138b Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 27 Jan 2024 04:21:31 +0000 Subject: [PATCH 76/98] Add config options for portal rendering --- src/main/java/com/minelittlepony/unicopia/Config.java | 10 ++++++++++ .../client/render/spell/PortalFrameBuffer.java | 6 ++++-- .../client/render/spell/PortalSpellRenderer.java | 5 ++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Config.java b/src/main/java/com/minelittlepony/unicopia/Config.java index c0f86374..3a128a0e 100644 --- a/src/main/java/com/minelittlepony/unicopia/Config.java +++ b/src/main/java/com/minelittlepony/unicopia/Config.java @@ -45,6 +45,16 @@ public class Config extends com.minelittlepony.common.util.settings.Config { .addComment("Removes butterflies from spawning in your world") .addComment("Turn this ON if you have another mod that adds butterflies."); + public final Setting simplifiedPortals = value("compatibility", "simplifiedPortals", false) + .addComment("Disables dynamic portal rendering"); + + public final Setting fancyPortalRefreshRate = value("client", "fancyPortalRefreshRate", -1L) + .addComment("Sets the refresh rate of portals when using fancy portal rendering") + .addComment("Set to -1 (default) for unlimited"); + + public final Setting maxPortalRecursion = value("client", "maxPortalRecursion", 2) + .addComment("Sets the maximum depth to reach when rendering portals through portals"); + public Config() { super(new HeirarchicalJsonConfigAdapter(new GsonBuilder() .registerTypeAdapter(Race.class, RegistryTypeAdapter.of(Race.REGISTRY)) diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java index 161d667e..823220f5 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalFrameBuffer.java @@ -11,6 +11,7 @@ import org.joml.Matrix4f; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; @@ -115,11 +116,12 @@ class PortalFrameBuffer implements AutoCloseable { public void build(PortalSpell spell, Caster caster, EntityReference.EntityValues target) { - if (framebuffer != null && System.currentTimeMillis() % 1 != 0) { + long refreshRate = Unicopia.getConfig().fancyPortalRefreshRate.get(); + if (refreshRate > 0 && framebuffer != null && System.currentTimeMillis() % refreshRate != 0) { return; } - if (pendingDraw && recursionCount > 2) { + if (pendingDraw && recursionCount > Math.max(0, Unicopia.getConfig().maxPortalRecursion.get())) { innerBuild(spell, caster, target); return; } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java index a509d1fc..8881834e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/spell/PortalSpellRenderer.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.client.render.spell; +import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.effect.PortalSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; @@ -14,8 +15,6 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.RotationAxis; public class PortalSpellRenderer extends SpellRenderer { - static boolean FANCY_PORTAlS = true; - @Override public boolean shouldRenderEffectPass(int pass) { return pass == 0; @@ -32,7 +31,7 @@ public class PortalSpellRenderer extends SpellRenderer { SphereModel.DISK.render(matrices, buff, light, 0, 2F * strength, 1, 1, 1, 1); matrices.pop(); - if (!FANCY_PORTAlS || !spell.isLinked()) { + if (Unicopia.getConfig().simplifiedPortals.get() || !spell.isLinked()) { matrices.push(); matrices.translate(0, -0.02, 0); matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180)); From e06ae63077d33c03fc2bf2b852fdd2b0e546acbe Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 27 Jan 2024 21:17:16 +0000 Subject: [PATCH 77/98] Add time-limited variants of the metamorphosis potions and add make all potions craftable --- .../unicopia/ability/ChangeFormAbility.java | 4 +- .../effect/MetamorphosisStatusEffect.java | 60 ++++++++++++ .../entity/effect/RaceChangeStatusEffect.java | 35 ++++--- .../unicopia/entity/effect/UPotions.java | 67 ++++++++++--- .../unicopia/entity/mob/UTradeOffers.java | 20 ---- .../unicopia/entity/player/Pony.java | 5 +- .../resources/assets/unicopia/lang/en_us.json | 88 +++++++++++++++++- .../mob_effect/change_race_hippogriff.png | Bin 0 -> 6551 bytes .../textures/mob_effect/change_race_kirin.png | Bin 0 -> 6550 bytes .../textures/mob_effect/morph_race_bat.png | Bin 0 -> 4462 bytes .../mob_effect/morph_race_changeling.png | Bin 0 -> 4377 bytes .../textures/mob_effect/morph_race_earth.png | Bin 0 -> 4414 bytes .../mob_effect/morph_race_hippogriff.png | Bin 0 -> 4654 bytes .../textures/mob_effect/morph_race_kirin.png | Bin 0 -> 4692 bytes .../mob_effect/morph_race_pegasus.png | Bin 0 -> 4428 bytes .../mob_effect/morph_race_unicorn.png | Bin 0 -> 4397 bytes 16 files changed, 224 insertions(+), 55 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/effect/MetamorphosisStatusEffect.java create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/change_race_hippogriff.png create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/change_race_kirin.png create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/morph_race_bat.png create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/morph_race_changeling.png create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/morph_race_earth.png create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/morph_race_hippogriff.png create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/morph_race_kirin.png create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/morph_race_pegasus.png create mode 100644 src/main/resources/assets/unicopia/textures/mob_effect/morph_race_unicorn.png diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ChangeFormAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ChangeFormAbility.java index 4c841b1a..64fabfa4 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/ChangeFormAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/ChangeFormAbility.java @@ -76,9 +76,9 @@ public class ChangeFormAbility implements Ability { targets.forEach(target -> { Race supressed = target.getSuppressedRace(); if (target == player || supressed.isUnset() == isTransforming) { - Race actualRace = target.getSpecies(); + Race actualRace = isTransforming ? target.getSpecies() : Race.UNSET; target.setSpecies(supressed.or(player.getCompositeRace().potential())); - target.setSuppressedRace(isTransforming ? actualRace : Race.UNSET); + target.setSuppressedRace(actualRace); } }); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/MetamorphosisStatusEffect.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/MetamorphosisStatusEffect.java new file mode 100644 index 00000000..56accf6f --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/MetamorphosisStatusEffect.java @@ -0,0 +1,60 @@ +package com.minelittlepony.unicopia.entity.effect; + +import java.util.HashMap; +import java.util.Map; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.Race; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.effect.StatusEffect; +import net.minecraft.entity.effect.StatusEffectCategory; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +public class MetamorphosisStatusEffect extends StatusEffect { + public static final int MAX_DURATION = 20 * 60; + + private static final Map REGISTRY = new HashMap<>(); + + public static final StatusEffect EARTH = register(0x886F0F, Race.EARTH); + public static final StatusEffect UNICORN = register(0x88FFFF, Race.UNICORN); + public static final StatusEffect PEGASUS = register(0x00C0ff, Race.PEGASUS); + public static final StatusEffect BAT = register(0x152F13, Race.BAT); + public static final StatusEffect CHANGELING = register(0xFFFF00, Race.CHANGELING); + public static final StatusEffect KIRIN = register(0xFF8800, Race.KIRIN); + public static final StatusEffect HIPPOGRIFF = register(0xE04F77, Race.HIPPOGRIFF); + + @Nullable + public static StatusEffect forRace(Race race) { + return REGISTRY.get(race); + } + + public static StatusEffect register(int color, Race race) { + Identifier id = Race.REGISTRY.getId(race); + StatusEffect effect = new MetamorphosisStatusEffect(color, race); + REGISTRY.put(race, effect); + return Registry.register(Registries.STATUS_EFFECT, + id.withPath(p -> "morph_race_" + p), + effect + ); + } + + public static Race getEffectiveRace(LivingEntity entity, Race fallback) { + return entity.getStatusEffects().stream().filter(effect -> effect.getEffectType() instanceof MetamorphosisStatusEffect).map(effect -> { + return ((MetamorphosisStatusEffect)effect.getEffectType()).getRace(); + }).findFirst().orElse(fallback); + } + + private final Race race; + + private MetamorphosisStatusEffect(int color, Race race) { + super(StatusEffectCategory.NEUTRAL, color); + this.race = race; + } + + public Race getRace() { + return race; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java index 462936ad..210395b9 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/RaceChangeStatusEffect.java @@ -1,5 +1,8 @@ package com.minelittlepony.unicopia.entity.effect; +import java.util.HashMap; +import java.util.Map; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.Race; @@ -28,25 +31,31 @@ public class RaceChangeStatusEffect extends StatusEffect { public static final int STAGE_DURATION = 200; public static final int MAX_DURATION = Stage.VALUES.length * STAGE_DURATION + 1; - public static final StatusEffect CHANGE_RACE_EARTH = register(0x886F0F, Race.EARTH); - public static final StatusEffect CHANGE_RACE_UNICORN = register(0x88FFFF, Race.UNICORN); - public static final StatusEffect CHANGE_RACE_PEGASUS = register(0x00FFFF, Race.PEGASUS); - public static final StatusEffect CHANGE_RACE_BAT = register(0x0FFF00, Race.BAT); - public static final StatusEffect CHANGE_RACE_CHANGELING = register(0xFFFF00, Race.CHANGELING); - public static final StatusEffect CHANGE_RACE_KIRIN = register(0xFF8800, Race.KIRIN); - public static final StatusEffect CHANGE_RACE_HIPPOGRIFF = register(0x00FFFF, Race.HIPPOGRIFF); + private static final Map REGISTRY = new HashMap<>(); + + @Nullable + public static StatusEffect forRace(Race race) { + return REGISTRY.get(race); + } + + public static final StatusEffect EARTH = register(0x886F0F, Race.EARTH); + public static final StatusEffect UNICORN = register(0x88FFFF, Race.UNICORN); + public static final StatusEffect PEGASUS = register(0x00C0ff, Race.PEGASUS); + public static final StatusEffect BAT = register(0x152F13, Race.BAT); + public static final StatusEffect CHANGELING = register(0xFFFF00, Race.CHANGELING); + public static final StatusEffect KIRIN = register(0xFF8800, Race.KIRIN); + public static final StatusEffect HIPPOGRIFF = register(0xE04F77, Race.HIPPOGRIFF); private final Race race; public static StatusEffect register(int color, Race race) { - Identifier id = Race.REGISTRY.getId(race); - return Registry.register(Registries.STATUS_EFFECT, - new Identifier(id.getNamespace(), "change_race_" + id.getPath().toLowerCase()), - new RaceChangeStatusEffect(color, race) - ); + Identifier id = race.getId(); + StatusEffect effect = new RaceChangeStatusEffect(color, race); + REGISTRY.put(race, effect); + return Registry.register(Registries.STATUS_EFFECT, id.withPath(p -> "change_race_" + id.getPath()), effect); } - public RaceChangeStatusEffect(int color, Race race) { + private RaceChangeStatusEffect(int color, Race race) { super(StatusEffectCategory.NEUTRAL, color); this.race = race; } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java index 82f5a0b3..2fb07320 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/UPotions.java @@ -1,29 +1,70 @@ package com.minelittlepony.unicopia.entity.effect; -import java.util.ArrayList; -import java.util.List; +import java.util.Objects; +import org.spongepowered.include.com.google.common.base.Preconditions; + +import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.item.UItems; +import net.minecraft.entity.effect.StatusEffect; import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.item.Item; +import net.minecraft.item.Items; import net.minecraft.potion.Potion; +import net.minecraft.potion.Potions; +import net.minecraft.recipe.BrewingRecipeRegistry; import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; import net.minecraft.registry.Registries; public interface UPotions { - List REGISTRY = new ArrayList<>(); - - Potion TRIBE_SWAP_EARTH_PONY = register("tribe_swap_earth", new Potion("unicopia.tribe_swap_earth", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_EARTH, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_UNICORN = register("tribe_swap_unicorn", new Potion("unicopia.tribe_swap_unicorn", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_UNICORN, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_PEGASUS = register("tribe_swap_pegasus", new Potion("unicopia.tribe_swap_pegasus", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_PEGASUS, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_BAT = register("tribe_swap_bat", new Potion("unicopia.tribe_swap_bat", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_BAT, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_CHANGELING = register("tribe_swap_changeling", new Potion("unicopia.tribe_swap_changeling", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_CHANGELING, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_KIRIN = register("tribe_swap_kirin", new Potion("unicopia.tribe_swap_kirin", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_KIRIN, RaceChangeStatusEffect.MAX_DURATION))); - Potion TRIBE_SWAP_HIPPOGRIFF = register("tribe_swap_hippogriff", new Potion("unicopia.tribe_swap_hippogriff", new StatusEffectInstance(RaceChangeStatusEffect.CHANGE_RACE_HIPPOGRIFF, RaceChangeStatusEffect.MAX_DURATION))); + MorphingPotion MORPH_EARTH_PONY = new MorphingPotion(Race.EARTH).registerBaseRecipes(Potions.STRENGTH, UItems.CURING_JOKE); + MorphingPotion MORPH_UNICORN = new MorphingPotion(Race.UNICORN).registerBaseRecipes(Potions.REGENERATION, UItems.BOTCHED_GEM); + MorphingPotion MORPH_PEGASUS = new MorphingPotion(Race.PEGASUS).registerBaseRecipes(Potions.SWIFTNESS, UItems.PEGASUS_FEATHER, UItems.HIPPOGRIFF_BADGE, Items.FEATHER); + MorphingPotion MORPH_BAT = new MorphingPotion(Race.BAT).registerBaseRecipes(Potions.NIGHT_VISION, UItems.BUTTERFLY); + MorphingPotion MORPH_CHANGELING = new MorphingPotion(Race.CHANGELING).registerBaseRecipes(Potions.HARMING, UItems.CARAPACE); + MorphingPotion MORPH_KIRIN = new MorphingPotion(Race.KIRIN).registerBaseRecipes(Potions.FIRE_RESISTANCE, Items.MAGMA_CREAM); + MorphingPotion MORPH_HIPPOGRIFF = new MorphingPotion(Race.HIPPOGRIFF).registerBaseRecipes(Potions.WATER_BREATHING, UItems.CLAM_SHELL, UItems.TURRET_SHELL, UItems.SCALLOP_SHELL); static Potion register(String name, Potion potion) { - REGISTRY.add(potion); - return Registry.register(Registries.POTION, Unicopia.id(name), potion); + return register(Unicopia.id(name), potion); + } + + static Potion register(Identifier id, Potion potion) { + return Registry.register(Registries.POTION, id, potion); + } + + static void addRecipe(Potion result, Potion basePotion, Item...items) { + Preconditions.checkArgument(BrewingRecipeRegistry.isBrewable(basePotion), "Base potion is not craftable. " + Registries.POTION.getId(basePotion) + " required for crafting " + Registries.POTION.getId(result)); + for (Item item : items) { + BrewingRecipeRegistry.registerPotionRecipe(basePotion, item, result); + } + } + + record MorphingPotion(Identifier id, Potion shortEffect, Potion longEffect, Potion permanentEffect) { + public MorphingPotion(Race race) { + this(race.getId(), + Objects.requireNonNull(MetamorphosisStatusEffect.forRace(race), "No metamorphosis status effect registered for " + race.getId()), + Objects.requireNonNull(RaceChangeStatusEffect.forRace(race), "No race change status effect registered for " + race.getId()) + ); + } + + public MorphingPotion(Identifier id, StatusEffect morphEffect, StatusEffect permanentEffect) { + this(id, + register(id.withPath(p -> "short_morph_" + p), new Potion(id.getNamespace() + ".short_morph_" + id.getPath(), new StatusEffectInstance(morphEffect, MetamorphosisStatusEffect.MAX_DURATION))), + register(id.withPath(p -> "long_morph_" + p), new Potion(id.getNamespace() + ".long_morph_" + id.getPath(), new StatusEffectInstance(morphEffect, MetamorphosisStatusEffect.MAX_DURATION * 10))), + register(id, new Potion(id.getNamespace() + ".tribe_swap_" + id.getPath(), new StatusEffectInstance(permanentEffect, RaceChangeStatusEffect.MAX_DURATION))) + ); + } + + public MorphingPotion registerBaseRecipes(Potion basePotion, Item...items) { + addRecipe(shortEffect, basePotion, items); + addRecipe(longEffect, shortEffect, Items.REDSTONE); + addRecipe(permanentEffect, longEffect, UItems.CURING_JOKE); + return this; + } } static void bootstrap() { diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java index cedc1064..ff73a2f8 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java @@ -4,7 +4,6 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; -import com.minelittlepony.unicopia.entity.effect.UPotions; import com.minelittlepony.unicopia.item.EnchantableItem; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.util.RegistryUtils; @@ -14,7 +13,6 @@ import net.minecraft.entity.Entity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.potion.PotionUtil; import net.minecraft.registry.tag.ItemTags; import net.minecraft.registry.tag.TagKey; import net.minecraft.util.Util; @@ -22,7 +20,6 @@ import net.minecraft.util.math.random.Random; import net.minecraft.village.TradeOffer; import net.minecraft.village.TradeOffers; import net.minecraft.village.VillagerProfession; -import net.minecraft.village.TradeOffers.Factory; public interface UTradeOffers { static void bootstrap() { @@ -59,10 +56,6 @@ public interface UTradeOffers { factories.add(buy(UItems.CRYSTAL_HEART, 1, UItems.MUSIC_DISC_CRUSADE, 1, 10, 6, 0.08F)); factories.add(buy(UItems.PEGASUS_AMULET, 1, UItems.ALICORN_AMULET, 1, 2, 6, 0.05F)); factories.add(buyForEmeralds(UItems.FRIENDSHIP_BRACELET, 2, 1, 10, 7, 0.17F)); - factories.add(new SellPotionHoldingItemFactory( - new Item[] { Items.ARROW, Items.GLASS_BOTTLE, Items.GLASS_BOTTLE }, - new Item[] { Items.TIPPED_ARROW, Items.POTION, Items.SPLASH_POTION }, - 5, 5, 2, 12, 30)); }); } @@ -105,17 +98,4 @@ public interface UTradeOffers { return new TradeOffer(offer.getOriginalFirstBuyItem(), offer.getSecondBuyItem(), UItems.FILLED_JAR.withContents(offer.getSellItem()), offer.getUses(), offer.getMaxUses(), offer.getMerchantExperience(), offer.getPriceMultiplier(), offer.getDemandBonus()); } } - - record SellPotionHoldingItemFactory (Item[] secondBuy, Item[] tippedArrow, int secondCount, int sellCount, int price, int maxUses, int experience) implements Factory { - @Override - public TradeOffer create(Entity entity, Random random) { - int index = random.nextInt(tippedArrow.length); - return new TradeOffer( - new ItemStack(Items.EMERALD, price), - new ItemStack(secondBuy[index], secondCount), - PotionUtil.setPotion(new ItemStack(tippedArrow[index], sellCount), UPotions.REGISTRY.get(random.nextInt(UPotions.REGISTRY.size()))), - maxUses, experience, 0.05f - ); - } - } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 7b1f6ccf..fab87347 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -21,6 +21,7 @@ import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.entity.*; import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance; import com.minelittlepony.unicopia.entity.duck.LivingEntityDuck; +import com.minelittlepony.unicopia.entity.effect.MetamorphosisStatusEffect; import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect; import com.minelittlepony.unicopia.entity.effect.UEffects; import com.minelittlepony.unicopia.entity.mob.UEntityAttributes; @@ -476,13 +477,13 @@ public class Pony extends Living implements Copyable, Update private void recalculateCompositeRace() { Race intrinsicRace = getSpecies(); Race suppressedRace = getSuppressedRace(); - compositeRace = getSpellSlot() + compositeRace = MetamorphosisStatusEffect.getEffectiveRace(entity, getSpellSlot() .get(SpellPredicate.IS_MIMIC, true) .map(AbstractDisguiseSpell::getDisguise) .map(EntityAppearance::getAppearance) .flatMap(Pony::of) .map(Pony::getSpecies) - .orElse(intrinsicRace).composite( + .orElse(intrinsicRace)).composite( AmuletSelectors.UNICORN_AMULET.test(entity) ? Race.UNICORN : AmuletSelectors.ALICORN_AMULET.test(entity) ? Race.ALICORN : null, diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index d516f4d9..39d28398 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -352,6 +352,19 @@ "effect.unicopia.butter_fingers": "Butterfingers", "effect.unicopia.change_race_earth": "Earth Pony Metamorphosis", + "effect.unicopia.change_race_pegasus": "Pegasus Metamorphosis", + "effect.unicopia.change_race_changeling": "Changeling Metamorphosis", + "effect.unicopia.change_race_bat": "Bat Pony Metamorphosis", + "effect.unicopia.change_race_kirin": "Kirin Metamorphosis", + "effect.unicopia.change_race_hippogriff": "Hippogriff Metamorphosis", + + "effect.unicopia.morph_race_earth": "Earth Pony Transformation", + "effect.unicopia.morph_race_pegasus": "Pegasus Transformation", + "effect.unicopia.morph_race_changeling": "Changeling Transformation", + "effect.unicopia.morph_race_bat": "Bat Pony Transformation", + "effect.unicopia.morph_race_kirin": "Kirin Transformation", + "effect.unicopia.morph_race_hippogriff": "Hippogriff Transformation", + "item.minecraft.potion.effect.unicopia.tribe_swap_earth": "Potion of Earth Pony Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_earth": "Splash Potion of Earth Pony Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_earth": "Lingering Potion of Earth Pony Metamorphosis", @@ -363,36 +376,101 @@ "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_unicorn": "Lingering Potion of Unicorn Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_unicorn": "Arrow of Unicorn Metamorphosis", - "effect.unicopia.change_race_pegasus": "Pegasus Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_pegasus": "Potion of Pegasus Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_pegasus": "Splash Potion of Pegasus Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_pegasus": "Lingering Potion of Pegasus Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_pegasus": "Arrow of Pegasus Metamorphosis", - "effect.unicopia.change_race_changeling": "Changeling Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_changeling": "Potion of Changeling Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_changeling": "Splash Potion of Changeling Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_changeling": "Lingering Potion of Changeling Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_changeling": "Arrow of Changeling Metamorphosis", - "effect.unicopia.change_race_bat": "Bat Pony Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_bat": "Potion of Bat Pony Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_bat": "Splash Potion of Bat Pony Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_bat": "Lingering Potion of Bat Pony Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_bat": "Arrow of Bat Pony Metamorphosis", - "effect.unicopia.change_race_kirin": "Kirin Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_kirin": "Potion of Kirin Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_kirin": "Splash Potion of Kirin Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_kirin": "Lingering Potion of Kirin Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_kirin": "Arrow of Kirin Metamorphosis", - "effect.unicopia.change_race_hippogriff": "Hippogriff Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_hippogriff": "Potion of Hippogriff Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_hippogriff": "Splash Potion of Hippogriff Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_hippogriff": "Lingering Potion of Hippogriff Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_hippogriff": "Arrow of Hippogriff Metamorphosis", + "item.minecraft.potion.effect.unicopia.short_morph_earth": "Potion of Earth Pony Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_earth": "Splash Potion of Earth Pony Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_earth": "Lingering Potion of Earth Pony Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_earth": "Arrow of Earth Pony Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_unicorn": "Potion of Unicorn Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_unicorn": "Splash Potion of Unicorn Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_unicorn": "Lingering Potion of Unicorn Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_unicorn": "Arrow of Unicorn Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_pegasus": "Potion of Pegasus Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_pegasus": "Splash Potion of Pegasus Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_pegasus": "Lingering Potion of Pegasus Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_pegasus": "Arrow of Pegasus Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_changeling": "Potion of Changeling Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_changeling": "Splash Potion of Changeling Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_changeling": "Lingering Potion of Changeling Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_changeling": "Arrow of Changeling Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_bat": "Potion of Bat Pony Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_bat": "Splash Potion of Bat Pony Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_bat": "Lingering Potion of Bat Pony Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_bat": "Arrow of Bat Pony Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_kirin": "Potion of Kirin Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_kirin": "Splash Potion of Kirin Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_kirin": "Lingering Potion of Kirin Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_kirin": "Arrow of Kirin Short Transformation", + + "item.minecraft.potion.effect.unicopia.short_morph_hippogriff": "Potion of Hippogriff Short Transformation", + "item.minecraft.splash_potion.effect.unicopia.short_morph_hippogriff": "Splash Potion of Hippogriff Short Transformation", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_hippogriff": "Lingering Potion of Hippogriff Short Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_hippogriff": "Arrow of Hippogriff Short Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_earth": "Potion of Earth Pony Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_earth": "Splash Potion of Earth Pony Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_earth": "Lingering Potion of Earth Pony Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_earth": "Arrow of Earth Pony Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_unicorn": "Potion of Unicorn Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_unicorn": "Splash Potion of Unicorn Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_unicorn": "Lingering Potion of Unicorn Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_unicorn": "Arrow of Unicorn Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_pegasus": "Potion of Pegasus Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_pegasus": "Splash Potion of Pegasus Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_pegasus": "Lingering Potion of Pegasus Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_pegasus": "Arrow of Pegasus Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_changeling": "Potion of Changeling Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_changeling": "Splash Potion of Changeling Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_changeling": "Lingering Potion of Changeling Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_changeling": "Arrow of Changeling Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_bat": "Potion of Bat Pony Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_bat": "Splash Potion of Bat Pony Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_bat": "Lingering Potion of Bat Pony Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_bat": "Arrow of Bat Pony Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_kirin": "Potion of Kirin Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_kirin": "Splash Potion of Kirin Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_kirin": "Lingering Potion of Kirin Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_kirin": "Arrow of Kirin Long Transformation", + + "item.minecraft.potion.effect.unicopia.long_morph_hippogriff": "Potion of Hippogriff Long Transformation", + "item.minecraft.splash_potion.effect.unicopia.long_morph_hippogriff": "Splash Potion of Hippogriff Long Transformation", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_hippogriff": "Lingering Potion of Hippogriff Long Transformation", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_hippogriff": "Arrow of Hippogriff Long Transformation", + "potion.withChance": "1 in %s chance of %s", "potion.potency.6": "VII", diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/change_race_hippogriff.png b/src/main/resources/assets/unicopia/textures/mob_effect/change_race_hippogriff.png new file mode 100644 index 0000000000000000000000000000000000000000..057c22409e21fdc91613ef0bc6383267b36b0e52 GIT binary patch literal 6551 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/change_race_kirin.png b/src/main/resources/assets/unicopia/textures/mob_effect/change_race_kirin.png new file mode 100644 index 0000000000000000000000000000000000000000..33be07ce36037ad660129e341a1d821a5991f4fc GIT binary patch literal 6550 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_bat.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_bat.png new file mode 100644 index 0000000000000000000000000000000000000000..77ef99b8aef63890e68f52aef831982b45c99e55 GIT binary patch literal 4462 zcmeHKYj6|S6<*2pOJF8rZ1NyeFG~_*6KNloEbZE|4U&vy5U57uxb8r`k1Mf7(hBX` zl3@~Fv13XCG%=3`ho%h)4NgKS0j4;FSDTPD)I6pn>A1!5v;jgO5J;SqhV<@AmQ9kG zj%S*GUd`1#d+#~lJ@-539_g;}&YhKGxW|AXNRGS4SqJ+&)RvV2zYDH^&BE>^XmI=8 z?qVbhzCcag4-lQI1DkGytkX>hC^wp=KyI%>N|m#+1XLwRyi?u7ERq>diSq)w0I zz`hGM3d(t~-wB)eSYoZ$p{zbx+NTCeD)LZ2%kA-^l!+uvC8f}wpv(+GGL#i1X@;;d z1Oc^m&klQ}BAx7hBNL)LDmolqx5I%(qLN<>0tAV7J^TxM%`tCY+r0UEvHZtc?r1*l zedgoeVCx#&FkjZ*ncWi}z`t7kK#u$8YoF_Hj9d-u{-}T8rS8?;Xx6P2wlZ7Mftodg z_qR9zYme-I{mP45 zKQjGg℘-dg&31M51VJFLmtIHP((D*3jzf6-zod3LC%ZiuY|Fms=TKaP5mHHkTdl ze`?Cs-%n2$v)lAnAE`dqnTGAJI`_17|5-d;G^8k5fV-MTEE z_$0G%MYQ9>zjsGr4#q`agVNxc!|+nb#0nAzOz}`e4H!~h8IQ31VxXWL2#8@j_DRnn z3>5`C_K?Lxcp?t4NUT{J1@o8A_3=v=^R$3fRv60T3s1*q9%HW5ZwTx_AR3Ikdm75q${vpNX@-|W~TMTsyt9*f0Hu@aLM z4d5hA(>Oul6lH`OM!7YtuyJEpo~lA5Fq}ZwAp@?P$$-Yff5&gqt-R7G*qituTBhWmBdz<#q z-Zf>YZ5-}}aYO;DNKv081?^a>>ZYbW@Hh#q!a7+6 zK+sej_6^nH=>!3*@vy!mi2m09rL7(wv^){=8c~Mpw`xO)6Ezl^v=1 zv5X+&CaO(`Jl%B{9L!YQ9#`fC-Q9P9Ww1e#Sepmm`ijFKdd|Kf;6+4*MS(n?U zLpEJ*YH1X6m)X6wZ=D|e;D&xd{U7F+ z74O`El9x8q(_`)Liy-^$y7#vIZrdUD{RwLi|8-%3w0C^#smZa-iPr|4;daNDH=UWg Z3O;@DTJ9UU85Cp+al7U^Uzypo>c4gZ2uc6| literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_changeling.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_changeling.png new file mode 100644 index 0000000000000000000000000000000000000000..c29bf471b8754cd9c52b60e38fd92cb2bca9b448 GIT binary patch literal 4377 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 Rr@&1tSg|m$XI@?Fe*k%C_~QTo literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_hippogriff.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_hippogriff.png new file mode 100644 index 0000000000000000000000000000000000000000..f52bfd19031f5c4ad992f80a4f6164d04697972b GIT binary patch literal 4654 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_pegasus.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_pegasus.png new file mode 100644 index 0000000000000000000000000000000000000000..56b4c3e04c5c54c19995129b55e4ba2e4ed44f22 GIT binary patch literal 4428 zcmeHLeNa@_72m}mNTB0j6eCG^J{nQt+xM{_Z}-W9$bxLbDp7`zOq%ZdxVyZO-M7Ad zz(U#tLJ8Us^P!HJ1QV)_nTTUziO`T#QQO8^iET4u(;26t(`hgte#ACPY@p}8<+II9 zhneO--p<{7-nr-e?zz8v?t!~o%h#<T=>qqKFUM)2UO(^ zc)hu%WOxBf^KB-xZUfCcMNTl!G0g$p)6mjE)@}5*#GGjK?I354$vZ$^K4y~uGHp&U zr9s~TjRE-%==VdD+v0N_0a<^NjZY~^I`VkG*6S%#X=VU}WQrBV6-vH$&a#2yax2Onyr%1W%gAq9mZf=r`pn+JM?hBZGy#pGvG!X3)h8CJrvlGm2rn}wcz5iJ6k;`ws z@~P#+?tk4HIq-yCp)oA|40Gb0t?ZUJ*ig&${LMRdN;_|M#LoTv_Vfbv2iI;s8~r0{ z4wej*HNNV+f90Z8RhRsgjQ!$@i!bfJw1bq~l`duT*BjP5?0{9@Iw z)4#u^yu9eng&%ME=-_r?sqd3{S)8Sz+BbgHK+4yr+^^k3W!- z({wrGgW<)AYu;Pkec5^bgKZz>nV&k-zw@tOJ(SshI_J_qdls!qnA_9*%x69OJ~!`L z)jzm>=_|juFHxS?nsD`rlJh&};JvHQzs&ZYb*;;4N*&0popaY+31ac7$8I##E1#Sj z`9dDJl9bb=ZW;RekP0~%lPjvUDqk5VDj^FmDFU*@LJ@t#OnC*d2rq6z8YZBi9CqVZ z-aCq8vgF1evim4sqzF~ZrHv}u*to7jY}_QeB)lL$B`?MSKnQ6(77Nvcqg>358@L?E zx|+l>!$jNU#;YKYiWC)LHjB+d5ydgNj=}R&usl@?a2q@&V-WD>#;Y|g!jWWseZ8gL zYEjf6NxNJwl43}PA;5x&HiR`kMuel;Iz$}9gQB7;M>JUpV>%`;D7Bg!$6*{Bk1rJQ z`6l4Q(J>Z)4>HC_NZLY?p%6LMBdQhG0mxWD-|7*qXow(k1BxoOs)&l~P*}^J3L%LT z{*hX>##oLdlBfoSz%&Z8($l8YPuzruPC-x(MGP+>dzz&t2PVmy7Mng|EN5yU;68yn z&3b(8hA~+Ae4IxSYxVHF9yhMf&q<0XOI%!ZIvg}FA%d11R>H=@e(+XWA{+q}Kn}s_ zvQoknC~r8b@nI3^PylX`0f*vUlq85ec+(7Fvr10FX%|?+Do|FN=yFgLLQ^2tt1@II zUo$l-9ZCWy+96W5fIt%g(JBx&yWLI*f@Ft~SO;QxXTUDTp(K$jQPdC*+bM_mAR;5- zpfQI|I9FKib>obMn$(on@LB*I+<2KBu8mC&RLCK;QR8(sX_j(Wos6BP7$<9U*e8uD zks5_e)G>|PX^iOG!ogyISYFRl05J5h7OqG|yr!rXic;goC#vqmun&%t#B02V*AReC z)ZvQpI(#2Raa25f9nPmna-iXVS?h-f%L9AZ@=`ep^EVi(_=(zxHpk!MuQjr9lrYRV z6dW(cmk{OakYvOOSn(mTnhysNRFAQA9oNg>k_wEAv0803OGs>hC2WGk5(3L30?{_s zDmWN{W!UNHQ6-?&^C~I~0*}BIq^I#03R`Y$)QaiW_0>q<1t5%|Xo6w?lQ42@!K7X@ z##fw2{ud{ChQX8~1AcKG6fdZS@pOkSr@82$^YhszSbuXPwMT62X5)_P1|nL15X6rEEpa6;qdh<1^K@!ONHn0<6kU(wDO+go!6TGJUTj>b1HFY$@-%=uVp=K{hj6G z8((IeXxUY8;h>-Q`}_AdtpDZRC)_K0_I7*KV@Gx!9en4_+miM!&(6+Xke|`N`cJ1a zM_)+WoE~gxODxRjxAnf-aO<_^=H_GGj%T53E-dWW-CUi~-?ZJCc`kELdwaX*-6qgq zxb}5oYEon0g%8_))1;$4cnHgUslW5U$`p3~-+#16yl|%Xxzv**ZSu0*dw)AnF#MW7 maD3liPIW)+FWCdXf!uw5B=^PB%ZFgmCU5aN&%rhRr~VBw(g2qL literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_unicorn.png b/src/main/resources/assets/unicopia/textures/mob_effect/morph_race_unicorn.png new file mode 100644 index 0000000000000000000000000000000000000000..b8151af32737bb777e9a57cb47d1473a6b430d79 GIT binary patch literal 4397 zcmeHKe{2)i9Y2SWgpf=q1qP*qH{vT6JyCckj%J*k|r8 zu|qXVnq>`b2W%vzYtxBsU8^+++R+k~c9b8JsJdp|R4J-9G=5A|`B9Q=Y%Pot?|WzG z*H&qYwEmZ8J-_dL-}n1@-}ian_qA_#c;ot#qPvSQ3@ZuN1~$Vu$LfXo@V(?lMug#a zs3E8XgJoDDJbY5){8T| zKy!kd1#8E_@rZ1so;PVg`L62shl74Up42r(Z9*89?)lCSe6?r7b2~O|d5f6$RLh*^ z_rouJ_ETcd!yQDV@U3;dU%!vK(|%t`@aboMcH!aVaO{n%7cL#@ZSTbkM|N>*xRu9i zcYpD%j`_@^>38?u806ngEy3Mobw`gYj!<8}$u3ol-st6dj^br`4j+5;Sw6VoRNslg zU%YtL`P-xax;=7er$=LOy!0dX%$vKtov(W1?Kdj7cXi9%H+$0OUY=cArGNXcH@|QE z9<{}4`s;T5nA@;={+fm>jrFYO(#lKEzjmdIl702Owtb+?GjM$Q6C!uy$ZF%Ifx;g) z{JQks&T!bjaNZ}KA0OTyT^jmy){@M=YX{!{`M%!bnmsckKUv#y=fV@ij~}ZS_0ta& zE#EcxjkC8F%v|@*{YMA6^Jn*bT;X`~qm$i#`uxE~CqG<%a32u;hi<-yJir5_n&{k+c&^(T(YaUzpQ!2J@@2E)gL_iMQcj? z^xVjGwSTB!`7XWl@;~3uAqUfHq`_|;IYMM!bqEe}p zGv#t>dW>Rto~LM*Vp$R_NTW4jifJ-oEVCf87y)ERx|%dqErDB@VpMB3eFOpX_-K6b zWGFNSpD=PP03TFZOj3-Krs8pGqK9Etw*W{kpfB|>BCSb8ZAOOHtV^i61trX76Cq@2 z%s<(zH`&{fB?>j6IG7r+Dl=(G>&A_FSQNz6c+&O)vL{)ZsxnU2q}Z$(dpi>Y0rxT7 zN!Fu#w~fIn6cPfO)NF+p4EP9ZeL>bFRTgZ~rMN_er9C8aDTs8tqfwG)keiJ1ZjNUe zUXjYV2~fd=VTuU}Sx^A(Q~`&Mdbn~I9VHRsdD2aLMN;(0E|Nit>^56!v8 zjp~tZKqgw4cI~uhtYZ;iGeE3p42UKI z(6Krk8Lh*sXj-7N;ahMaO;(lG|7C4m9=rnV;mB)M1J-Y~RoNT01#QnhWgnYV`zqnM zeJKP{%5K3BTaawW30T=FX{(rsA*dd?bRE^JUy=&Q>v1Uzf-GP}FX@glJjwBJ#d#4i zUYE06u!bfsFjngQ(KXB3P2c1GbHQ%Pr|6&hAFFN zjPAIC`Y%o@Y=a3!2K=%*C|*ztsj*_1(^{~KLJ Date: Sat, 27 Jan 2024 21:18:42 +0000 Subject: [PATCH 78/98] Add a trap to the changling hive using the new potion --- .../chests/changeling_hive_trap.json | 204 ++++++++++++++++++ .../changeling_hive/trapped/offshoot2.nbt | Bin 0 -> 1204 bytes .../changeling_hive/side_passages.json | 9 + 3 files changed, 213 insertions(+) create mode 100644 src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json create mode 100644 src/main/resources/data/unicopia/structures/changeling_hive/trapped/offshoot2.nbt diff --git a/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json b/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json new file mode 100644 index 00000000..2dac1a86 --- /dev/null +++ b/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json @@ -0,0 +1,204 @@ +{ + "type": "minecraft:chest", + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_earth" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 3 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_unicorn" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_pegasus" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_bat" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_kirin" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:short_morph_hippogriff" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 1 + }, + + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_earth" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 5 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_unicorn" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_pegasus" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_bat" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_kirin" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + }, + { + "type": "minecraft:item", + "name": "minecraft:tipped_arrow", + "functions": [ + { "function": "minecraft:set_potion", "id": "unicopia:long_morph_hippogriff" }, + { + "function": "minecraft:set_count", + "count": { + "min": 3.0, + "max": 9.0, + "type": "minecraft:uniform" + } + } + ], + "weight": 2 + } + ], + "rolls": 6.0 + } + ] +} diff --git a/src/main/resources/data/unicopia/structures/changeling_hive/trapped/offshoot2.nbt b/src/main/resources/data/unicopia/structures/changeling_hive/trapped/offshoot2.nbt new file mode 100644 index 0000000000000000000000000000000000000000..497a7b7d9fa58ceb76e5d94d6d61d6f653796a2b GIT binary patch literal 1204 zcmV;l1WWrLiwFP!00000|K*xZkK;4|hMmOu{#XPO`~l#)_dRn&hy&tUd0I~=yPc#; zTt@H@_{*F~cIvjWPmT0WvOD{03G8 zGSZPrfV0iGoNdPCY%?xrn{hd(z}aS8&Nkz6wi%bR&A6ODyIl1I3mFMAv4a&HU!kKX z06qcm34l)sd_v$8-tk#txqMFEAmeh5fO7<#z&QoZDR54Kb9%>liRE(6fKLW|GT@T~Rt{J>U=@H>09FB5C190+ zRRUH8SQTJZfK`L}TmzpP_|(9s0X_}zX@E}yeC92@Vl8D{_arvsy9e;G0?-qJ9-DDF z+l=qK+vj6*u5)_24*(h8_p*;=Gp_S40V@V9oAEt6_zH$$(D= zSUKp)K~DjC3eZ!69-DEk2_;}vfK`E>8uZwVYi`tl)c{rlSPfv!oAexKn;|`}`5XXN z09XNFg@6?TRtQ*jZS}45KA#BqNWhYSB>^i2tQfFjz)Aos0jvbDQou?9D+4_l=*dA( z4tfgEQ-Gcl^pv2d0zDPzsXti*5)d1GKT|c9R3x9J6K_&tj7b^rk5%7sX zkA21uK*q(2K_cjd;bz8k_yY1?!{X@SW+l^lB)i|o{oZk2SPW*_{VLnA2QNvansI+jWa5-Dk)86Ml_r3XT-f!{!MY|r&`2GsA zGK1Q`I(~C@+_d9fccUH_zVO5aHv8Wkbi3?dPVapBN%v}}jnVUER1~84b?EncnE$(K zIwoV+zx*lUN3|REbgbkr^AUM>fp1OQ2{h`v)2Q(1mj~gxG~M?vdeEC= zZ6-2hjGCs!Mo2RprVo;-bUF|dzCW0!GxuC;hIaqb4tn*}_n)o-*)(m`@O(j#T8-xP z?ndCqInv{6@dZP^d4?xxAr_MJ%QU0xl|B{wtrdU!sWZnt09ES%K4SG!YYK9_EpNR&S*qkh-J SIFAtZ-{N1WM??XJC;$L+YD;7Q literal 0 HcmV?d00001 diff --git a/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/side_passages.json b/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/side_passages.json index 2c4bb501..0a5bd470 100644 --- a/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/side_passages.json +++ b/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/side_passages.json @@ -117,6 +117,15 @@ }, "weight": 3 }, + { + "element": { + "element_type": "minecraft:single_pool_element", + "location": "unicopia:changeling_hive/trapped/offshoot2", + "processors": "unicopia:changeling_hive_decay", + "projection": "rigid" + }, + "weight": 3 + }, { "element": { "element_type": "minecraft:single_pool_element", From fcad058e3af0ee686ae0d13adf9055e7cf6f5c31 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 27 Jan 2024 21:19:01 +0000 Subject: [PATCH 79/98] Add the bulb as a possible generation in the pit --- .../changeling_hive/pit_decoration/bulb.nbt | Bin 0 -> 1549 bytes .../changeling_hive/pit_decors.json | 11 +++++++++++ 2 files changed, 11 insertions(+) create mode 100644 src/main/resources/data/unicopia/structures/changeling_hive/pit_decoration/bulb.nbt diff --git a/src/main/resources/data/unicopia/structures/changeling_hive/pit_decoration/bulb.nbt b/src/main/resources/data/unicopia/structures/changeling_hive/pit_decoration/bulb.nbt new file mode 100644 index 0000000000000000000000000000000000000000..daf0e3140b9123614c9455c86d68ffe22991251c GIT binary patch literal 1549 zcmV+o2J-nIiwFP!00000|E*YEY!p=#zVoxo?3Se!i?pIxjaXuc8Z|M&kp4q!id`yI zG{|yy_wM%6*}2QiovjO)C=yhHi3(_ zkE2MpiK!7sT7eXp38P|OW4~?k?(#-jAQs1kB1{0o@h;9ynzp%XLXIM}8(XCFF@#(| zY(2};93`f^go0v2MPg!3nIWzlkUBCl&D7kc^3Qe081}#H5)=a!l9R zc*?{^o*<83$FI5ER#?=Z)^_8x0xOXMxF=;53CSV}EgUdPwhn_htrNJ8uf@8)RxeW{ z5AIo%WG2UY4`X_kjSFqy=ccZ-#mu4*>q>ENFBCI-DY((d5adK7)=MxS70wk-YwQPw zy(TO6j0%Y5L~PAu^Gb@fZ!l0rnQf)g zww|u?^zMg-e)@BN-~GoNQ5G@6eMk$o}NAf=NI8_51e0` zp8oGDilZpUW}a{fY1B&UxJ*oNftJxJm>I|-v|?GzF%cChH9iJc|Y}+6S2w&XSJ~_Fgzj@z-g|S`dcK#Sg@eDIf+wtri z(4vD^Jh5di2~~t!KklSI>r9)$_#mD<7@7HW>MG_jgy1UR-=?UV6Ov zNS&UZKSJnf|D;xYxaR8F-cqNYwfgbaEn|I8zJKlta$UZ8db~K*uZMlSI=aQ~+yCjVACBH} z#Av!5?}N(ob1BU8bGX*~pz{2ztW!^EZ{k|hrvA_N{JW?}`99KqTb`eTb-WK&G}n55 z((m5l`{3!RGxsdKuRD4oVIS#x=ehUl)e}R}^-N$LKFIq`HEw?R>Nm%i`PUH??I&30 zqg@kXdVG-NxG0=Z3}vKrQBzt4f`^QCCX`-L!6VQDm}Ri84KwbvAbu<4{Gbeq=>wb; z+-64HYU(nf2bxujRlgSd2Y4@8$p+H0VCki;ooWARy_o++e|4`6uSqD>Zqp=eR@PBn z`H3EEBxz=Bq&UbJht)OFak(u4_zdTX8-zx`#HTVCdH~bu;xa&vTb^4Mu9u4z?-&6&y3bAeN;T#7Q_%(A3g8%Zw#OV!2U}9iDKmN|sJ# z^Yp#)M%3|_Yq?V$Obnb` zBZuHDhu|!S;4FvWEZ5*%D_3up8%hn%vvnyUI4dDID>XRJUP}$ZS*^i&E{q&vt)P5X zYjBp3yM3ae$TmjdQ(rTgu8X|_4XK9MWO$(RZ7!;Hp6Jxbr$%XsJX)kYl_gMm0a|2C zM~yT45u{M$WeqjtsDbsAyV{bf?FtiH@PRKXt-AxzG&8o1<}Ey~`Q6_QR_CY$)!tD6 zv!IA|!Z~qz6=>y>&>7bq%V6VW6v<)TB9-lw+WacRiJ4(c&C2cO8UNU(@XaHBKyEF>-@^kkHVJIeuR3nTSoN#M|h9axMQ`6cYdd18fB; literal 0 HcmV?d00001 diff --git a/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/pit_decors.json b/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/pit_decors.json index c2d1801b..f5380f91 100644 --- a/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/pit_decors.json +++ b/src/main/resources/data/unicopia/worldgen/template_pool/changeling_hive/pit_decors.json @@ -52,6 +52,17 @@ "projection": "rigid" }, "weight": 1 + }, + { + "element": { + "element_type": "minecraft:single_pool_element", + "location": "unicopia:changeling_hive/pit_decoration/bulb", + "processors": { + "processors": [] + }, + "projection": "rigid" + }, + "weight": 1 } ], "fallback": "minecraft:empty" From 7c1eaf87363bf4816519554cdb3fec15f34b07eb Mon Sep 17 00:00:00 2001 From: Cryghast Date: Sun, 28 Jan 2024 22:44:28 +0800 Subject: [PATCH 80/98] update zh_cn and en_us --- .../resources/assets/unicopia/lang/en_us.json | 3 +- .../resources/assets/unicopia/lang/zh_cn.json | 106 ++++++++++++++++-- 2 files changed, 100 insertions(+), 9 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 39d28398..f409a35f 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -352,6 +352,7 @@ "effect.unicopia.butter_fingers": "Butterfingers", "effect.unicopia.change_race_earth": "Earth Pony Metamorphosis", + "effect.unicopia.change_race_unicorn": "Unicorn Metamorphosis", "effect.unicopia.change_race_pegasus": "Pegasus Metamorphosis", "effect.unicopia.change_race_changeling": "Changeling Metamorphosis", "effect.unicopia.change_race_bat": "Bat Pony Metamorphosis", @@ -359,6 +360,7 @@ "effect.unicopia.change_race_hippogriff": "Hippogriff Metamorphosis", "effect.unicopia.morph_race_earth": "Earth Pony Transformation", + "effect.unicopia.morph_race_unicorn": "Unicorn Transformation", "effect.unicopia.morph_race_pegasus": "Pegasus Transformation", "effect.unicopia.morph_race_changeling": "Changeling Transformation", "effect.unicopia.morph_race_bat": "Bat Pony Transformation", @@ -370,7 +372,6 @@ "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_earth": "Lingering Potion of Earth Pony Metamorphosis", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_earth": "Arrow of Earth Pony Metamorphosis", - "effect.unicopia.change_race_unicorn": "Unicorn Metamorphosis", "item.minecraft.potion.effect.unicopia.tribe_swap_unicorn": "Potion of Unicorn Metamorphosis", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_unicorn": "Splash Potion of Unicorn Metamorphosis", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_unicorn": "Lingering Potion of Unicorn Metamorphosis", diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index b8525bd7..9eb2718d 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -123,6 +123,7 @@ "item.unicopia.green_apple_seeds": "史密斯婆婆苹果的种子", "item.unicopia.sweet_apple_seeds": "甜苹果的种子", "item.unicopia.sour_apple_seeds": "酸苹果的种子", + "item.unicopia.golden_oak_seeds": "金橡树种子", "item.unicopia.apple_pie_hoof": "蹄印苹果派", "item.unicopia.apple_pie_slice": "一片苹果派", "item.unicopia.candied_apple": "苹果蜜饯", @@ -248,6 +249,10 @@ "block.unicopia.weather_vane": "风向标", "block.unicopia.curing_joke": "疗玩笑", "block.unicopia.gold_root": "黄金根", + "block.unicopia.golden_oak_sprout": "金橡树嫩芽", + "block.unicopia.golden_oak_sapling": "金橡树树苗", + "block.unicopia.golden_oak_leaves": "金橡树树叶", + "block.unicopia.golden_oak_log": "金橡树原木", "block.unicopia.mango": "芒果", "block.unicopia.mango_leaves": "芒果树叶", "block.unicopia.mango_sapling": "芒果树苗", @@ -347,47 +352,126 @@ "effect.unicopia.butter_fingers": "手滑", "effect.unicopia.change_race_earth": "转生陆马", + "effect.unicopia.change_race_unicorn": "转生独角兽", + "effect.unicopia.change_race_pegasus": "转生天马", + "effect.unicopia.change_race_changeling": "转生幻形灵", + "effect.unicopia.change_race_bat": "转生夜骐", + "effect.unicopia.change_race_kirin": "转生麒麟", + "effect.unicopia.change_race_hippogriff": "转生骏鹰", + + "effect.unicopia.morph_race_earth": "变成陆马", + "effect.unicopia.morph_race_unicopia": "变成独角兽", + "effect.unicopia.morph_race_pegasus": "变成天马", + "effect.unicopia.morph_race_changeling": "变成幻形灵", + "effect.unicopia.morph_race_bat": "变成夜骐", + "effect.unicopia.morph_race_kirin": "变成麒麟", + "effect.unicopia.morph_race_hippogriff": "变成骏鹰", + "item.minecraft.potion.effect.unicopia.tribe_swap_earth": "转生陆马药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_earth": "喷溅型转生陆马药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_earth": "滞留型转生陆马药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_earth": "转生陆马之箭", - "effect.unicopia.change_race_unicorn": "转生独角兽", "item.minecraft.potion.effect.unicopia.tribe_swap_unicorn": "转生独角兽药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_unicorn": "喷溅型转生独角兽药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_unicorn": "滞留型转生独角兽药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_unicorn": "转生独角兽之箭", - "effect.unicopia.change_race_pegasus": "转生天马", "item.minecraft.potion.effect.unicopia.tribe_swap_pegasus": "转生天马药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_pegasus": "喷溅型转生天马药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_pegasus": "滞留型转生天马药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_pegasus": "转生天马之箭", - "effect.unicopia.change_race_changeling": "转生幻形灵", "item.minecraft.potion.effect.unicopia.tribe_swap_changeling": "转生幻形灵药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_changeling": "喷溅型转生幻形灵药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_changeling": "滞留型转生幻形灵药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_changeling": "转生幻形灵之箭", - "effect.unicopia.change_race_bat": "转生夜骐", "item.minecraft.potion.effect.unicopia.tribe_swap_bat": "转生夜骐药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_bat": "喷溅型转生夜骐药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_bat": "滞留型转生夜骐药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_bat": "转生夜骐之箭", - "effect.unicopia.change_race_kirin": "转生麒麟", "item.minecraft.potion.effect.unicopia.tribe_swap_kirin": "转生麒麟药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_kirin": "喷溅型转生麒麟药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_kirin": "滞留型转生麒麟药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_kirin": "转生麒麟之箭", - "effect.unicopia.change_race_hippogriff": "转生骏鹰", "item.minecraft.potion.effect.unicopia.tribe_swap_hippogriff": "转生骏鹰药水", "item.minecraft.splash_potion.effect.unicopia.tribe_swap_hippogriff": "喷溅型转生骏鹰药水", "item.minecraft.lingering_potion.effect.unicopia.tribe_swap_hippogriff": "滞留型转生骏鹰药水", "item.minecraft.tipped_arrow.effect.unicopia.tribe_swap_hippogriff": "转生骏鹰之箭", - + + "item.minecraft.potion.effect.unicopia.short_morph_earth": "短效变成陆马药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_earth": "喷溅型短效变成陆马药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_earth": "滞留型短效变成陆马药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_earth": "短效变成陆马之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_unicorn": "短效变成独角兽药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_unicorn": "喷溅型短效变成独角兽药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_unicorn": "滞留型短效变成独角兽药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_unicorn": "短效变成独角兽之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_pegasus": "短效变成天马药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_pegasus": "喷溅型短效变成天马药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_pegasus": "滞留型短效变成天马药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_pegasus": "短效变成天马之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_changeling": "短效变成幻形灵药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_changeling": "喷溅型短效变成幻形灵药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_changeling": "滞留型短效变成幻形灵药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_changeling": "短效变成幻形灵之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_bat": "短效变成夜骐药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_bat": "喷溅型短效变成夜骐药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_bat": "滞留型短效变成夜骐药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_bat": "短效变成夜骐之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_kirin": "短效变成麒麟药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_kirin": "喷溅型短效变成麒麟药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_kirin": "滞留型短效变成麒麟药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_kirin": "短效变成麒麟之箭", + + "item.minecraft.potion.effect.unicopia.short_morph_hippogriff": "短效变成骏鹰药水", + "item.minecraft.splash_potion.effect.unicopia.short_morph_hippogriff": "喷溅型短效变成骏鹰药水", + "item.minecraft.lingering_potion.effect.unicopia.short_morph_hippogriff": "滞留型短效变成骏鹰药水", + "item.minecraft.tipped_arrow.effect.unicopia.short_morph_hippogriff": "短效变成骏鹰之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_earth": "长效变成陆马药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_earth": "喷溅型长效变成陆马药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_earth": "滞留型长效变成陆马药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_earth": "长效变成陆马之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_unicorn": "长效变成独角兽药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_unicorn": "喷溅型长效变成独角兽药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_unicorn": "滞留型长效变成独角兽药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_unicorn": "长效变成独角兽之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_pegasus": "长效变成天马药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_pegasus": "喷溅型长效变成天马药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_pegasus": "滞留型长效变成天马药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_pegasus": "长效变成天马之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_changeling": "长效变成幻形灵药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_changeling": "喷溅型长效变成幻形灵药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_changeling": "滞留型长效变成幻形灵药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_changeling": "长效变成幻形灵之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_bat": "长效变成夜骐药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_bat": "喷溅型长效变成夜骐药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_bat": "滞留型长效变成夜骐药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_bat": "长效变成夜骐之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_kirin": "长效变成麒麟药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_kirin": "喷溅型长效变成麒麟药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_kirin": "滞留型长效变成麒麟药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_kirin": "长效变成麒麟之箭", + + "item.minecraft.potion.effect.unicopia.long_morph_hippogriff": "长效变成骏鹰药水", + "item.minecraft.splash_potion.effect.unicopia.long_morph_hippogriff": "喷溅型长效变成骏鹰药水", + "item.minecraft.lingering_potion.effect.unicopia.long_morph_hippogriff": "滞留型长效变成骏鹰药水", + "item.minecraft.tipped_arrow.effect.unicopia.long_morph_hippogriff": "长效变成骏鹰之箭", + "potion.withChance": "%s 分之 1 的概率为 %s", "potion.potency.6": "VII", @@ -993,6 +1077,10 @@ "death.attack.unicopia.rock.self": "%1$s 被击中了", "death.attack.unicopia.rock.item": "%1$s 被 %2$s 用 %3$s 击中了", "death.attack.unicopia.rock.player": "%1$s 被 %2$s 击中了", + "death.attack.unicopia.horseshoe": "%1$s 被咣了", + "death.attack.unicopia.horseshoe.self": "%1$s 咣了自己", + "death.attack.unicopia.horseshoe.item": "%1$s 被 %2$s 用 %3$s 咣了", + "death.attack.unicopia.horseshoe.player": "%1$s 被 %2$s 咣了", "death.fell.accident.ladder.pegasus": "%1$s 从梯子上掉下来时忘了自己会飞", "death.fell.accident.vines.pegasus": "%1$s 从藤蔓上掉下来时忘了自己会飞", @@ -1110,7 +1198,7 @@ "advancements.unicopia.eat_pinecone.title": "豁出去了", "advancements.unicopia.eat_pinecone.description": "吃下一颗松果", "advancements.unicopia.imported_oats.title": "昂贵的味道", - "advancements.unicopia.imported_oats.description": "用魔法邮寄高档进口燕麦", + "advancements.unicopia.imported_oats.description": "寄出或收到高档进口燕麦", "advancements.unicopia.experimental.title": "你不能带走它", "advancements.unicopia.experimental.description": "给一件工具附上经验提取的魔力", @@ -1159,6 +1247,8 @@ "advancements.unicopia.earth_route.description": "入伙苹果帮", "advancements.unicopia.sticks_and_stones.title": "岩与攻击", "advancements.unicopia.sticks_and_stones.description": "扔出石块砸死一位生灵", + "advancements.unicopia.dead_ringer.title": "死亡回旋", + "advancements.unicopia.dead_ringer.description": "用蹄铁砸死一位生灵", "advancements.unicopia.born_on_a_rock_farm.title": "种石得石", "advancements.unicopia.born_on_a_rock_farm.description": "播种石籽", "advancements.unicopia.thats_unusual.title": "不对劲", From 78dbc800aef9c08b0413cc1b876e6190a2887bab Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 29 Jan 2024 16:35:08 +0000 Subject: [PATCH 81/98] Assign traits to a lot of items that needed it --- .../com/minelittlepony/unicopia/Debug.java | 50 ++++++-- .../com/minelittlepony/unicopia/UTags.java | 1 + .../magic/spell/trait/ItemWithTraits.java | 5 + .../magic/spell/trait/SpellTraits.java | 8 ++ .../unicopia/item/TraitItem.java | 21 ++++ .../resources/data/c/tags/items/boats.json | 15 +++ .../resources/data/c/tags/items/concrete.json | 21 ++++ .../data/c/tags/items/concrete_powders.json | 21 ++++ .../data/c/tags/items/glazed_terracotta.json | 21 ++++ .../resources/data/c/tags/items/seeds.json | 2 + .../overworld/organic_plant_derived.json | 30 +++++ .../organic_plant_derived_artificial.json | 8 ++ .../minecraft/tags/items/chest_boats.json | 6 + ...oden_fence_gates.json => fence_gates.json} | 0 .../minecraft/tags/items/hanging_signs.json | 6 + .../minecraft/tags/items/logs_that_burn.json | 13 ++ .../data/minecraft/tags/items/saplings.json | 7 +- .../data/minecraft/tags/items/slabs.json | 6 + .../data/minecraft/tags/items/stairs.json | 6 + .../data/minecraft/tags/items/tools.json | 7 ++ .../minecraft/tags/items/wooden_doors.json | 4 +- ...rate_artificial_materials_from_ground.json | 23 +--- .../conglomerate_materials_from_ground.json | 54 +-------- ...d_rock_derived.json => cracked_stone.json} | 0 .../traits/blocks/overworld/dirt.json | 7 ++ .../traits/blocks/overworld/dirt_worked.json | 11 ++ .../traits/blocks/overworld/energised.json | 7 ++ .../blocks/overworld/infested_stone.json | 13 ++ .../intellectual_objects_studicious.json | 10 ++ .../blocks/overworld/intellectual_worked.json | 7 ++ .../blocks/overworld/living_matter.json | 45 +------ .../loose_materials_from_ground.json | 21 +--- .../overworld/materials_from_the_ground.json | 2 +- .../mechanical_with_organic_components.json | 59 ++------- .../blocks/overworld/organic_dead_matter.json | 4 +- .../overworld/organic_plant_derived.json | 112 ++---------------- .../overworld/organic_plant_derived_dark.json | 2 +- .../traits/blocks/overworld/refined_ores.json | 2 +- .../overworld/rocks_and_rock_derived.json | 5 + .../traits/blocks/overworld/skulk.json | 11 ++ .../traits/blocks/overworld/skulk_worked.json | 7 ++ .../blocks/overworld/soft_and_kind.json | 9 ++ .../traits/items/overworld/animal_horns.json | 7 ++ .../traits/items/overworld/chaotic.json | 6 +- .../items/overworld/chaotic_worked.json | 9 ++ .../items/overworld/clothing_bloody.json | 3 +- .../items/overworld/edible_cooked_meat.json | 12 +- .../traits/items/overworld/edible_fruits.json | 7 ++ .../items/overworld/edible_plant_based.json | 4 +- .../edible_plant_based_and_modified.json | 4 +- .../items/overworld/edible_raw_meat.json | 6 +- .../overworld/edible_raw_meat_from_sea.json | 5 +- .../overworld/edible_raw_meat_poisoned.json | 3 +- .../traits/items/overworld/full_o_life.json | 9 +- .../items/overworld/full_o_life_wet.json | 6 +- .../traits/items/overworld/full_o_water.json | 2 +- .../items/overworld/intellectual_objects.json | 19 +-- .../intellectual_objects_enchanted.json | 3 +- .../intellectual_objects_focused.json | 4 +- .../intellectual_objects_studicious.json | 4 + .../overworld/organic_plant_derived.json | 30 +++++ .../organic_plant_derived_artificial.json | 19 +-- .../traits/items/overworld/special.json | 3 +- .../traits/items/overworld/tools.json | 27 +---- .../items/overworld/uplifting_trinkets.json | 14 +-- .../traits/items/underworld/weapons.json | 3 +- .../data/unicopia/tags/items/badges.json | 13 ++ .../data/unicopia/tags/items/chitin.json | 12 ++ .../data/unicopia/tags/items/cloud_slabs.json | 1 + .../unicopia/tags/items/cloud_stairs.json | 4 +- .../unicopia/tags/items/floats_on_clouds.json | 4 +- .../unicopia/tags/items/has_no_traits.json | 20 ++++ .../data/unicopia/traits/chitin.json | 9 ++ .../unicopia/traits/edible_plant_based.json | 18 --- .../resources/data/unicopia/traits/food.json | 33 ++++++ .../data/unicopia/traits/from_the_ground.json | 6 +- .../data/unicopia/traits/gilded.json | 11 ++ .../traits/intellectual_objects_ordered.json | 8 ++ .../resources/data/unicopia/traits/love.json | 8 ++ .../data/unicopia/traits/magical.json | 8 ++ .../data/unicopia/traits/organic_living.json | 8 ++ .../traits/organic_plant_derived.json | 10 -- .../organic_plant_derived_artificial.json | 7 ++ .../traits/organic_plant_derived_zap.json | 3 +- .../data/unicopia/traits/shells.json | 8 ++ .../data/unicopia/traits/soft_and_kind.json | 9 ++ .../data/unicopia/traits/special.json | 7 +- .../unicopia/traits/uplifting_trinkets.json | 4 +- 88 files changed, 648 insertions(+), 441 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/ItemWithTraits.java create mode 100644 src/main/java/com/minelittlepony/unicopia/item/TraitItem.java create mode 100644 src/main/resources/data/c/tags/items/boats.json create mode 100644 src/main/resources/data/c/tags/items/concrete.json create mode 100644 src/main/resources/data/c/tags/items/concrete_powders.json create mode 100644 src/main/resources/data/c/tags/items/glazed_terracotta.json create mode 100644 src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived.json create mode 100644 src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived_artificial.json create mode 100644 src/main/resources/data/minecraft/tags/items/chest_boats.json rename src/main/resources/data/minecraft/tags/items/{wooden_fence_gates.json => fence_gates.json} (100%) create mode 100644 src/main/resources/data/minecraft/tags/items/hanging_signs.json create mode 100644 src/main/resources/data/minecraft/tags/items/logs_that_burn.json create mode 100644 src/main/resources/data/minecraft/tags/items/slabs.json create mode 100644 src/main/resources/data/minecraft/tags/items/stairs.json create mode 100644 src/main/resources/data/minecraft/tags/items/tools.json rename src/main/resources/data/minecraft/traits/blocks/overworld/{degraded_rocks_and_rock_derived.json => cracked_stone.json} (100%) create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/dirt.json create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/dirt_worked.json create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/energised.json create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/infested_stone.json create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_objects_studicious.json create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_worked.json create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/skulk.json create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/skulk_worked.json create mode 100644 src/main/resources/data/minecraft/traits/blocks/overworld/soft_and_kind.json create mode 100644 src/main/resources/data/minecraft/traits/items/overworld/animal_horns.json create mode 100644 src/main/resources/data/minecraft/traits/items/overworld/chaotic_worked.json create mode 100644 src/main/resources/data/minecraft/traits/items/overworld/edible_fruits.json create mode 100644 src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived.json create mode 100644 src/main/resources/data/unicopia/tags/items/badges.json create mode 100644 src/main/resources/data/unicopia/tags/items/chitin.json create mode 100644 src/main/resources/data/unicopia/tags/items/has_no_traits.json create mode 100644 src/main/resources/data/unicopia/traits/chitin.json delete mode 100644 src/main/resources/data/unicopia/traits/edible_plant_based.json create mode 100644 src/main/resources/data/unicopia/traits/food.json create mode 100644 src/main/resources/data/unicopia/traits/gilded.json create mode 100644 src/main/resources/data/unicopia/traits/intellectual_objects_ordered.json create mode 100644 src/main/resources/data/unicopia/traits/love.json create mode 100644 src/main/resources/data/unicopia/traits/magical.json create mode 100644 src/main/resources/data/unicopia/traits/organic_living.json delete mode 100644 src/main/resources/data/unicopia/traits/organic_plant_derived.json create mode 100644 src/main/resources/data/unicopia/traits/organic_plant_derived_artificial.json create mode 100644 src/main/resources/data/unicopia/traits/shells.json create mode 100644 src/main/resources/data/unicopia/traits/soft_and_kind.json diff --git a/src/main/java/com/minelittlepony/unicopia/Debug.java b/src/main/java/com/minelittlepony/unicopia/Debug.java index 2e5a9982..8cc8847e 100644 --- a/src/main/java/com/minelittlepony/unicopia/Debug.java +++ b/src/main/java/com/minelittlepony/unicopia/Debug.java @@ -1,33 +1,35 @@ package com.minelittlepony.unicopia; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import com.google.common.collect.Sets; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.entity.mob.AirBalloonEntity; import com.minelittlepony.unicopia.entity.mob.UEntities; import net.minecraft.entity.vehicle.BoatEntity; import net.minecraft.registry.Registries; +import net.minecraft.registry.tag.TagKey; +import net.minecraft.util.Identifier; import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionTypes; public interface Debug { boolean SPELLBOOK_CHAPTERS = Boolean.getBoolean("unicopia.debug.spellbookChapters"); boolean CHECK_GAME_VALUES = Boolean.getBoolean("unicopia.debug.checkGameValues"); + boolean CHECK_TRAIT_COVERAGE = Boolean.getBoolean("unicopia.debug.checkTraitCoverage"); - boolean[] TESTS_COMPLETE = {false}; + AtomicReference LAST_TESTED_WORLD = new AtomicReference<>(null); static void runTests(World world) { - if (!CHECK_GAME_VALUES || TESTS_COMPLETE[0]) { + if (!CHECK_GAME_VALUES || !world.getDimensionKey().getValue().equals(DimensionTypes.OVERWORLD_ID) || (LAST_TESTED_WORLD.getAndSet(world) == world)) { return; } - TESTS_COMPLETE[0] = true; - try { - Registries.ITEM.getEntrySet().forEach(entry -> { - if (SpellTraits.of(entry.getValue()).isEmpty()) { - // Unicopia.LOGGER.warn("No traits registered for item {}", entry.getKey()); - } - }); - } catch (Throwable t) { - throw new IllegalStateException("Tests failed", t); + if (CHECK_TRAIT_COVERAGE) { + testTraitCoverage(); } try { @@ -40,4 +42,30 @@ public interface Debug { throw new IllegalStateException("Tests failed", t); } } + + private static void testTraitCoverage() { + Registries.ITEM.getEntrySet().stream().collect(Collectors.toMap( + entry -> entry.getKey().getValue().getNamespace(), + Set::of, + Sets::union + )).forEach((namespace, entries) -> { + @SuppressWarnings("deprecation") + var unregistered = entries.stream() + .filter(entry -> !entry.getValue().getRegistryEntry().isIn(UTags.HAS_NO_TRAITS) && SpellTraits.of(entry.getValue()).isEmpty()) + .map(entry -> { + String id = entry.getKey().getValue().toString(); + + return id + "(" + Registries.ITEM.streamTags() + .filter(entry.getValue().getRegistryEntry()::isIn) + .map(TagKey::id) + .map(Identifier::toString) + .collect(Collectors.joining(", ")) + ")"; + }) + .toList(); + + if (!unregistered.isEmpty()) { + Unicopia.LOGGER.warn("No traits registered for {} items in namepsace {} {}", unregistered.size(), namespace, String.join(",\r\n", unregistered)); + } + }); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java index 9d4a460b..e8d0b5d7 100644 --- a/src/main/java/com/minelittlepony/unicopia/UTags.java +++ b/src/main/java/com/minelittlepony/unicopia/UTags.java @@ -23,6 +23,7 @@ public interface UTags { TagKey SHADES = item("shades"); TagKey CHANGELING_EDIBLE = item("food_types/changeling_edible"); TagKey SPOOKED_MOB_DROPS = item("spooked_mob_drops"); + TagKey HAS_NO_TRAITS = item("has_no_traits"); TagKey IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively"); TagKey FLOATS_ON_CLOUDS = item("floats_on_clouds"); TagKey COOLS_OFF_KIRINS = item("cools_off_kirins"); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/ItemWithTraits.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/ItemWithTraits.java new file mode 100644 index 00000000..5b6cd107 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/ItemWithTraits.java @@ -0,0 +1,5 @@ +package com.minelittlepony.unicopia.ability.magic.spell.trait; + +public interface ItemWithTraits { + SpellTraits getDefaultTraits(); +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java index ac082e3c..d6ca4d8c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java @@ -27,6 +27,7 @@ import net.fabricmc.api.Environment; import net.minecraft.block.Block; import net.minecraft.inventory.Inventory; import net.minecraft.item.Item; +import net.minecraft.item.SpawnEggItem; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; @@ -38,6 +39,7 @@ import net.minecraft.registry.Registries; public final class SpellTraits implements Iterable> { public static final SpellTraits EMPTY = new SpellTraits(Map.of()); + private static final SpellTraits SPAWN_EGG_TRAITS = new SpellTraits(Map.of(Trait.LIFE, 20F)); private static Map REGISTRY = new HashMap<>(); static final Map> ITEMS = new HashMap<>(); @@ -212,6 +214,12 @@ public final class SpellTraits implements Iterable> { } public static SpellTraits of(Item item) { + if (item instanceof ItemWithTraits i) { + return i.getDefaultTraits(); + } + if (item instanceof SpawnEggItem) { + return SPAWN_EGG_TRAITS; + } return REGISTRY.getOrDefault(Registries.ITEM.getId(item), EMPTY); } diff --git a/src/main/java/com/minelittlepony/unicopia/item/TraitItem.java b/src/main/java/com/minelittlepony/unicopia/item/TraitItem.java new file mode 100644 index 00000000..c3b95049 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/TraitItem.java @@ -0,0 +1,21 @@ +package com.minelittlepony.unicopia.item; + +import com.minelittlepony.unicopia.ability.magic.spell.trait.ItemWithTraits; +import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; +import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; + +import net.minecraft.item.Item; + +public class TraitItem extends Item implements ItemWithTraits { + private final SpellTraits traits; + + public TraitItem(Trait trait, Settings settings) { + super(settings); + this.traits = new SpellTraits.Builder().with(trait, 1).build(); + } + + @Override + public SpellTraits getDefaultTraits() { + return traits; + } +} diff --git a/src/main/resources/data/c/tags/items/boats.json b/src/main/resources/data/c/tags/items/boats.json new file mode 100644 index 00000000..dde2408d --- /dev/null +++ b/src/main/resources/data/c/tags/items/boats.json @@ -0,0 +1,15 @@ +{ + "replace": false, + "values": [ + "minecraft:oak_boat", + "minecraft:bamboo_raft", + "minecraft:spruce_boat", + "minecraft:birch_boat", + "minecraft:jungle_boat", + "minecraft:acacia_boat", + "minecraft:dark_oak_boat", + "minecraft:mangrove_boat", + "minecraft:cherry_boat", + "unicopia:palm_boat" + ] +} diff --git a/src/main/resources/data/c/tags/items/concrete.json b/src/main/resources/data/c/tags/items/concrete.json new file mode 100644 index 00000000..c09d67ba --- /dev/null +++ b/src/main/resources/data/c/tags/items/concrete.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_concrete", + "minecraft:orange_concrete", + "minecraft:magenta_concrete", + "minecraft:light_blue_concrete", + "minecraft:yellow_concrete", + "minecraft:lime_concrete", + "minecraft:pink_concrete", + "minecraft:gray_concrete", + "minecraft:light_gray_concrete", + "minecraft:cyan_concrete", + "minecraft:purple_concrete", + "minecraft:blue_concrete", + "minecraft:brown_concrete", + "minecraft:green_concrete", + "minecraft:red_concrete", + "minecraft:black_concrete" + ] +} diff --git a/src/main/resources/data/c/tags/items/concrete_powders.json b/src/main/resources/data/c/tags/items/concrete_powders.json new file mode 100644 index 00000000..e3142856 --- /dev/null +++ b/src/main/resources/data/c/tags/items/concrete_powders.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_concrete_powder", + "minecraft:orange_concrete_powder", + "minecraft:magenta_concrete_powder", + "minecraft:light_blue_concrete_powder", + "minecraft:yellow_concrete_powder", + "minecraft:lime_concrete_powder", + "minecraft:pink_concrete_powder", + "minecraft:gray_concrete_powder", + "minecraft:light_gray_concrete_powder", + "minecraft:cyan_concrete_powder", + "minecraft:purple_concrete_powder", + "minecraft:blue_concrete_powder", + "minecraft:brown_concrete_powder", + "minecraft:green_concrete_powder", + "minecraft:red_concrete_powder", + "minecraft:black_concrete_powder" + ] +} diff --git a/src/main/resources/data/c/tags/items/glazed_terracotta.json b/src/main/resources/data/c/tags/items/glazed_terracotta.json new file mode 100644 index 00000000..02b98848 --- /dev/null +++ b/src/main/resources/data/c/tags/items/glazed_terracotta.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_glazed_terracotta", + "minecraft:orange_glazed_terracotta", + "minecraft:magenta_glazed_terracotta", + "minecraft:light_blue_glazed_terracotta", + "minecraft:yellow_glazed_terracotta", + "minecraft:lime_glazed_terracotta", + "minecraft:pink_glazed_terracotta", + "minecraft:gray_glazed_terracotta", + "minecraft:light_gray_glazed_terracotta", + "minecraft:cyan_glazed_terracotta", + "minecraft:purple_glazed_terracotta", + "minecraft:blue_glazed_terracotta", + "minecraft:brown_glazed_terracotta", + "minecraft:green_glazed_terracotta", + "minecraft:red_glazed_terracotta", + "minecraft:black_glazed_terracotta" + ] +} diff --git a/src/main/resources/data/c/tags/items/seeds.json b/src/main/resources/data/c/tags/items/seeds.json index d0c9c06a..d61f66fb 100644 --- a/src/main/resources/data/c/tags/items/seeds.json +++ b/src/main/resources/data/c/tags/items/seeds.json @@ -1,6 +1,8 @@ { "replace": false, "values": [ + "minecraft:pumpkin_seeds", + "minecraft:melon_seeds", "unicopia:oat_seeds", "unicopia:green_apple_seeds", "unicopia:sweet_apple_seeds", diff --git a/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived.json b/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived.json new file mode 100644 index 00000000..fc16efe7 --- /dev/null +++ b/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived.json @@ -0,0 +1,30 @@ +{ + "replace": false, + "traits": "earth:2 life:1", + "items": [ + "#minecraft:planks", + "#minecraft:logs_that_burn", + "#minecraft:wooden_stairs", + "#minecraft:wooden_slabs", + "#minecraft:wooden_fences", + + "minecraft:sponge", + "minecraft:cobweb", + + "minecraft:kelp", + "minecraft:cake", + "minecraft:chorus_plant", + "minecraft:moss_carpet", + "minecraft:mossy_cobblestone", + + "minecraft:brown_mushroom_block", + "minecraft:red_mushroom_block", + "minecraft:mushroom_stem", + + "minecraft:slime_block", + "minecraft:honey_block", + "minecraft:honeycomb_block", + "minecraft:bee_nest", + "minecraft:beehive" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived_artificial.json b/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived_artificial.json new file mode 100644 index 00000000..72b34d4b --- /dev/null +++ b/src/main/resources/data/farmersdelight/traits/items/overworld/organic_plant_derived_artificial.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "order:1 knowledge:3", + "items": [ + "#farmersdelight:canvas_signs", + "#farmersdelight:cabinets" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/chest_boats.json b/src/main/resources/data/minecraft/tags/items/chest_boats.json new file mode 100644 index 00000000..4ffee04d --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/chest_boats.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_chest_boat" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/wooden_fence_gates.json b/src/main/resources/data/minecraft/tags/items/fence_gates.json similarity index 100% rename from src/main/resources/data/minecraft/tags/items/wooden_fence_gates.json rename to src/main/resources/data/minecraft/tags/items/fence_gates.json diff --git a/src/main/resources/data/minecraft/tags/items/hanging_signs.json b/src/main/resources/data/minecraft/tags/items/hanging_signs.json new file mode 100644 index 00000000..2d324a64 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/hanging_signs.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_hanging_sign" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/logs_that_burn.json b/src/main/resources/data/minecraft/tags/items/logs_that_burn.json new file mode 100644 index 00000000..4e7dd5d0 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/logs_that_burn.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "unicopia:palm_log", + "unicopia:palm_wood", + "unicopia:stripped_palm_log", + "unicopia:stripped_palm_wood", + "unicopia:zap_log", + "unicopia:zap_wood", + "unicopia:stripped_zap_log", + "unicopia:stripped_zap_wood" + ] +} diff --git a/src/main/resources/data/minecraft/tags/items/saplings.json b/src/main/resources/data/minecraft/tags/items/saplings.json index c504b996..1be3e7f1 100644 --- a/src/main/resources/data/minecraft/tags/items/saplings.json +++ b/src/main/resources/data/minecraft/tags/items/saplings.json @@ -1,6 +1,11 @@ { "replace": false, "values": [ - "unicopia:mango_sapling" + "unicopia:mango_sapling", + "unicopia:palm_sapling", + "unicopia:green_apple_sapling", + "unicopia:sour_apple_sapling", + "unicopia:sweet_apple_sapling", + "unicopia:zapling" ] } diff --git a/src/main/resources/data/minecraft/tags/items/slabs.json b/src/main/resources/data/minecraft/tags/items/slabs.json new file mode 100644 index 00000000..56e1871c --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/slabs.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:chiselled_chitin_slab" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/stairs.json b/src/main/resources/data/minecraft/tags/items/stairs.json new file mode 100644 index 00000000..ddd6b7e3 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/stairs.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "unicopia:chiselled_chitin_stairs" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/tools.json b/src/main/resources/data/minecraft/tags/items/tools.json new file mode 100644 index 00000000..1a41feac --- /dev/null +++ b/src/main/resources/data/minecraft/tags/items/tools.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "#unicopia:horse_shoes", + "#unicopia:polearms" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/items/wooden_doors.json b/src/main/resources/data/minecraft/tags/items/wooden_doors.json index e7071e6f..b00445b2 100644 --- a/src/main/resources/data/minecraft/tags/items/wooden_doors.json +++ b/src/main/resources/data/minecraft/tags/items/wooden_doors.json @@ -1,6 +1,8 @@ { "replace": false, "values": [ - "unicopia:palm_door" + "unicopia:palm_door", + "unicopia:dark_oak_stable_door", + "unicopia:stable_door" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_artificial_materials_from_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_artificial_materials_from_ground.json index fb4ea397..401bed91 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_artificial_materials_from_ground.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_artificial_materials_from_ground.json @@ -2,23 +2,12 @@ "replace": false, "traits": "earth:1 chaos:1", "items": [ + "#minecraft:walls", + "#minecraft:slabs", + "#minecraft:stairs", "minecraft:cobblestone", - "minecraft:cobblestone_stairs", - "minecraft:cobblestone_slab", - "minecraft:cobblestone_wall", - "minecraft:mossy_cobblestone_wall", - "minecraft:mossy_cobblestone_stairs", - "minecraft:mossy_cobblestone_slab", - "minecraft:cobbled_deepslate_wall", - "minecraft:cobbled_deepslate_stairs", - "minecraft:cobbled_deepslate_slab", - "minecraft:sandstone_stairs", - "minecraft:sandstone", - "minecraft:chiseled_sandstone", - "minecraft:cut_sandstone", - "minecraft:sandstone_slab", - "minecraft:cut_sandstone_slab", - "minecraft:sandstone_wall", - "minecraft:smooth_sandstone_slab" + "#c:uncolored_sandstone_blocks", + "#c:uncolored_sandstone_stairs", + "#c:uncolored_sandstone_slabs" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json index aa98dbe1..a19e4901 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json @@ -2,56 +2,8 @@ "replace": false, "traits": "earth:1 order:1 knowledge:4", "items": [ - "minecraft:white_concrete", - "minecraft:orange_concrete", - "minecraft:magenta_concrete", - "minecraft:light_blue_concrete", - "minecraft:yellow_concrete", - "minecraft:lime_concrete", - "minecraft:pink_concrete", - "minecraft:gray_concrete", - "minecraft:light_gray_concrete", - "minecraft:cyan_concrete", - "minecraft:purple_concrete", - "minecraft:blue_concrete", - "minecraft:brown_concrete", - "minecraft:green_concrete", - "minecraft:red_concrete", - "minecraft:black_concrete", - - "minecraft:white_terracotta", - "minecraft:orange_terracotta", - "minecraft:magenta_terracotta", - "minecraft:light_blue_terracotta", - "minecraft:yellow_terracotta", - "minecraft:lime_terracotta", - "minecraft:pink_terracotta", - "minecraft:gray_terracotta", - "minecraft:light_gray_terracotta", - "minecraft:cyan_terracotta", - "minecraft:purple_terracotta", - "minecraft:blue_terracotta", - "minecraft:brown_terracotta", - "minecraft:green_terracotta", - "minecraft:red_terracotta", - "minecraft:black_terracotta", - "minecraft:terracotta", - - "minecraft:white_glazed_terracotta", - "minecraft:orange_glazed_terracotta", - "minecraft:magenta_glazed_terracotta", - "minecraft:light_blue_glazed_terracotta", - "minecraft:yellow_glazed_terracotta", - "minecraft:lime_glazed_terracotta", - "minecraft:pink_glazed_terracotta", - "minecraft:gray_glazed_terracotta", - "minecraft:light_gray_glazed_terracotta", - "minecraft:cyan_glazed_terracotta", - "minecraft:purple_glazed_terracotta", - "minecraft:blue_glazed_terracotta", - "minecraft:brown_glazed_terracotta", - "minecraft:green_glazed_terracotta", - "minecraft:red_glazed_terracotta", - "minecraft:black_glazed_terracotta" + "#minecraft:terracotta", + "#c:concrete", + "#c:glazed_terracotta" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/degraded_rocks_and_rock_derived.json b/src/main/resources/data/minecraft/traits/blocks/overworld/cracked_stone.json similarity index 100% rename from src/main/resources/data/minecraft/traits/blocks/overworld/degraded_rocks_and_rock_derived.json rename to src/main/resources/data/minecraft/traits/blocks/overworld/cracked_stone.json diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/dirt.json b/src/main/resources/data/minecraft/traits/blocks/overworld/dirt.json new file mode 100644 index 00000000..a77f37dd --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/dirt.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "earth:6", + "items": [ + "#minecraft:dirt" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/dirt_worked.json b/src/main/resources/data/minecraft/traits/blocks/overworld/dirt_worked.json new file mode 100644 index 00000000..118714f4 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/dirt_worked.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "traits": "earth:5 strength:3", + "items": [ + "minecraft:packed_mud", + "minecraft:decorated_pot", + "minecraft:reinforced_deepslate", + "minecraft:mud_bricks", + "minecraft:farmland" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/energised.json b/src/main/resources/data/minecraft/traits/blocks/overworld/energised.json new file mode 100644 index 00000000..9e26b500 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/energised.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "power:4", + "items": [ + "minecraft:coal_block" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/infested_stone.json b/src/main/resources/data/minecraft/traits/blocks/overworld/infested_stone.json new file mode 100644 index 00000000..4fd0a29b --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/infested_stone.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "traits": "life:2 blood:1 earth:6", + "items": [ + "minecraft:infested_cracked_stone_bricks", + "minecraft:infested_stone", + "minecraft:infested_stone_bricks", + "minecraft:infested_chiseled_stone_bricks", + "minecraft:infested_deepslate", + "minecraft:infested_cobblestone", + "minecraft:infested_mossy_stone_bricks" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_objects_studicious.json b/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_objects_studicious.json new file mode 100644 index 00000000..d37f0009 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_objects_studicious.json @@ -0,0 +1,10 @@ +{ + "replace": false, + "traits": "knowledge:9 life:3 water:9", + "items": [ + "minecraft:ochre_froglight", + "minecraft:verdant_froglight", + "minecraft:pearlescent_froglight", + "minecraft:frogspawn" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_worked.json b/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_worked.json new file mode 100644 index 00000000..7b9e89ae --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/intellectual_worked.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "knowledge:9 order:3 strength:3", + "items": [ + "minecraft:chiseled_bookshelf" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/living_matter.json b/src/main/resources/data/minecraft/traits/blocks/overworld/living_matter.json index 93ab077f..5f51c2ae 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/living_matter.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/living_matter.json @@ -2,37 +2,16 @@ "replace": false, "traits": "earth:1 life:3", "items": [ - "minecraft:oak_sapling", - "minecraft:spruce_sapling", - "minecraft:birch_sapling", - "minecraft:acacia_sapling", - "minecraft:dark_oak_sapling", - "minecraft:oak_leaves", - "minecraft:spruce_leaves", - "minecraft:birch_leaves", - "minecraft:jungle_leaves", - "minecraft:acacia_leaves", - "minecraft:dark_oak_leaves", + "#minecraft:saplings", + "#minecraft:leaves", "minecraft:azalea_leaves", - "minecraft:flowering_azalea_leaves", + "#minecraft:flowers", "minecraft:grass", "minecraft:fern", "minecraft:azalea", "minecraft:flowering_azalea", "minecraft:seagrass", "minecraft:sea_pickle", - "minecraft:dandelion", - "minecraft:poppy", - "minecraft:blue_orchid", - "minecraft:allium", - "minecraft:azure_bluet", - "minecraft:red_tulip", - "minecraft:orange_tulip", - "minecraft:white_tulip", - "minecraft:pink_tulip", - "minecraft:oxeye_daisy", - "minecraft:cornflower", - "minecraft:lily_of_the_valley", "minecraft:wither_rose", "minecraft:spore_blossom", "minecraft:brown_mushroom", @@ -53,21 +32,9 @@ "minecraft:glow_berries", "minecraft:carrot", "minecraft:potato", - "minecraft:tube_coral_block", - "minecraft:brain_coral_block", - "minecraft:bubble_coral_block", - "minecraft:fire_coral_block", - "minecraft:horn_coral_block", - "minecraft:tube_coral", - "minecraft:brain_coral", - "minecraft:bubble_coral", - "minecraft:fire_coral", - "minecraft:horn_coral", - "minecraft:tube_coral_fan", - "minecraft:brain_coral_fan", - "minecraft:bubble_coral_fan", - "minecraft:fire_coral_fan", - "minecraft:horn_coral_fan", + "#c:corals", + "#c:coral_fans", + "#c:coral_blocks", "minecraft:dragon_egg", "minecraft:turtle_egg", "minecraft:glow_lichen", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/loose_materials_from_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/loose_materials_from_ground.json index c77ab962..1e185154 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/loose_materials_from_ground.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/loose_materials_from_ground.json @@ -2,24 +2,9 @@ "replace": false, "traits": "earth:1 chaos:2 order:-1", "items": [ - "minecraft:sand", + "#minecraft:sand", "minecraft:gravel", - - "minecraft:white_concrete_powder", - "minecraft:orange_concrete_powder", - "minecraft:magenta_concrete_powder", - "minecraft:light_blue_concrete_powder", - "minecraft:yellow_concrete_powder", - "minecraft:lime_concrete_powder", - "minecraft:pink_concrete_powder", - "minecraft:gray_concrete_powder", - "minecraft:light_gray_concrete_powder", - "minecraft:cyan_concrete_powder", - "minecraft:purple_concrete_powder", - "minecraft:blue_concrete_powder", - "minecraft:brown_concrete_powder", - "minecraft:green_concrete_powder", - "minecraft:red_concrete_powder", - "minecraft:black_concrete_powder" + "minecraft:suspicious_gravel", + "#c:concrete_powders" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/materials_from_the_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/materials_from_the_ground.json index 45d1f540..727e1b52 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/materials_from_the_ground.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/materials_from_the_ground.json @@ -12,7 +12,7 @@ "minecraft:clay", - "minecraft:coal_ore", + "minecraft:coal_ore", "minecraft:deepslate_coal_ore", "minecraft:iron_ore", "minecraft:deepslate_iron_ore", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/mechanical_with_organic_components.json b/src/main/resources/data/minecraft/traits/blocks/overworld/mechanical_with_organic_components.json index a7cabb62..f84cbc4a 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/mechanical_with_organic_components.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/mechanical_with_organic_components.json @@ -4,68 +4,23 @@ "items": [ "minecraft:tnt", "minecraft:trapped_chest", - "minecraft:shulker_box", - "minecraft:white_shulker_box", - "minecraft:orange_shulker_box", - "minecraft:magenta_shulker_box", - "minecraft:light_blue_shulker_box", - "minecraft:yellow_shulker_box", - "minecraft:lime_shulker_box", - "minecraft:pink_shulker_box", - "minecraft:gray_shulker_box", - "minecraft:light_gray_shulker_box", - "minecraft:cyan_shulker_box", - "minecraft:purple_shulker_box", - "minecraft:blue_shulker_box", - "minecraft:brown_shulker_box", - "minecraft:green_shulker_box", - "minecraft:red_shulker_box", - "minecraft:black_shulker_box", + "#c:shulker_boxes", "minecraft:ladder", "minecraft:bookshelf", "minecraft:sculk_sensor", - "minecraft:chest", + "#c:chests", "minecraft:crafting_table", "minecraft:hay_block", "minecraft:carved_pumpkin", "minecraft:jack_o_lantern", - - "minecraft:oak_button", - "minecraft:spruce_button", - "minecraft:birch_button", - "minecraft:jungle_button", - "minecraft:acacia_button", - "minecraft:dark_oak_button", - "minecraft:oak_pressure_plate", - "minecraft:spruce_pressure_plate", - "minecraft:birch_pressure_plate", - "minecraft:jungle_pressure_plate", - "minecraft:acacia_pressure_plate", - "minecraft:dark_oak_pressure_plate", + "#minecraft:wooden_buttons", + "#minecraft:wooden_pressure_plates", + "#minecraft:wooden_doors", + "#minecraft:wooden_trapdoors", + "#minecraft:fence_gates", - "minecraft:oak_door", - "minecraft:spruce_door", - "minecraft:birch_door", - "minecraft:jungle_door", - "minecraft:acacia_door", - "minecraft:dark_oak_door", - - "minecraft:oak_trapdoor", - "minecraft:spruce_trapdoor", - "minecraft:birch_trapdoor", - "minecraft:jungle_trapdoor", - "minecraft:acacia_trapdoor", - "minecraft:dark_oak_trapdoor", - - "minecraft:oak_fence_gate", - "minecraft:spruce_fence_gate", - "minecraft:birch_fence_gate", - "minecraft:jungle_fence_gate", - "minecraft:acacia_fence_gate", - "minecraft:dark_oak_fence_gate", - "minecraft:composter", "minecraft:barrel" ] diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_dead_matter.json b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_dead_matter.json index 8ed34693..dfe96c94 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_dead_matter.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_dead_matter.json @@ -19,12 +19,14 @@ "minecraft:dead_fire_coral_fan", "minecraft:dead_horn_coral_fan", "minecraft:dried_kelp_block", + "minecraft:bundle", "minecraft:skeleton_skull", "minecraft:wither_skeleton_skull", "minecraft:player_head", "minecraft:zombie_head", "minecraft:creeper_head", - "minecraft:dragon_head" + "minecraft:dragon_head", + "minecraft:piglin_head" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived.json b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived.json index fe8c9136..d59ad4e6 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived.json @@ -2,78 +2,20 @@ "replace": false, "traits": "earth:2 life:1", "items": [ - "minecraft:oak_leaves", - "minecraft:oak_planks", - "minecraft:oak_log", - "minecraft:oak_wood", - "minecraft:oak_slab", - "minecraft:oak_fence", - "minecraft:oak_stairs", - - "minecraft:spruce_leaves", - "minecraft:spruce_planks", - "minecraft:spruce_log", - "minecraft:spruce_wood", - "minecraft:spruce_slab", - "minecraft:spruce_fence", - "minecraft:spruce_stairs", - - "minecraft:birch_leaves", - "minecraft:birch_planks", - "minecraft:birch_log", - "minecraft:birch_wood", - "minecraft:birch_slab", - "minecraft:birch_fence", - "minecraft:birch_stairs", - - "minecraft:jungle_leaves", - "minecraft:jungle_planks", - "minecraft:jungle_log", - "minecraft:jungle_wood", - "minecraft:jungle_slab", - "minecraft:jungle_fence", - "minecraft:jungle_stairs", - - "minecraft:acacia_leaves", - "minecraft:acacia_planks", - "minecraft:acacia_log", - "minecraft:acacia_wood", - "minecraft:acacia_slab", - "minecraft:acacia_fence", - "minecraft:acacia_stairs", - - "minecraft:stripped_oak_log", - "minecraft:stripped_spruce_log", - "minecraft:stripped_birch_log", - "minecraft:stripped_jungle_log", - "minecraft:stripped_acacia_log", - - "minecraft:stripped_oak_wood", - "minecraft:stripped_spruce_wood", - "minecraft:stripped_birch_wood", - "minecraft:stripped_jungle_wood", - "minecraft:stripped_acacia_wood", + "#minecraft:planks", + "#minecraft:bamboo_blocks", + "#minecraft:logs_that_burn", + "#minecraft:wooden_stairs", + "#minecraft:wooden_slabs", + "#minecraft:wooden_fences", + "minecraft:bamboo_mosaic", + "minecraft:mangrove_roots", + "minecraft:bowl", + "minecraft:paper", "minecraft:sponge", "minecraft:cobweb", - "minecraft:white_wool", - "minecraft:orange_wool", - "minecraft:magenta_wool", - "minecraft:light_blue_wool", - "minecraft:yellow_wool", - "minecraft:lime_wool", - "minecraft:pink_wool", - "minecraft:gray_wool", - "minecraft:light_gray_wool", - "minecraft:cyan_wool", - "minecraft:purple_wool", - "minecraft:blue_wool", - "minecraft:brown_wool", - "minecraft:green_wool", - "minecraft:red_wool", - "minecraft:black_wool", - "minecraft:kelp", "minecraft:cake", "minecraft:chorus_plant", @@ -84,40 +26,6 @@ "minecraft:red_mushroom_block", "minecraft:mushroom_stem", - "minecraft:white_carpet", - "minecraft:orange_carpet", - "minecraft:magenta_carpet", - "minecraft:light_blue_carpet", - "minecraft:yellow_carpet", - "minecraft:lime_carpet", - "minecraft:pink_carpet", - "minecraft:gray_carpet", - "minecraft:light_gray_carpet", - "minecraft:cyan_carpet", - "minecraft:purple_carpet", - "minecraft:blue_carpet", - "minecraft:brown_carpet", - "minecraft:green_carpet", - "minecraft:red_carpet", - "minecraft:black_carpet", - - "minecraft:white_bed", - "minecraft:orange_bed", - "minecraft:magenta_bed", - "minecraft:light_blue_bed", - "minecraft:yellow_bed", - "minecraft:lime_bed", - "minecraft:pink_bed", - "minecraft:gray_bed", - "minecraft:light_gray_bed", - "minecraft:cyan_bed", - "minecraft:purple_bed", - "minecraft:blue_bed", - "minecraft:brown_bed", - "minecraft:green_bed", - "minecraft:red_bed", - "minecraft:black_bed", - "minecraft:slime_block", "minecraft:honey_block", "minecraft:honeycomb_block", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived_dark.json b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived_dark.json index f0020cac..97dac6f0 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived_dark.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/organic_plant_derived_dark.json @@ -4,7 +4,7 @@ "items": [ "minecraft:dark_oak_leaves", "minecraft:dark_oak_planks", - "minecraft:dark_oak_log", + "#minecraft:dark_oak_logs", "minecraft:dark_oak_stairs", "minecraft:stripped_dark_oak_log", "minecraft:stripped_dark_oak_wood", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/refined_ores.json b/src/main/resources/data/minecraft/traits/blocks/overworld/refined_ores.json index 12e85369..eeaaab67 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/refined_ores.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/refined_ores.json @@ -2,7 +2,7 @@ "replace": false, "traits": "strength:1 earth:1 knowledge:2", "items": [ - "minecraft:coal_block", + "#c:ores", "minecraft:emerald_block", "minecraft:iron_block", "minecraft:copper_block", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/rocks_and_rock_derived.json b/src/main/resources/data/minecraft/traits/blocks/overworld/rocks_and_rock_derived.json index 3c6a3758..0ba39e40 100644 --- a/src/main/resources/data/minecraft/traits/blocks/overworld/rocks_and_rock_derived.json +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/rocks_and_rock_derived.json @@ -27,6 +27,11 @@ "minecraft:smooth_sandstone", "minecraft:smooth_stone", + "#minecraft:stone_bricks", + "#minecraft:stone_tool_materials", + "#minecraft:stone_crafting_materials", + "#minecraft:walls", + "minecraft:brick_stairs", "minecraft:stone_brick_stairs", "minecraft:brick_wall", diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/skulk.json b/src/main/resources/data/minecraft/traits/blocks/overworld/skulk.json new file mode 100644 index 00000000..0ba1791c --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/skulk.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "traits": "darkness:11 blood:1 earth:2", + "items": [ + "minecraft:sculk", + "minecraft:sculk_shrieker", + "minecraft:sculk_catalyst", + "minecraft:echo_shard", + "minecraft:sculk_vein" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/skulk_worked.json b/src/main/resources/data/minecraft/traits/blocks/overworld/skulk_worked.json new file mode 100644 index 00000000..01f152a9 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/skulk_worked.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "darkness:11 blood:1 earth:2 focus:18", + "items": [ + "minecraft:calibrated_sculk_sensor" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/soft_and_kind.json b/src/main/resources/data/minecraft/traits/blocks/overworld/soft_and_kind.json new file mode 100644 index 00000000..e4848f3e --- /dev/null +++ b/src/main/resources/data/minecraft/traits/blocks/overworld/soft_and_kind.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "traits": "kindness:3", + "items": [ + "#minecraft:beds", + "#minecraft:wool", + "#minecraft:wool_carpets" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/animal_horns.json b/src/main/resources/data/minecraft/traits/items/overworld/animal_horns.json new file mode 100644 index 00000000..bf492b8f --- /dev/null +++ b/src/main/resources/data/minecraft/traits/items/overworld/animal_horns.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "strength:3 life:1 kindness:-1", + "items": [ + "minecraft:goat_horn" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/chaotic.json b/src/main/resources/data/minecraft/traits/items/overworld/chaotic.json index 0e1627c7..d6718786 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/chaotic.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/chaotic.json @@ -1,9 +1,7 @@ { "replace": false, - "traits": "chaos:5", + "traits": "chaos:15", "items": [ - "minecraft:gunpowder", - "minecraft:bow", - "minecraft:tnt_minecart" + "minecraft:string" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/chaotic_worked.json b/src/main/resources/data/minecraft/traits/items/overworld/chaotic_worked.json new file mode 100644 index 00000000..0e1627c7 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/items/overworld/chaotic_worked.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "traits": "chaos:5", + "items": [ + "minecraft:gunpowder", + "minecraft:bow", + "minecraft:tnt_minecart" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/clothing_bloody.json b/src/main/resources/data/minecraft/traits/items/overworld/clothing_bloody.json index 251f4f9c..28e5b60c 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/clothing_bloody.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/clothing_bloody.json @@ -7,6 +7,7 @@ "minecraft:leather_chestplate", "minecraft:leather_leggings", "minecraft:leather_boots", - "minecraft:leather_horse_armor" + "minecraft:leather_horse_armor", + "minecraft:rabbit_hide" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json index dda3eda8..e65442d1 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json @@ -2,14 +2,10 @@ "replace": false, "traits": "famine:-0.5 life:-1 knowledge:2", "items": [ - "minecraft:cooked_chicken", - "minecraft:cooked_beef", + "#c:cooked_meats", + "#c:cooked_fish", "minecraft:fermented_spider_eye", - "minecraft:cooked_mutton", - "minecraft:cooked_cod", - "minecraft:cooked_salmon", - "minecraft:cooked_porkchop", - "minecraft:cooked_rabbit", - "minecraft:rabbit_stew" + "#unicopia:food_types/cooked_fish", + "#unicopia:food_types/cooked_meat" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_fruits.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_fruits.json new file mode 100644 index 00000000..f5ec8152 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_fruits.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "kindness:3 happiness:9 water:2", + "items": [ + "c:fruits" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based.json index 8f31615b..a04d3f7e 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based.json @@ -15,6 +15,8 @@ "minecraft:baked_potato": "earth:2", "minecraft:poisonous_potato": "earth:2 poison:1", "minecraft:melon_slice": "life:1", - "minecraft:cookie": "happiness:6" + "minecraft:cookie": "happiness:6", + "#c:grain": "life:3 earth:3", + "#unicopia:food_types/raw_sea_vegitable": "life:2 water:10" } } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based_and_modified.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based_and_modified.json index 7b4d7df1..fbabbc6e 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based_and_modified.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_plant_based_and_modified.json @@ -5,6 +5,8 @@ "minecraft:golden_apple", "minecraft:enchanted_golden_apple", "minecraft:golden_carrot", - "minecraft:honey_bottle" + "minecraft:honey_bottle", + "#c:crops", + "#unicopia:food_types/forage_edible_filling" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json index 79609dc2..07371361 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json @@ -2,10 +2,6 @@ "replace": false, "traits": "blood:1 famine:-2", "items": [ - "minecraft:beef", - "minecraft:rabbit", - "minecraft:porkchop", - "minecraft:chicken", - "minecraft:mutton" + "#c:raw_meats" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_from_sea.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_from_sea.json index 4c4b41e4..a5a72f4e 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_from_sea.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_from_sea.json @@ -2,8 +2,7 @@ "replace": false, "traits": "life:1 famine:-2 water:5", "items": [ - "minecraft:cod", - "minecraft:salmon", - "minecraft:tropical_fish" + "#c:raw_fish", + "#unicopia:food_types/raw_fish" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_poisoned.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_poisoned.json index fb4e1c40..37031631 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_poisoned.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat_poisoned.json @@ -3,6 +3,7 @@ "traits": "blood:1 famine:2 poison:4", "items": [ "minecraft:spider_eye", - "minecraft:rotten_flesh" + "minecraft:rotten_flesh", + "#unicopia:food_types/forage_nauseating" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/full_o_life.json b/src/main/resources/data/minecraft/traits/items/overworld/full_o_life.json index 86a1c75c..9f1ba84a 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/full_o_life.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/full_o_life.json @@ -2,14 +2,13 @@ "replace": false, "traits": "life:10 earth:2", "items": [ - "minecraft:pumpkin_seeds", - "minecraft:beetroot_seeds", - "minecraft:melon_seeds", + "#c:seeds", + "#minecraft:villager_plantable_seeds", "minecraft:cocoa_beans", "minecraft:egg", - "minecraft:wheat_seeds", "minecraft:wheat", "minecraft:bone_meal", - "minecraft:honeycomb" + "minecraft:honeycomb", + "minecraft:sniffer_egg" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/full_o_life_wet.json b/src/main/resources/data/minecraft/traits/items/overworld/full_o_life_wet.json index 95b180c3..95fc5874 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/full_o_life_wet.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/full_o_life_wet.json @@ -3,10 +3,6 @@ "traits": "water:5 life:5", "items": [ "minecraft:milk_bucket", - "minecraft:pufferfish_bucket", - "minecraft:salmon_bucket", - "minecraft:cod_bucket", - "minecraft:tropical_fish_bucket", - "minecraft:axolotl_bucket" + "#c:entity_water_buckets" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/full_o_water.json b/src/main/resources/data/minecraft/traits/items/overworld/full_o_water.json index 0b477073..c3e0b2ce 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/full_o_water.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/full_o_water.json @@ -2,6 +2,6 @@ "replace": false, "traits": "water:7", "items": [ - "minecraft:water_bucket" + "#c:water_buckets" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects.json b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects.json index 529835ec..4857a23d 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects.json @@ -4,22 +4,7 @@ "items": [ "minecraft:loom", "minecraft:lead", - "minecraft:writable_book", - "minecraft:white_dye", - "minecraft:orange_dye", - "minecraft:magenta_dye", - "minecraft:light_blue_dye", - "minecraft:yellow_dye", - "minecraft:lime_dye", - "minecraft:pink_dye", - "minecraft:gray_dye", - "minecraft:light_gray_dye", - "minecraft:cyan_dye", - "minecraft:purple_dye", - "minecraft:blue_dye", - "minecraft:brown_dye", - "minecraft:green_dye", - "minecraft:red_dye", - "minecraft:black_dye" + "#minecraft:bookshelf_books", + "#c:dyes" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_enchanted.json b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_enchanted.json index cb5498db..b54f4773 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_enchanted.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_enchanted.json @@ -5,6 +5,7 @@ "minecraft:experience_bottle", "minecraft:firework_rocket", "minecraft:firework_star", - "minecraft:enchanted_book" + "minecraft:enchanted_book", + "minecraft:recovery_compass" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_focused.json b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_focused.json index a99e32a6..032e7c35 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_focused.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_focused.json @@ -4,6 +4,8 @@ "items": [ "minecraft:potion", "minecraft:glass_bottle", - "minecraft:ender_eye" + "minecraft:ender_eye", + "#minecraft:trim_materials", + "minecraft:brush" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_studicious.json b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_studicious.json index 5b40675d..97e890e1 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_studicious.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/intellectual_objects_studicious.json @@ -2,6 +2,10 @@ "replace": false, "traits": "knowledge:9 order:3", "items": [ + "#minecraft:decorated_pot_sherds", + "#minecraft:trim_templates", + "#minecraft:signs", + "#minecraft:hanging_signs", "minecraft:flower_banner_pattern", "minecraft:creeper_banner_pattern", "minecraft:skull_banner_pattern", diff --git a/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived.json b/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived.json new file mode 100644 index 00000000..fc16efe7 --- /dev/null +++ b/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived.json @@ -0,0 +1,30 @@ +{ + "replace": false, + "traits": "earth:2 life:1", + "items": [ + "#minecraft:planks", + "#minecraft:logs_that_burn", + "#minecraft:wooden_stairs", + "#minecraft:wooden_slabs", + "#minecraft:wooden_fences", + + "minecraft:sponge", + "minecraft:cobweb", + + "minecraft:kelp", + "minecraft:cake", + "minecraft:chorus_plant", + "minecraft:moss_carpet", + "minecraft:mossy_cobblestone", + + "minecraft:brown_mushroom_block", + "minecraft:red_mushroom_block", + "minecraft:mushroom_stem", + + "minecraft:slime_block", + "minecraft:honey_block", + "minecraft:honeycomb_block", + "minecraft:bee_nest", + "minecraft:beehive" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived_artificial.json b/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived_artificial.json index e31599bf..3c81a2ee 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived_artificial.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/organic_plant_derived_artificial.json @@ -2,22 +2,7 @@ "replace": false, "traits": "order:1 knowledge:3", "items": [ - "minecraft:bowl", - "minecraft:string", - "minecraft:paper", - "minecraft:bundle", - "minecraft:oak_sign", - "minecraft:spruce_sign", - "minecraft:birch_sign", - "minecraft:jungle_sign", - "minecraft:acacia_sign", - "minecraft:dark_oak_sign", - "minecraft:oak_boat", - "minecraft:spruce_boat", - "minecraft:birch_boat", - "minecraft:jungle_boat", - "minecraft:acacia_boat", - "minecraft:dark_oak_boat", - "minecraft:rabbit_hide" + "#minecraft:chest_boats", + "#c:boats" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/special.json b/src/main/resources/data/minecraft/traits/items/overworld/special.json index e1ab5a93..b3a8525c 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/special.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/special.json @@ -15,6 +15,7 @@ "minecraft:slime_ball": "rot:2", "minecraft:ink_sac": "darkness:4", "minecraft:glow_ink_sac": "focus:1 chaos:3", - "minecraft:nautilus_shell": "water:6 life:3" + "minecraft:nautilus_shell": "water:6 life:3", + "minecraft:disc_fragment_5": "knowledge:1 darkness:1" } } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/tools.json b/src/main/resources/data/minecraft/traits/items/overworld/tools.json index 9799bed0..2c15bafb 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/tools.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/tools.json @@ -3,30 +3,7 @@ "traits": "order:-2 kindness:1 strength:3", "items": [ "minecraft:shears", - - "minecraft:wooden_shovel", - "minecraft:wooden_pickaxe", - "minecraft:wooden_axe", - "minecraft:wooden_hoe", - - "minecraft:stone_shovel", - "minecraft:stone_pickaxe", - "minecraft:stone_axe", - "minecraft:stone_hoe", - - "minecraft:golden_shovel", - "minecraft:golden_pickaxe", - "minecraft:golden_axe", - "minecraft:golden_hoe", - - "minecraft:iron_shovel", - "minecraft:iron_pickaxe", - "minecraft:iron_axe", - "minecraft:iron_hoe", - - "minecraft:diamond_shovel", - "minecraft:diamond_pickaxe", - "minecraft:diamond_axe", - "minecraft:diamond_hoe" + "#minecraft:tools", + "#c:tools" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/overworld/uplifting_trinkets.json b/src/main/resources/data/minecraft/traits/items/overworld/uplifting_trinkets.json index 7c7927a7..b0afe26c 100644 --- a/src/main/resources/data/minecraft/traits/items/overworld/uplifting_trinkets.json +++ b/src/main/resources/data/minecraft/traits/items/overworld/uplifting_trinkets.json @@ -2,18 +2,6 @@ "replace": false, "traits": "happiness:7 focus:2", "items": [ - "minecraft:music_disc_13", - "minecraft:music_disc_cat", - "minecraft:music_disc_blocks", - "minecraft:music_disc_chirp", - "minecraft:music_disc_far", - "minecraft:music_disc_mall", - "minecraft:music_disc_mellohi", - "minecraft:music_disc_stal", - "minecraft:music_disc_strad", - "minecraft:music_disc_ward", - "minecraft:music_disc_11", - "minecraft:music_disc_wait", - "minecraft:music_disc_pigstep" + "#minecraft:music_discs" ] } \ No newline at end of file diff --git a/src/main/resources/data/minecraft/traits/items/underworld/weapons.json b/src/main/resources/data/minecraft/traits/items/underworld/weapons.json index 8b700489..b47c5e8d 100644 --- a/src/main/resources/data/minecraft/traits/items/underworld/weapons.json +++ b/src/main/resources/data/minecraft/traits/items/underworld/weapons.json @@ -2,6 +2,7 @@ "replace": false, "traits": "order:-2 kindness:-3 strength:13 darkness:9 blood:4", "items": [ - "minecraft:netherite_sword" + "minecraft:netherite_sword", + "minecraft:netherite_upgrade_smithing_template" ] } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/tags/items/badges.json b/src/main/resources/data/unicopia/tags/items/badges.json new file mode 100644 index 00000000..16481fc4 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/badges.json @@ -0,0 +1,13 @@ +{ + "replace": false, + "values": [ + "unicopia:earth_badge", + "unicopia:unicorn_badge", + "unicopia:pegasus_badge", + "unicopia:changeling_badge", + "unicopia:bat_badge", + "unicopia:kirin_badge", + "unicopia:alicorn_badge", + "unicopia:hippogriff_badge" + ] +} diff --git a/src/main/resources/data/unicopia/tags/items/chitin.json b/src/main/resources/data/unicopia/tags/items/chitin.json new file mode 100644 index 00000000..aff5bfad --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/chitin.json @@ -0,0 +1,12 @@ +{ + "replace": false, + "values": [ + "unicopia:chitin", + "unicopia:surface_chitin", + "unicopia:chiselled_chitin", + "unicopia:chiselled_chitin_slab", + "unicopia:chiselled_chitin_stairs", + "unicopia:chiselled_chitin_hull", + "unicopia:chitin_spikes" + ] +} diff --git a/src/main/resources/data/unicopia/tags/items/cloud_slabs.json b/src/main/resources/data/unicopia/tags/items/cloud_slabs.json index a7337a0e..d17c86ae 100644 --- a/src/main/resources/data/unicopia/tags/items/cloud_slabs.json +++ b/src/main/resources/data/unicopia/tags/items/cloud_slabs.json @@ -3,6 +3,7 @@ "values": [ "unicopia:cloud_slab", "unicopia:dense_cloud_slab", + "unicopia:cloud_brick_slab", "unicopia:etched_cloud_slab", "unicopia:cloud_plank_slab" ] diff --git a/src/main/resources/data/unicopia/tags/items/cloud_stairs.json b/src/main/resources/data/unicopia/tags/items/cloud_stairs.json index cbbba0dc..49b0d346 100644 --- a/src/main/resources/data/unicopia/tags/items/cloud_stairs.json +++ b/src/main/resources/data/unicopia/tags/items/cloud_stairs.json @@ -3,7 +3,9 @@ "values": [ "unicopia:cloud_stairs", "unicopia:dense_cloud_stairs", + "unicopia:cloud_brick_stairs", "unicopia:etched_cloud_stairs", - "unicopia:cloud_plank_stairs" + "unicopia:cloud_plank_stairs", + "unicopia:cloud_chest" ] } diff --git a/src/main/resources/data/unicopia/tags/items/floats_on_clouds.json b/src/main/resources/data/unicopia/tags/items/floats_on_clouds.json index 189ac69b..cc18beba 100644 --- a/src/main/resources/data/unicopia/tags/items/floats_on_clouds.json +++ b/src/main/resources/data/unicopia/tags/items/floats_on_clouds.json @@ -5,6 +5,8 @@ "#unicopia:cloud_slabs", "#unicopia:cloud_stairs", "#unicopia:cloud_beds", - "unicopia:cloud_pillar" + "unicopia:cloud_pillar", + "unicopia:carved_cloud", + "unicopia:shaping_bench" ] } diff --git a/src/main/resources/data/unicopia/tags/items/has_no_traits.json b/src/main/resources/data/unicopia/tags/items/has_no_traits.json new file mode 100644 index 00000000..c29a2af1 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/items/has_no_traits.json @@ -0,0 +1,20 @@ +{ + "replace": false, + "values": [ + "minecraft:air", + "minecraft:spawner", + "minecraft:structure_void", + "minecraft:structure_block", + "minecraft:command_block", + "minecraft:chain_command_block", + "minecraft:repeating_command_block", + "minecraft:light", + "minecraft:jigsaw", + "minecraft:barrier", + "minecraft:bedrock", + "minecraft:end_portal_frame", + "minecraft:debug_stick", + "minecraft:command_block_minecart", + "#unicopia:badges" + ] +} diff --git a/src/main/resources/data/unicopia/traits/chitin.json b/src/main/resources/data/unicopia/traits/chitin.json new file mode 100644 index 00000000..aeddbfd4 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/chitin.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "traits": "earth:9 darkness:8 kindness:-3", + "items": [ + "#unicopia:chitin", + "unicopia:slime_pustule", + "unicopia:carapace" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/edible_plant_based.json b/src/main/resources/data/unicopia/traits/edible_plant_based.json deleted file mode 100644 index ca6cee4d..00000000 --- a/src/main/resources/data/unicopia/traits/edible_plant_based.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "replace": false, - "items": { - "unicopia:green_apple": "life:3", - "unicopia:sweet_apple": "life:3", - "unicopia:sour_apple": "life:3", - "unicopia:zap_apple": "chaos:5", - "unicopia:rotten_apple": "rot:2", - "unicopia:cooked_zap_apple": "chaos:10", - "unicopia:daffodil_daisy_sandwich": "life:2 earth:1", - "unicopia:hay_burger": "life:2 earth:1", - "unicopia:hay_fries": "life:1 earth:2", - "unicopia:wheat_worms": "life:4 earth:6", - "unicopia:cider": "chaos:1 darkness:1", - "unicopia:juice": "chaos:-1 darkness:-9", - "unicopia:burned_juice": "chaos:-1 darkness:-19" - } -} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/food.json b/src/main/resources/data/unicopia/traits/food.json new file mode 100644 index 00000000..0b8e0830 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/food.json @@ -0,0 +1,33 @@ +{ + "replace": false, + "items": { + "unicopia:green_apple": "life:3", + "unicopia:sweet_apple": "life:3", + "unicopia:sour_apple": "life:3", + "unicopia:zap_apple": "chaos:5", + "unicopia:rotten_apple": "rot:2", + "unicopia:cooked_zap_apple": "chaos:10", + "unicopia:daffodil_daisy_sandwich": "life:2 earth:1", + "unicopia:hay_burger": "life:2 earth:1", + "unicopia:hay_fries": "life:1 earth:2", + "unicopia:crispy_hay_fries": "life:1 earth:2 happiness:2", + "unicopia:wheat_worms": "life:4 earth:6", + "unicopia:cider": "chaos:1 darkness:1", + "unicopia:juice": "chaos:-1 darkness:-9", + "unicopia:burned_juice": "chaos:-1 darkness:-19", + "unicopia:pineapple_crown": "life:6", + "unicopia:banana": "life:5 generosity:3", + "unicopia:mango": "earth:2 life:1", + "unicopia:pineapple": "life4 generosity:10", + "unicopia:pinecone": "life:1 happiness:-1", + "#unicopia:oats": "life:1 happiness:1", + "unicopia:apple_pie_slice": "life4 generosity:10", + "unicopia:oatmeal": "happiness:4", + "unicopia:imported_oats": "life:1 happiness:2", + "#unicopia:food_types/candy": "earth:7 strength:3", + "unicopia:zap_bulb": "life:6 power:10", + "unicopia:muffin": "happiness:11", + "unicopia:horse_shoe_fries": "earth:1 strength:11", + "#unicopia:acorns": "life:1 earth:4 strength:3" + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/from_the_ground.json b/src/main/resources/data/unicopia/traits/from_the_ground.json index a228169b..a5493530 100644 --- a/src/main/resources/data/unicopia/traits/from_the_ground.json +++ b/src/main/resources/data/unicopia/traits/from_the_ground.json @@ -6,7 +6,11 @@ "unicopia:gemstone": "order:1 power:-1", "unicopia:pebbles": "earth:3", "unicopia:rock": "earth:6", + "unicopia:tom": "earth:11", "unicopia:weird_rock": "earth:16 chaos:9", - "unicopia:rock_stew": "earth:9 chaos:16" + "unicopia:rock_stew": "earth:9 chaos:16", + "unicopia:toast": "earth:3", + "unicopia:burned_toast": "chaos:1 earth:3", + "unicopia:jam_toast": "earth:3 strength:1 power:11" } } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/gilded.json b/src/main/resources/data/unicopia/traits/gilded.json new file mode 100644 index 00000000..797b985b --- /dev/null +++ b/src/main/resources/data/unicopia/traits/gilded.json @@ -0,0 +1,11 @@ +{ + "replace": false, + "traits": "strength:10 generosity:-3", + "items": [ + "unicopia:golden_oak_sapling", + "unicopia:golden_oak_leaves", + "unicopia:golden_oak_log", + "unicopia:golden_wing", + "unicopia:golden_oak_seeds" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/intellectual_objects_ordered.json b/src/main/resources/data/unicopia/traits/intellectual_objects_ordered.json new file mode 100644 index 00000000..3d650d72 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/intellectual_objects_ordered.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "knowledge:9 order:9", + "items": [ + "unicopia:weather_vane", + "unicopia:giant_balloon" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/love.json b/src/main/resources/data/unicopia/traits/love.json new file mode 100644 index 00000000..534e5a40 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/love.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "happiness:10 kindness:10", + "items": [ + "#c:love", + "#unicopia:clouds" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/magical.json b/src/main/resources/data/unicopia/traits/magical.json new file mode 100644 index 00000000..3de09a57 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/magical.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "happiness:10 kindness:10", + "items": [ + "#unicopia:groups/unicorn", + "unicopia:curing_joke" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/organic_living.json b/src/main/resources/data/unicopia/traits/organic_living.json new file mode 100644 index 00000000..8cbca2c4 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/organic_living.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "life:10", + "items": [ + "unicopia:mysterious_egg", + "unicopia:tom" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/organic_plant_derived.json b/src/main/resources/data/unicopia/traits/organic_plant_derived.json deleted file mode 100644 index c2167d62..00000000 --- a/src/main/resources/data/unicopia/traits/organic_plant_derived.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "replace": false, - "traits": "earth:2 life:1", - "items": [ - "unicopia:green_apple_leaves", - "unicopia:sweet_apple_leaves", - "unicopia:sour_apple_leaves", - "unicopia:mango" - ] -} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/organic_plant_derived_artificial.json b/src/main/resources/data/unicopia/traits/organic_plant_derived_artificial.json new file mode 100644 index 00000000..5b8fcd5e --- /dev/null +++ b/src/main/resources/data/unicopia/traits/organic_plant_derived_artificial.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "traits": "order:1 knowledge:3", + "items": [ + "#unicopia:baskets" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/organic_plant_derived_zap.json b/src/main/resources/data/unicopia/traits/organic_plant_derived_zap.json index 172f955c..a5f068dc 100644 --- a/src/main/resources/data/unicopia/traits/organic_plant_derived_zap.json +++ b/src/main/resources/data/unicopia/traits/organic_plant_derived_zap.json @@ -6,6 +6,7 @@ "unicopia:zap_log", "unicopia:stripped_zap_log", "unicopia:zap_wood", - "unicopia:stripped_zap_wood" + "unicopia:stripped_zap_wood", + "unicopia:flowering_zap_leaves" ] } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/shells.json b/src/main/resources/data/unicopia/traits/shells.json new file mode 100644 index 00000000..94fb1693 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/shells.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "traits": "water:7 earth:1 life:1", + "items": [ + "#unicopia:food_types/shells", + "unicopia:shelly" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/soft_and_kind.json b/src/main/resources/data/unicopia/traits/soft_and_kind.json new file mode 100644 index 00000000..c7c44f84 --- /dev/null +++ b/src/main/resources/data/unicopia/traits/soft_and_kind.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "traits": "kindness:3", + "items": [ + "#unicopia:bed_sheets", + "#unicopia:floats_on_clouds", + "#unicopia:pies" + ] +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/special.json b/src/main/resources/data/unicopia/traits/special.json index d8f7a165..961eb18e 100644 --- a/src/main/resources/data/unicopia/traits/special.json +++ b/src/main/resources/data/unicopia/traits/special.json @@ -11,6 +11,11 @@ "unicopia:butterfly": "darkness:4 blood:7", "unicopia:spellbook": "power:18 darkness:7", "unicopia:pegasus_amulet": "power:18 order:10 power:9", - "unicopia:alicorn_amulet": "strength:23 order:-10 power:11 darkness:22" + "unicopia:alicorn_amulet": "strength:23 order:-10 power:11 darkness:22", + "#unicopia:cools_off_kirins": "water:6", + "unicopia:hive": "earth:9 darkness:8 kindness:-3 life:10", + "#unicopia:shades": "darkness:3", + "unicopia:broken_sunglasses": "darkness:3 chaos:3", + "unicopia:salt_cube": "power:3" } } \ No newline at end of file diff --git a/src/main/resources/data/unicopia/traits/uplifting_trinkets.json b/src/main/resources/data/unicopia/traits/uplifting_trinkets.json index ec28da31..9360f22f 100644 --- a/src/main/resources/data/unicopia/traits/uplifting_trinkets.json +++ b/src/main/resources/data/unicopia/traits/uplifting_trinkets.json @@ -5,6 +5,8 @@ "unicopia:music_disc_pet": "order:10 kindness:8 focus:9", "unicopia:music_disc_popular": "order:10 generosity:8 focus:9", "unicopia:music_disc_funk": "chaos:-10", - "unicopia:friendship_bracelet": "generosity:1 order:2 happiness:1" + "unicopia:friendship_bracelet": "generosity:1 order:2 happiness:1", + "unicopia:pearl_necklace": "chaos:7 knowledge:2", + "unicopia:lightning_jar": "chaos:1 knowledge:2 power:8" } } \ No newline at end of file From d4e698fa56f410fba7cc7cdab9a78cf7a82de711 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 29 Jan 2024 19:05:20 +0000 Subject: [PATCH 82/98] Move earth ponies' transmutations to recipes --- .../ability/EarthPonyGrowAbility.java | 109 ++++------- .../unicopia/compat/emi/Main.java | 19 ++ .../unicopia/item/TransformCropsRecipe.java | 176 ++++++++++++++++++ .../minelittlepony/unicopia/item/UItems.java | 1 + .../unicopia/item/URecipes.java | 2 + .../resources/assets/unicopia/lang/en_us.json | 2 + .../unicopia/models/item/plunder_vine.json | 3 + .../unicopia/recipes/growing/curing_joke.json | 12 ++ .../unicopia/recipes/growing/gold_root.json | 12 ++ .../recipes/growing/golden_oak_sapling.json | 12 ++ .../recipes/growing/plunder_vine.json | 12 ++ .../unicopia/recipes/growing/zapling.json | 12 ++ .../unicopia/tags/items/has_no_traits.json | 1 + 13 files changed, 296 insertions(+), 77 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java create mode 100644 src/main/resources/assets/unicopia/models/item/plunder_vine.json create mode 100644 src/main/resources/data/unicopia/recipes/growing/curing_joke.json create mode 100644 src/main/resources/data/unicopia/recipes/growing/gold_root.json create mode 100644 src/main/resources/data/unicopia/recipes/growing/golden_oak_sapling.json create mode 100644 src/main/resources/data/unicopia/recipes/growing/plunder_vine.json create mode 100644 src/main/resources/data/unicopia/recipes/growing/zapling.json diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index b1dcf2a5..61ab54d5 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -1,9 +1,6 @@ package com.minelittlepony.unicopia.ability; -import java.util.HashSet; -import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.function.DoubleSupplier; import java.util.function.Supplier; import com.minelittlepony.unicopia.Race; @@ -12,12 +9,12 @@ import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.block.UBlocks; -import com.minelittlepony.unicopia.block.state.StateUtil; import com.minelittlepony.unicopia.entity.player.Pony; +import com.minelittlepony.unicopia.item.TransformCropsRecipe; +import com.minelittlepony.unicopia.item.URecipes; import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.server.world.BlockDestructionManager; -import com.minelittlepony.unicopia.server.world.UTreeGen; import com.minelittlepony.unicopia.util.TraceHelper; import com.minelittlepony.unicopia.util.VecHelper; @@ -33,7 +30,6 @@ import net.minecraft.particle.ParticleTypes; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.random.Random; import net.minecraft.world.World; import net.minecraft.world.WorldEvents; @@ -139,40 +135,41 @@ public class EarthPonyGrowAbility implements Ability { } private boolean applyDirectly(Pony player, BlockPos pos) { - return TransmutationRecipe.RECIPES.stream() - .filter(recipe -> recipe.matches(player.asWorld(), pos)) - .map(recipe -> recipe.checkPattern(player.asWorld(), pos)) - .filter(result -> result.matchedLocations().size() + 1 >= TransmutationRecipe.MINIMUM_INPUT) - .filter(result -> { - boolean transform = result.shoudTransform(player.asWorld().random); + return player.asWorld().getRecipeManager() + .getAllMatches(URecipes.GROWING, new TransformCropsRecipe.PlacementArea(player, pos), player.asWorld()) + .stream() + .map(recipe -> recipe.checkPattern(player.asWorld(), pos)) + .filter(result -> result.matchedLocations().size() + 1 >= TransformCropsRecipe.MINIMUM_INPUT) + .filter(result -> { + boolean transform = result.shoudTransform(player.asWorld().random); - player.playSound(USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, 1); + player.playSound(USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, 1); - result.matchedLocations().forEach(cell -> { - spawnConversionParticles(player.asWorld(), cell.up(), false); - BlockDestructionManager manager = BlockDestructionManager.of(player.asWorld()); - if (transform) { - if (manager.damageBlock(cell, 8) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) { - player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState()); - player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell))); - } - } else { - if (manager.damageBlock(cell, 4) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) { - player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState()); - player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell))); - } - } - }); - - spawnConversionParticles(player.asWorld(), pos, transform); + result.matchedLocations().forEach(cell -> { + spawnConversionParticles(player.asWorld(), cell.up(), false); + BlockDestructionManager manager = BlockDestructionManager.of(player.asWorld()); if (transform) { - player.asWorld().setBlockState(pos, result.recipe().getResult(player.asWorld(), pos)); + if (manager.damageBlock(cell, 8) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) { + player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState()); + player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell))); + } + } else { + if (manager.damageBlock(cell, 4) >= BlockDestructionManager.MAX_DAMAGE || player.asWorld().random.nextInt(20) == 0) { + player.asWorld().setBlockState(cell, Blocks.DIRT.getDefaultState()); + player.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, cell, Block.getRawIdFromState(player.asWorld().getBlockState(cell))); + } } + }); - return true; - }) - .findFirst() - .isPresent(); + spawnConversionParticles(player.asWorld(), pos, transform); + if (transform) { + player.asWorld().setBlockState(pos, result.recipe().getResult(player.asWorld(), pos)); + } + + return true; + }) + .findFirst() + .isPresent(); } private static void spawnConversionParticles(World w, BlockPos pos, boolean success) { @@ -201,48 +198,6 @@ public class EarthPonyGrowAbility implements Ability { } - private static record TransmutationRecipe(Block input, BlockState output, BlockState material) { - static final List RECIPES = List.of( - new TransmutationRecipe(Blocks.OAK_SAPLING, UTreeGen.GOLDEN_APPLE_TREE.sapling().get().getDefaultState(), Blocks.RAW_GOLD_BLOCK.getDefaultState()), - new TransmutationRecipe(Blocks.CARROTS, UBlocks.GOLD_ROOT.getDefaultState(), Blocks.RAW_GOLD_BLOCK.getDefaultState()), - new TransmutationRecipe(Blocks.CORNFLOWER, UBlocks.CURING_JOKE.getDefaultState(), Blocks.LAPIS_BLOCK.getDefaultState()), - new TransmutationRecipe(Blocks.WITHER_ROSE, UBlocks.PLUNDER_VINE_BUD.getDefaultState(), Blocks.NETHERRACK.getDefaultState()) - ); - static final int RADIUS = 3; - static final int SIDE_LENGTH = (2 * RADIUS) + 1; - static final int AREA = (SIDE_LENGTH * SIDE_LENGTH) - 1; - static final int MINIMUM_INPUT = 9; - - public boolean matches(World world, BlockPos pos) { - return world.getBlockState(pos).isOf(input); - } - - public Result checkPattern(World world, BlockPos pos) { - BlockPos center = pos.down(); - Set matches = new HashSet<>(); - for (BlockPos cell : BlockPos.iterateInSquare(center, RADIUS, Direction.EAST, Direction.NORTH)) { - if (cell.equals(center)) { - continue; - } - if (!world.getBlockState(cell).equals(material)) { - break; - } - matches.add(cell.toImmutable()); - } - return new Result(this, matches); - } - - public BlockState getResult(World world, BlockPos pos) { - return StateUtil.copyState(world.getBlockState(pos), output); - } - - record Result (TransmutationRecipe recipe, Set matchedLocations) { - public boolean shoudTransform(Random random) { - return random.nextInt(TransmutationRecipe.AREA) < matchedLocations().size(); - } - } - } - public interface Growable { boolean grow(World world, BlockState state, BlockPos pos); } diff --git a/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java b/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java index 01adac1f..4159d296 100644 --- a/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java +++ b/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java @@ -13,6 +13,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.item.EnchantableItem; +import com.minelittlepony.unicopia.item.TransformCropsRecipe; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.URecipes; import com.minelittlepony.unicopia.item.group.MultiItem; @@ -20,6 +21,7 @@ import com.minelittlepony.unicopia.item.group.MultiItem; import dev.emi.emi.api.EmiPlugin; import dev.emi.emi.api.EmiRegistry; import dev.emi.emi.api.recipe.EmiRecipeCategory; +import dev.emi.emi.api.recipe.EmiWorldInteractionRecipe; import dev.emi.emi.api.render.EmiTexture; import dev.emi.emi.api.stack.Comparison; import dev.emi.emi.api.stack.EmiStack; @@ -33,8 +35,10 @@ import net.minecraft.util.Identifier; public class Main implements EmiPlugin { static final EmiStack SPELL_BOOK_STATION = EmiStack.of(UItems.SPELLBOOK); static final EmiStack CLOUD_SHAPING_STATION = EmiStack.of(UBlocks.SHAPING_BENCH); + static final EmiStack GROWING_STATION = EmiStack.of(UItems.EARTH_BADGE); static final EmiRecipeCategory SPELL_BOOK_CATEGORY = new EmiRecipeCategory(Unicopia.id("spellbook"), SPELL_BOOK_STATION, SPELL_BOOK_STATION); static final EmiRecipeCategory CLOUD_SHAPING_CATEGORY = new EmiRecipeCategory(Unicopia.id("cloud_shaping"), CLOUD_SHAPING_STATION, CLOUD_SHAPING_STATION); + static final EmiRecipeCategory GROWING_CATEGORY = new EmiRecipeCategory(Unicopia.id("growing"), GROWING_STATION, GROWING_STATION); static final Identifier WIDGETS = Unicopia.id("textures/gui/widgets.png"); static final EmiTexture EMPTY_ARROW = new EmiTexture(WIDGETS, 44, 0, 24, 17); @@ -103,5 +107,20 @@ public class Main implements EmiPlugin { }); } }); + + registry.addCategory(GROWING_CATEGORY); + registry.addWorkstation(GROWING_CATEGORY, GROWING_STATION); + registry.getRecipeManager().listAllOfType(URecipes.GROWING).forEach(recipe -> { + registry.addRecipe(new EmiWorldInteractionRecipe(EmiWorldInteractionRecipe.builder() + .id(recipe.getId()) + .leftInput(EmiStack.of(recipe.getTargetAsItem())) + .rightInput(EmiStack.of(recipe.getCatalyst(), TransformCropsRecipe.MINIMUM_INPUT), true) + .output(EmiStack.of(recipe.getOutput()))) { + @Override + public EmiRecipeCategory getCategory() { + return GROWING_CATEGORY; + } + }); + }); } } diff --git a/src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java b/src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java new file mode 100644 index 00000000..284665ce --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java @@ -0,0 +1,176 @@ +package com.minelittlepony.unicopia.item; + +import java.util.HashSet; +import java.util.Set; + +import com.google.gson.JsonObject; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.minelittlepony.unicopia.entity.player.Pony; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.SingleStackInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeSerializer; +import net.minecraft.recipe.RecipeType; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.EmptyBlockView; +import net.minecraft.world.World; + +public class TransformCropsRecipe implements Recipe { + public static final int RADIUS = 3; + public static final int SIDE_LENGTH = (2 * RADIUS) + 1; + public static final int AREA = (SIDE_LENGTH * SIDE_LENGTH) - 1; + public static final int MINIMUM_INPUT = 9; + + private final Identifier id; + + private final Block target; + private final BlockState catalyst; + private final BlockState output; + + public TransformCropsRecipe(Identifier id, Block target, BlockState catalyst, BlockState output) { + this.id = id; + this.output = output; + this.target = target; + this.catalyst = catalyst; + } + + public ItemStack getTargetAsItem() { + return target.asItem().getDefaultStack(); + } + + public ItemStack getCatalyst() { + return catalyst.getBlock().getPickStack(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, catalyst); + } + + public ItemStack getOutput() { + return output.getBlock().getPickStack(EmptyBlockView.INSTANCE, BlockPos.ORIGIN, output); + } + + @Override + public Identifier getId() { + return id; + } + + @Override + public RecipeSerializer getSerializer() { + return URecipes.TRANSFORM_CROP_SERIALIZER; + } + + @Override + public RecipeType getType() { + return URecipes.GROWING; + } + + @Override + public boolean matches(PlacementArea inventory, World world) { + return world.getBlockState(inventory.position()).isOf(target); + } + + @Override + public ItemStack craft(PlacementArea inventory, DynamicRegistryManager manager) { + return getOutput(manager); + } + + @Override + public ItemStack getOutput(DynamicRegistryManager manager) { + return output.getBlock().asItem().getDefaultStack(); + } + + public Result checkPattern(World world, BlockPos pos) { + BlockPos center = pos.down(); + Set matches = new HashSet<>(); + for (BlockPos cell : BlockPos.iterateInSquare(center, RADIUS, Direction.EAST, Direction.NORTH)) { + if (cell.equals(center)) { + continue; + } + if (!world.getBlockState(cell).equals(catalyst)) { + break; + } + matches.add(cell.toImmutable()); + } + return new Result(this, matches); + } + + public BlockState getResult(World world, BlockPos pos) { + return StateUtil.copyState(world.getBlockState(pos), output); + } + + @Override + public boolean fits(int width, int height) { + return width >= SIDE_LENGTH && height >= SIDE_LENGTH; + } + + public static class Serializer implements RecipeSerializer { + record Intermediate(Block target, BlockState fuel, BlockState output) {} + private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Registries.BLOCK.getCodec().fieldOf("target").forGetter(Intermediate::target), + BlockState.CODEC.fieldOf("consume").forGetter(Intermediate::fuel), + BlockState.CODEC.fieldOf("output").forGetter(Intermediate::output) + ).apply(instance, Intermediate::new)); + + @Override + public TransformCropsRecipe read(Identifier id, JsonObject json) { + Intermediate content = CODEC.decode(JsonOps.INSTANCE, json).result().map(Pair::getFirst).get(); + return new TransformCropsRecipe(id, content.target(), content.fuel(), content.output()); + } + + @Override + public TransformCropsRecipe read(Identifier id, PacketByteBuf buffer) { + return new TransformCropsRecipe(id, + buffer.readRegistryValue(Registries.BLOCK), + Block.getStateFromRawId(buffer.readInt()), + Block.getStateFromRawId(buffer.readInt()) + ); + } + + @Override + public void write(PacketByteBuf buffer, TransformCropsRecipe recipe) { + buffer.writeRegistryValue(Registries.BLOCK, recipe.target); + buffer.writeInt(Block.getRawIdFromState(recipe.catalyst)); + buffer.writeInt(Block.getRawIdFromState(recipe.output)); + } + } + + public static record PlacementArea (Pony pony, BlockPos position) implements SingleStackInventory { + @Override + public ItemStack getStack(int var1) { + return ItemStack.EMPTY; + } + + @Override + public ItemStack removeStack(int slot, int count) { + return ItemStack.EMPTY; + } + + @Override + public void setStack(int slot, ItemStack stack) { } + + @Override + public void markDirty() { } + + @Override + public boolean canPlayerUse(PlayerEntity player) { + return true; + } + } + + public record Result (TransformCropsRecipe recipe, Set matchedLocations) { + public boolean shoudTransform(Random random) { + return random.nextInt(AREA) < matchedLocations().size(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index a5ede2a8..3aa1e5ea 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -48,6 +48,7 @@ public interface UItems { FriendshipBraceletItem FRIENDSHIP_BRACELET = register("friendship_bracelet", new FriendshipBraceletItem(new FabricItemSettings().rarity(Rarity.UNCOMMON)), ItemGroups.TOOLS); + Item PLUNDER_VINE = register("plunder_vine", new BlockItem(UBlocks.PLUNDER_VINE_BUD, new Item.Settings())); Item EMPTY_JAR = register("empty_jar", new JarItem(new Item.Settings().maxCount(16).fireproof(), false, false, false), ItemGroups.FUNCTIONAL); FilledJarItem FILLED_JAR = register("filled_jar", new FilledJarItem(new Item.Settings().maxCount(1).recipeRemainder(EMPTY_JAR))); Item RAIN_CLOUD_JAR = register("rain_cloud_jar", new JarItem(new Item.Settings().maxCount(1).fireproof().recipeRemainder(EMPTY_JAR), true, false, false), ItemGroups.FUNCTIONAL); diff --git a/src/main/java/com/minelittlepony/unicopia/item/URecipes.java b/src/main/java/com/minelittlepony/unicopia/item/URecipes.java index 2c0a8573..497e6d6c 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/URecipes.java +++ b/src/main/java/com/minelittlepony/unicopia/item/URecipes.java @@ -23,6 +23,7 @@ import net.minecraft.util.collection.DefaultedList; public interface URecipes { RecipeType SPELLBOOK = RecipeType.register("unicopia:spellbook"); RecipeType CLOUD_SHAPING = RecipeType.register("unicopia:cloud_shaping"); + RecipeType GROWING = RecipeType.register("unicopia:growing"); RecipeSerializer ZAP_APPLE_SERIALIZER = RecipeSerializer.register("unicopia:crafting_zap_apple", new ZapAppleRecipe.Serializer()); RecipeSerializer GLOWING_SERIALIZER = RecipeSerializer.register("unicopia:crafting_glowing", new SpecialRecipeSerializer<>(GlowingRecipe::new)); @@ -33,6 +34,7 @@ public interface URecipes { RecipeSerializer TRAIT_COMBINING = RecipeSerializer.register("unicopia:spellbook/combining", new SpellEnhancingRecipe.Serializer()); RecipeSerializer SPELL_DUPLICATING = RecipeSerializer.register("unicopia:spellbook/duplicating", new SpellDuplicatingRecipe.Serializer()); RecipeSerializer CLOUD_SHAPING_SERIALIZER = RecipeSerializer.register("unicopia:cloud_shaping", new CuttingRecipe.Serializer<>(CloudShapingRecipe::new) {}); + RecipeSerializer TRANSFORM_CROP_SERIALIZER = RecipeSerializer.register("unicopia:transform_crop", new TransformCropsRecipe.Serializer()); static DefaultedList getIngredients(JsonArray json) { DefaultedList defaultedList = DefaultedList.of(); diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index f409a35f..fe83a05b 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -46,6 +46,7 @@ "item.unicopia.spellbook": "Spellbook", "emi.category.unicopia.spellbook": "Spellbook", "emi.category.unicopia.cloud_shaping": "Shaping", + "emi.category.unicopia.growing": "Growing", "item.unicopia.alicorn_badge": "Alicorn Emblem", "item.unicopia.unicorn_badge": "Unicorn Emblem", @@ -73,6 +74,7 @@ "item.unicopia.love_bucket": "Love Bucket", "item.unicopia.love_mug": "Mug o' Love", + "item.unicopia.plunder_vine": "Plunder Vine", "item.unicopia.empty_jar": "Glass Jar", "item.unicopia.filled_jar": "%s in a Jar", "item.unicopia.rain_cloud_jar": "Rain in a Jar", diff --git a/src/main/resources/assets/unicopia/models/item/plunder_vine.json b/src/main/resources/assets/unicopia/models/item/plunder_vine.json new file mode 100644 index 00000000..32b343c6 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/plunder_vine.json @@ -0,0 +1,3 @@ +{ + "parent": "unicopia:block/plunder_vine_bud" +} diff --git a/src/main/resources/data/unicopia/recipes/growing/curing_joke.json b/src/main/resources/data/unicopia/recipes/growing/curing_joke.json new file mode 100644 index 00000000..61625b41 --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/curing_joke.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:cornflower", + "consume": { + "Name": "minecraft:lapis_block", + "Properties": {} + }, + "output": { + "Name": "unicopia:curing_joke", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/gold_root.json b/src/main/resources/data/unicopia/recipes/growing/gold_root.json new file mode 100644 index 00000000..a6d2d09b --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/gold_root.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:carrots", + "consume": { + "Name": "minecraft:raw_gold_block", + "Properties": {} + }, + "output": { + "Name": "unicopia:gold_root", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/golden_oak_sapling.json b/src/main/resources/data/unicopia/recipes/growing/golden_oak_sapling.json new file mode 100644 index 00000000..b129e3ae --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/golden_oak_sapling.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:oak_sapling", + "consume": { + "Name": "minecraft:raw_gold_block", + "Properties": {} + }, + "output": { + "Name": "unicopia:golden_oak_sapling", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/plunder_vine.json b/src/main/resources/data/unicopia/recipes/growing/plunder_vine.json new file mode 100644 index 00000000..df6a6eac --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/plunder_vine.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:wither_rose", + "consume": { + "Name": "minecraft:netherrack", + "Properties": {} + }, + "output": { + "Name": "unicopia:plunder_vine_bud", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/recipes/growing/zapling.json b/src/main/resources/data/unicopia/recipes/growing/zapling.json new file mode 100644 index 00000000..971c2099 --- /dev/null +++ b/src/main/resources/data/unicopia/recipes/growing/zapling.json @@ -0,0 +1,12 @@ +{ + "type": "unicopia:transform_crop", + "target": "minecraft:dark_oak_sapling", + "consume": { + "Name": "unicopia:chitin", + "Properties": {} + }, + "output": { + "Name": "unicopia:zapling", + "Properties": {} + } +} \ No newline at end of file diff --git a/src/main/resources/data/unicopia/tags/items/has_no_traits.json b/src/main/resources/data/unicopia/tags/items/has_no_traits.json index c29a2af1..1a300515 100644 --- a/src/main/resources/data/unicopia/tags/items/has_no_traits.json +++ b/src/main/resources/data/unicopia/tags/items/has_no_traits.json @@ -15,6 +15,7 @@ "minecraft:end_portal_frame", "minecraft:debug_stick", "minecraft:command_block_minecart", + "unicopia:plunder_vine", "#unicopia:badges" ] } From e15d64ff3dc7509fb6d8ab75c1547f82123b16cf Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 29 Jan 2024 20:30:58 +0000 Subject: [PATCH 83/98] Implement support for displaying chapters with varying content height --- .../client/gui/spellbook/SpellbookScreen.java | 1 - .../gui/spellbook/element/DynamicContent.java | 43 +++++++----- .../client/gui/spellbook/element/Panel.java | 66 +++++++++++++++++++ 3 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java index 490d835f..0dc0a71e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java @@ -160,7 +160,6 @@ public class SpellbookScreen extends HandledScreen imple tabs.getAllTabs().forEach(tab -> { Bounds bounds = tab.bounds(); - chapters.getCurrentChapter(); boolean hover = bounds.contains(mouseX, mouseY); int color = tab.chapter().color() & 0xFFFFFF; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java index 80812afb..c8a64c74 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java @@ -21,13 +21,17 @@ import net.minecraft.util.*; public class DynamicContent implements Content { private static final Text UNKNOWN = Text.of("???"); - private static final Text UNKNOWN_LEVEL = Text.literal("Level: ???").formatted(Formatting.DARK_GREEN); private SpellbookState.PageState state = new SpellbookState.PageState(); private final List pages; private Bounds bounds = Bounds.empty(); + private final Panel leftPanel = new Panel(this); + private final Panel rightPanel = new Panel(this); + + private int headerColor; + public DynamicContent(PacketByteBuf buffer) { pages = buffer.readList(Page::new); } @@ -35,32 +39,26 @@ public class DynamicContent implements Content { @Override public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) { int pageIndex = state.getOffset() * 2; - - getPage(pageIndex).ifPresent(page -> page.draw(context, mouseX, mouseY, container)); - - context.getMatrices().push(); - getPage(pageIndex + 1).ifPresent(page -> { - page.bounds.left = bounds.left + bounds.width / 2 + 20; - page.draw(context, mouseX, mouseY, container); - }); - context.getMatrices().pop(); - TextRenderer font = MinecraftClient.getInstance().textRenderer; - int headerColor = mouseY % 255; + headerColor = mouseY % 255; - Text pageText = Text.translatable("%s/%s", (pageIndex / 2) + 1, pages.size() / 2); + Text pageText = Text.translatable("%s/%s", (pageIndex / 2) + 1, (int)Math.ceil(pages.size() / 2F)); context.drawText(font, pageText, (int)(337 - font.getWidth(pageText) / 2F), 190, headerColor, false); } @Override public void copyStateFrom(Content old) { if (old instanceof DynamicContent o) { + if (state.getOffset() == o.state.getOffset()) { + leftPanel.verticalScrollbar.getScrubber().scrollTo(o.leftPanel.verticalScrollbar.getScrubber().getPosition(), false); + rightPanel.verticalScrollbar.getScrubber().scrollTo(o.rightPanel.verticalScrollbar.getScrubber().getPosition(), false); + } state = o.state; setBounds(o.bounds); } } - private Optional getPage(int index) { + Optional getPage(int index) { if (index < 0 || index >= pages.size()) { return Optional.empty(); } @@ -71,9 +69,15 @@ public class DynamicContent implements Content { this.bounds = bounds; pages.forEach(page -> { page.reset(); + int oldHeight = page.bounds.height; page.bounds.copy(bounds); + page.bounds.left = 0; + page.bounds.top = 0; page.bounds.width /= 2; + page.bounds.height = oldHeight; }); + + leftPanel.setBounds(bounds); } @Override @@ -83,6 +87,10 @@ public class DynamicContent implements Content { screen.addPageButtons(187, 30, 350, incr -> { state.swap(incr, (int)Math.ceil(pages.size() / 2F)); }); + + int pageIndex = state.getOffset() * 2; + leftPanel.init(screen, pageIndex); + rightPanel.init(screen, pageIndex + 1); } class Page implements Drawable { @@ -145,18 +153,17 @@ public class DynamicContent implements Content { element.compile(relativeY, container); relativeY += element.bounds().height; } + bounds.height = relativeY; } boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level; - int headerColor = mouseY % 255; - MatrixStack matrices = context.getMatrices(); DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, bounds.left, bounds.top - 10, 1.3F, headerColor); - DrawableUtil.drawScaledText(context, level < 0 ? UNKNOWN_LEVEL : Text.literal("Level: " + (level + 1)).formatted(Formatting.DARK_GREEN), bounds.left, bounds.top - 10 + 12, 0.8F, headerColor); + DrawableUtil.drawScaledText(context, Text.translatable("gui.unicopia.spellbook.page.level_requirement", level < 0 ? "???" : "" + (level + 1)).formatted(Formatting.DARK_GREEN), bounds.left, bounds.top - 10 + 12, 0.8F, headerColor); matrices.push(); - matrices.translate(bounds.left, bounds.top + 16, 0); + matrices.translate(0, 16, 0); elements.stream().filter(PageElement::isFloating).forEach(element -> { Bounds bounds = element.bounds(); matrices.push(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java new file mode 100644 index 00000000..fc84e687 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java @@ -0,0 +1,66 @@ +package com.minelittlepony.unicopia.client.gui.spellbook.element; + +import java.util.Optional; + +import com.minelittlepony.common.client.gui.IViewRoot; +import com.minelittlepony.common.client.gui.ScrollContainer; +import com.minelittlepony.common.client.gui.dimension.Bounds; +import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; +import com.minelittlepony.unicopia.client.gui.spellbook.element.DynamicContent.Page; + +import net.minecraft.client.gui.DrawContext; + +class Panel extends ScrollContainer { + private final DynamicContent content; + + Panel(DynamicContent content) { + this.content = content; + } + + private Optional page = Optional.empty(); + + public void init(SpellbookScreen screen, int pageIndex) { + verticalScrollbar.layoutToEnd = true; + getContentPadding().top = 15; + page = content.getPage(pageIndex); + + margin.left = screen.getX() + 30; + margin.top = screen.getY() + 15; + margin.right = screen.width - screen.getBackgroundWidth() - screen.getX() + 20; + margin.bottom = screen.height - screen.getBackgroundHeight() - screen.getY() + 40; + + if (pageIndex % 2 == 1) { + margin.left += screen.getBackgroundWidth() / 2 - 10; + } else { + margin.right += screen.getBackgroundWidth() / 2; + } + init(() -> {}); + screen.addDrawable(this); + ((IViewRoot)screen).getChildElements().add(this); + } + + @Override + protected void renderContents(DrawContext context, int mouseX, int mouseY, float partialTicks) { + page.ifPresent(p -> { + int oldHeight = p.getBounds().height; + p.draw(context, mouseX, mouseY, this); + if (p.getBounds().height != oldHeight) { + verticalScrollbar.reposition(); + } + }); + super.renderContents(context, mouseX, mouseY, partialTicks); + } + + @Override + public Bounds getContentBounds() { + return page == null ? Bounds.empty() : page.map(page -> { + return new Bounds(0, 0, 1, page.getBounds().height); + }).orElse(Bounds.empty()); + } + + @Override + protected void drawBackground(DrawContext context, int mouseX, int mouseY, float partialTicks) { } + + @Override + protected void drawDecorations(DrawContext context, int mouseX, int mouseY, float partialTicks) { } +} \ No newline at end of file From d08de6fae8482cb110862bf7a15b4e55b692206f Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 29 Jan 2024 21:20:04 +0000 Subject: [PATCH 84/98] Make chapter contents translatable --- .../container/SpellbookChapterLoader.java | 31 +- .../resources/assets/unicopia/lang/en_us.json | 353 +++++++++++++++++- .../core/rendertype_portal_surface.fsh | 3 - .../core/rendertype_portal_surface.json | 7 +- .../spellbook/chapters/air_magic.json | 147 ++++---- .../spellbook/chapters/crystal_heart.json | 126 +++---- .../spellbook/chapters/dark_magic.json | 275 ++++++-------- .../spellbook/chapters/fire_magic.json | 194 ++++------ .../spellbook/chapters/ice_magic.json | 191 +++++----- .../spellbook/chapters/introduction.json | 96 ++--- .../spellbook/chapters/the_otherworldly.json | 55 ++- 11 files changed, 846 insertions(+), 632 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java index b7911f05..15e0dd3a 100644 --- a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java +++ b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java @@ -83,6 +83,14 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab } } + private static Text readText(JsonElement json) { + return json.isJsonPrimitive() ? Text.translatable(json.getAsString()) : Text.Serializer.fromJson(json); + } + + public enum Flow { + NONE, LEFT, RIGHT + } + public record Chapter ( Identifier id, TabSide side, @@ -103,7 +111,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab .filter(pages -> pages.size() > 0) .stream() .flatMap(pages -> StreamSupport.stream(pages.spliterator(), false)) - .map(Page::new) + .map(Page::of) .toList(); } @@ -122,13 +130,19 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab int level, List elements ) { - public Page(JsonElement json) { + private static final Page EMPTY = new Page(Text.empty(), 0, List.of()); + + public static Page of(JsonElement json) { + return json.isJsonObject() && json.getAsJsonObject().keySet().isEmpty() ? EMPTY : new Page(json); + } + + Page(JsonElement json) { this(json.getAsJsonObject()); } - public Page(JsonObject json) { + Page(JsonObject json) { this( - Text.Serializer.fromJson(json.get("title")), + readText(json.get("title")), JsonHelper.getInt(json, "level", 0), new ArrayList<>() ); @@ -148,10 +162,6 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab } } - public enum Flow { - NONE, LEFT, RIGHT - } - private interface Element { void toBuffer(PacketByteBuf buffer); @@ -213,7 +223,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab return new Multi(count, new Id((byte)4, Identifier.tryParse(json.get("spell").getAsString()))); } - return new Multi(count, new TextBlock(Text.Serializer.fromJson(json.get("text")))); + return new Multi(count, new TextBlock(readText(json.get("text")))); } @Override @@ -229,7 +239,6 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab static Element read(JsonElement json) { if (!json.isJsonPrimitive()) { - JsonObject el = JsonHelper.asObject(json, "element"); if (el.has("texture")) { return new Image( @@ -256,7 +265,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab } } - return new TextBlock(Text.Serializer.fromJson(json)); + return new TextBlock(readText(json)); } private static Bounds boundsFromJson(JsonObject el) { diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index fe83a05b..348b9423 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -515,7 +515,7 @@ "spell.unicopia.siphoning.lore": "Channels other creature's life force into the caster", "spell.unicopia.reveal": "Dispell Illusion", "spell.unicopia.reveal.lore": "Negates shapeshifting magic", - "spell.unicopia.light": "Dancing Light", + "spell.unicopia.light": "Dancing Lights", "spell.unicopia.light.lore": "Summons multiple small lights to follow the caster", "spell.unicopia.awkward": "Botched", "spell.unicopia.awkward.lore": "Unstable magics", @@ -793,10 +793,361 @@ "gui.unicopia.spellbook.page.recipes": "Recipes", "gui.unicopia.spellbook.page.recipes.empty": "0 Recipes Unlocked", "gui.unicopia.spellbook.page.mana": "Mana", + "gui.unicopia.spellbook.page.level_requirement": "Level: %s", "gui.unicopia.spellbook.page.requirements.entry.item": "- %1$sx %2$s", "gui.unicopia.spellbook.page.requirements.entry.trait": "- At least %1$sx %2$s trait", "gui.unicopia.spellbook.page.requirements.entry.spell": "- %1$sx %2$s gem", + "gui.unicopia.spellbook.recipe.requires": "Requires:", + "gui.unicopia.spellbook.author1.sign_off": "At the princess' behest", + "gui.unicopia.spellbook.author1.sign_off.b": "At the princess' behest, so dreadfully sorry", + "gui.unicopia.spellbook.author1.name": "- Starswirl the Bearded", + "gui.unicopia.spellbook.author2.name": "- Lord Sombra", + "gui.unicopia.spellbook.author3.name": "- XOXOX Lulu", + "gui.unicopia.spellbook.chapter.artefacts.status.unconfirmed": "Status: Unconfirmed", + "gui.unicopia.spellbook.chapter.artefacts.status.confirmed": "Status: Confirmed", + "gui.unicopia.spellbook.chapter.artefacts.status.lost": "Status: Lost", + "gui.unicopia.spellbook.chapter.introduction.p1.title": "Preface", + "gui.unicopia.spellbook.chapter.introduction.p1.body": "To whomever holds this tome, beware what you seek for you might not like what you find. §kHither yonder equs§r.", + "gui.unicopia.spellbook.chapter.introduction.p2.title": "Ch.1 Magic in Equestria", + "gui.unicopia.spellbook.chapter.introduction.p2.body": "Equestria is filled with magic of all different shapes and forms. Following recent events, however, it's has become plainly obvious that we do not fully understand all that there is about the world of Equestria. That is why the crown has tasked me with researching Magic in all of its forms, so we might utilise it and, I hope, save ourselves from the §kdiscordic assault§r.", + "gui.unicopia.spellbook.chapter.introduction.p3.title": "1st Mare '12", + "gui.unicopia.spellbook.chapter.introduction.p3.1.body": "Unusual Rocks", + "gui.unicopia.spellbook.chapter.introduction.p3.2.body": "These 'Gemstones' as the locals call them are a common material found around the world. Farm-Ponies dig them up all the time and consider it a local delicacy, but I believe these stones are capable of a lot more than they let on.", + "gui.unicopia.spellbook.chapter.introduction.p4.title": "Gemstones", + "gui.unicopia.spellbook.chapter.introduction.p4.1.body": "My research is still incomplete but I may have stumbled upon something. These stones have high magical potentia! More than I've ever seen before!", + "gui.unicopia.spellbook.chapter.introduction.p4.2.body": "§mLuna wants-§r I'm going to keep experimenting. Hooves-crossed, I'll update you tomorrow if I find anything.", + "gui.unicopia.spellbook.chapter.introduction.p5.title": "2nd Mare '12", + "gui.unicopia.spellbook.chapter.introduction.p5.1.body": "It worked! Holy §kCelestia's ass-cheeks'§r it actually worked!", + "gui.unicopia.spellbook.chapter.introduction.p5.2.body": "This is amazing! These can do so much more than I could have ever imagined. Think of the advances I could bring to Equestria. Gem-powered lighting, heating, cooling, I'd no longer have to spend summer sitting on this-", + "gui.unicopia.spellbook.chapter.introduction.p5.3.body": "I'm getting ahead of myself. Let me explain...", + "gui.unicopia.spellbook.chapter.introduction.p6.title": "Spellcrafting", + "gui.unicopia.spellbook.chapter.introduction.p6.1.body": "I drew a guide at the start of this book to help with the placement.", + "gui.unicopia.spellbook.chapter.introduction.p6.2.body": "Put a raw gem-it mustn't have any spells already-in the middle and place materials around it in the slots I marked.", + "gui.unicopia.spellbook.chapter.introduction.p6.3.body": "Each material gives different effects and putting them closer enhances their influence on the gem.", + "gui.unicopia.spellbook.chapter.introduction.p7.title": "3rd Mare '12", + "gui.unicopia.spellbook.chapter.introduction.p7.1.body": "I'm going to start documenting spell combinations as I find them. Some of them are pretty obvious, like gem + fire = fire gem", + "gui.unicopia.spellbook.chapter.introduction.p7.2.body": "But some are less clear. For instance, what traits would an egg add? Much experimenting is needed. Oh, I'm giddy with excitement!", + "gui.unicopia.spellbook.chapter.introduction.p8.title": "Botched Gems", + "gui.unicopia.spellbook.chapter.introduction.p8.1.body": "Not every combination works. What's dissapointing is now I have all these useless stones piling up in my chambers.", + "gui.unicopia.spellbook.chapter.introduction.p8.2.body": "I don't know what to do with them. They're not edible. At least the locals don't think so.", + "gui.unicopia.spellbook.chapter.introduction.p8.3.body": "They do still have the traits I gave them, so maybe I can find a use other than building a rock-fort with little Luna...", + "gui.unicopia.spellbook.chapter.introduction.p9.title": "13th Mare '12", + "gui.unicopia.spellbook.chapter.introduction.p9.1.body": "Sorry for the long delay in updates. I've been hard at work researching different spells and desciding my approach.", + "gui.unicopia.spellbook.chapter.introduction.p9.2.body": "Fire is becomg a very interesting aspect, what with traits for it being readily available.", + + "gui.unicopia.spellbook.chapter.fire.p1.title": "Ch.2 Fire Magic", + "gui.unicopia.spellbook.chapter.fire.p2.title": "9th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p2.1.body": "It took me longer than I anticipated, nearly a month! Hah! But I present to you, dear reader, my findings for the first elementary form of magic: FIRE.", + "gui.unicopia.spellbook.chapter.fire.p2.2.body": "It's a working title, okay?", + "gui.unicopia.spellbook.chapter.fire.scorch.1.body": "Simple and to the point, Scorch does exactly what you'd think. By embuing a gem with the fire trait, one can indute it to glow and become hot to the touch.", + "gui.unicopia.spellbook.chapter.fire.scorch.2.body": "The effect becomes stronger the more fire you load it with, but take care not to overload it, as it may become volatile.", + "gui.unicopia.spellbook.chapter.fire.flame.1.body": "Creates a heating affect up to a radius of 3 hooves from any surfaces it touches.", + "gui.unicopia.spellbook.chapter.fire.flame.2.body": "Useful when one needs a flame in a hurry or to fend off a wild wendigo.", + "gui.unicopia.spellbook.chapter.fire.p5.title": "10th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p5.1.body": "Progress?", + "gui.unicopia.spellbook.chapter.fire.p5.2.body": "I've managed to improve the previous spell somewhat, but there is still something lacking. It's all very orderly. Predicatable.", + "gui.unicopia.spellbook.chapter.fire.p5.3.body": "Luna has suggested adding more fire, but I'm weary to create more scorch marks on the tower. Faust save me if anypony were to find out what I've been doing...", + "gui.unicopia.spellbook.chapter.fire.p6.title": "Fire Magic III", + "gui.unicopia.spellbook.chapter.fire.p6.1.body": "Focusing Magic", + "gui.unicopia.spellbook.chapter.fire.p6.2.body": "Some spells normally take a great amount of focus to cast, and a steady wit to control, However I've found objects embued with the focusing trait work wonderfully as a substitute for when the caster is lacking.", + "gui.unicopia.spellbook.chapter.fire.p7.title": "Fire Magic III-II", + "gui.unicopia.spellbook.chapter.fire.p7.1.body": "Any glass objects you can find, eyes, bottles, whatever incorporates a lense can be used to embue focus on a spell.", + "gui.unicopia.spellbook.chapter.fire.p8.title": "Scrap 2", + "gui.unicopia.spellbook.chapter.fire.p8.1.body": "We went to the market today. Had to get out of that tower, do something, be somewhere. Luna suggested we go in to see what they were selling for the fair so I decided to indulge her.", + "gui.unicopia.spellbook.chapter.fire.p8.2.body": "The townsfolk are still rather skeptical of us, though who's to blame them. Luna was getting along well with the other foals, at least.", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.1.body": "Creates a series of heated projectiles to fling at a target. Upon impact the target will be set alight.", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.2.body": "- Increase focus will allow finer grained control of the projectile's trajectory.", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.3.body": "- With over 50 focus it's almost like they know where the target is (homing?).", + "gui.unicopia.spellbook.chapter.fire.p10.title": "Fire Magic IV", + "gui.unicopia.spellbook.chapter.fire.p10.1.body": "Powerful Magic", + "gui.unicopia.spellbook.chapter.fire.p10.2.body": "Where some spells take focus, others require power. Either to exert a force, or to generate energy in some form.", + "gui.unicopia.spellbook.chapter.fire.p10.3.body": "There are few unicorns that have the inherent strength and power to cast spells of these kinds, but luckily such a trait is not in short supply around us.", + "gui.unicopia.spellbook.chapter.fire.p11.title": "Fire Magic IV-II", + "gui.unicopia.spellbook.chapter.fire.p11.1.body": "Earthly elements, stone, many metals and minerals, that are strong under compression will exhibit the strength trait.", + "gui.unicopia.spellbook.chapter.fire.p11.2.body": "Electrical/Conductive elements that can be used to power things, or that glow can also be used to obtain the power trait.", + "gui.unicopia.spellbook.chapter.fire.p12.title": "11th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p12.1.body": "Got a knock at the door today. Luna was very eager to answer it but I had to send her away as it was a messenger from the crown.", + "gui.unicopia.spellbook.chapter.fire.p12.2.body": "My research has garnered a certain bit of notoriety, it appears. The royals are very eager to see what I have concocted.", + "gui.unicopia.spellbook.chapter.fire.p12.3.body": "They've gotten it in their heads that they can use it against the West.", + "gui.unicopia.spellbook.chapter.fire.p12.4.body": "Gods forbid they succeed. I shudder to think what the council might do if they got their hooves on my work.", + "gui.unicopia.spellbook.chapter.fire.p12.5.body": "Addendum", + "gui.unicopia.spellbook.chapter.fire.p12.6.body": "§mI am told the crown has started giving directions to find other uses. Ways to...", + "gui.unicopia.spellbook.chapter.fire.p13.title": "20th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p13.1.body": "I have word from the crown. They appear satisfied, for now, and have agreed to let my continue my research as I have into the winter.", + "gui.unicopia.spellbook.chapter.fire.p13.2.body": "I am under duress to destroy the last several entries, I'm afraid.", + "gui.unicopia.spellbook.chapter.fire.p14.title": "21st Jum '12", + "gui.unicopia.spellbook.chapter.fire.p14.1.body": "I shall visit §kCommander Hurricane§r tomorrow. Perhaps she may shed light onto my predicament.", + "gui.unicopia.spellbook.chapter.fire.shield.1.body": "Casting shields are one of the first things every unicorn learns in self-defense. It's simple and easy to cast, and is an excellent introduction to incanting.", + "gui.unicopia.spellbook.chapter.fire.shield.2.body": "Its disadvantage is the energy and mental cost, however we can negate both by attaching it to a gem as per follows...", + "gui.unicopia.spellbook.chapter.fire.shield.modifier.1": "+ add power trait to increase effect range", + "gui.unicopia.spellbook.chapter.fire.p16.title": "Protection II", + "gui.unicopia.spellbook.chapter.fire.p16.1.body": "By adding extra traits, I was able to slightly modify the shield to allow or deny certain parties into the effect range.", + "gui.unicopia.spellbook.chapter.fire.p16.2.body": "+ add life trait --> all animals may enter\n+ add blood trait --> all monsters may enter\n+ add ice trait --> all ponies may enter", + "gui.unicopia.spellbook.chapter.fire.p17.title": "Protection III", + "gui.unicopia.spellbook.chapter.fire.p17.1.body": "+ add genorosity trait to attach the spell to a location rather than yourself", + "gui.unicopia.spellbook.chapter.fire.p18.title": "Scrap: 9th Jum '12", + "gui.unicopia.spellbook.chapter.fire.p18.1.body": "Fire magic has proven to a be a little more... unpredictable than anticipated. Every time I feel like I'm making progress it finds a way to set me back.", + "gui.unicopia.spellbook.chapter.fire.p18.2.body": "I can't stop now, though...I'm told the situation in the west is growing dire. They have asked me to pick up the pace and produce something we can use to get the upper hoof against the §kChangeling Storm§r.", + + "gui.unicopia.spellbook.chapter.ice.p1.title": "Ch.3 Ice Magic", + "gui.unicopia.spellbook.chapter.ice.p2.title": "4th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p2.1.body": "This is an interesting one. Rather simple, I admit, but Luna insisted I make something cold to help us deal with this darn heat.", + "gui.unicopia.spellbook.chapter.ice.p2.2.body": "All you need is a gem and something cold. Like a snowball.", + "gui.unicopia.spellbook.chapter.ice.frost.1.body": "Creates a chilling affect up to a radius of 3 hooves from any surfaces it touches.", + "gui.unicopia.spellbook.chapter.ice.frost.2.body": "Will sap energy out of the immediate environment causing a phase change.", + "gui.unicopia.spellbook.chapter.ice.p4.title": "Chilling Breath", + "gui.unicopia.spellbook.chapter.ice.p4.1.body": "Alters the ability of certain objects to distenguish between hot and cold.", + "gui.unicopia.spellbook.chapter.ice.p4.2.body": "This is a very weak spell, but when used with a boat can be exceedingly useful to get out of a sticky stituation.", + "gui.unicopia.spellbook.chapter.ice.p5.title": "5th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p5.1.body": "The village-ponies had a bonfire last night. I could tell by the strong smell of burning wood and the sound of music.", + "gui.unicopia.spellbook.chapter.ice.p5.2.body": "Luna, bless her heart, insisted that we take a break to join them. She had to practically drag me away from my desk to do it.", + "gui.unicopia.spellbook.chapter.ice.p5.3.body": "What can I say? She's a light in my heart.", + "gui.unicopia.spellbook.chapter.ice.p6.title": "Bonfire", + "gui.unicopia.spellbook.chapter.ice.p6.1.body": "We arrived at the bonfire, and of course the first thing they had was a mug of ale in my hoof. I didn't drink it, of course-alcohol is a poison to me. I'd be sick as a mule.", + "gui.unicopia.spellbook.chapter.ice.p6.2.body": "Luna enjoyed it-the bonfire, not the ale!-though. She made immediate friends with one of the town's fillies, Celly or something. They played the whole night.", + "gui.unicopia.spellbook.chapter.ice.p7.title": "Bonfire II", + "gui.unicopia.spellbook.chapter.ice.p7.1.body": "On the way back Luna was telling me of the stories her friend told her. The town has a lot of legends, as to be expected.", + "gui.unicopia.spellbook.chapter.ice.p7.2.body": "One of them was about a scary old warlock who lived in a haunted tower at the edge of town. There's no mystery who that was about.", + "gui.unicopia.spellbook.chapter.ice.p7.3.body": "Ice Spell II", + "gui.unicopia.spellbook.chapter.ice.p7.4.body": "Creates a cooling affect up to a radius of 3 hooves from any surfaces it touches.", + "gui.unicopia.spellbook.chapter.ice.p8.title": "6th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p8.1.body": "There was a strange noise in the village last night. Very strange. I heard a lot ponies shouting and there may have been a fire.", + "gui.unicopia.spellbook.chapter.ice.p8.2.body": "I hope everything is okay.", + "gui.unicopia.spellbook.chapter.ice.light.1.body": "By combining a fire bolt gem with a splash of life and the chilling effect of ice I've created a spell to help with seeing in the night.", + "gui.unicopia.spellbook.chapter.ice.light.2.body": "Dancing Lights will summon a cluster of glowing orbs to illuminate your path.", + "gui.unicopia.spellbook.chapter.ice.light.modifier.1": "* By adding more focus you can extend the duration of the spell", + "gui.unicopia.spellbook.chapter.ice.p10.title": "12th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p10.1.body": "There were more noises last night, this time a lot closer. The town's dimeaner has also changed. A lot of the ponies I see that are normally very cheerful have become sullen.", + "gui.unicopia.spellbook.chapter.ice.p10.2.body": "Something has happened, that much is obvious, though few will tell me what.", + "gui.unicopia.spellbook.chapter.ice.p11.title": "15th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p11.1.body": "Winter is nearly upon us now. I just saw the earliest flakes of snow outside this window as I write.", + "gui.unicopia.spellbook.chapter.ice.p11.2.body": "The locals have begun their winter unwrapping and though the usual grumblings about frozen fields abound, I can tell there is still §ka sense of uneasyness§r.", + "gui.unicopia.spellbook.chapter.ice.p12.title": "17th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p12.1.body": "It's rather surprising how quickly the weather starts to change around here. Everything in Catermoore is so very well controlled, with the spells we use to manage temperature and the pegasi assisting with the weather, we sometimes forget what wild seasons can be like.", + "gui.unicopia.spellbook.chapter.ice.p12.2.body": "But these Earth Ponies don't ave any of those luxuries. They have to deal with the weather as it comes.", + "gui.unicopia.spellbook.chapter.ice.p13.title": "Frozen Lake", + "gui.unicopia.spellbook.chapter.ice.p13.1.body": "Just this morning I was on the way to the stall when I passed the lake at the foot of this tower's hill and saw it was nearly completely frozen over. A few colts had taken to scating across it.", + "gui.unicopia.spellbook.chapter.ice.p13.2.body": "Luna asked if she could join them, but I didn't allow it. I couldn't say why at the time, but I had a bad feeling, like something was going to happen...", + "gui.unicopia.spellbook.chapter.ice.p14.title": "17th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p14.1.body": "Oh gods, of princesses. I. I don't know how to describe this. My hooves are shaking, I can barely breeze. I'm freezing cold and I can't stop thinking about what happened.", + "gui.unicopia.spellbook.chapter.ice.p14.2.body": "I knew there was something wrong. I b---knew it. I didn't know what it was at the time, but I'm extremely glad I didn't let Luna scate on that lake.", + "gui.unicopia.spellbook.chapter.ice.p15.title": "Frozen Lake II", + "gui.unicopia.spellbook.chapter.ice.p15.1.body": "Let me take a step back a bit to describe what happened. Do you remember the frozen lake and those colts that were skating on it?", + "gui.unicopia.spellbook.chapter.ice.p15.2.body": "Well, after that I went on to the market and I was looking at the strawberries in one of the stalls--Didn't get any, sadly they were forgotten in the confusion.", + "gui.unicopia.spellbook.chapter.ice.p16.title": "Frozen Lake III", + "gui.unicopia.spellbook.chapter.ice.p16.1.body": "Whilst I was debating the vendor about the price, I felt Luna tugging on my tunic and she asked me what was going on. I heard galloping and I saw ponies running by us. None of them stopped to explain, but I heard some very gruff words in old ponish.", + "gui.unicopia.spellbook.chapter.ice.p16.2.body": "They were heading in the direction of our tower so I set off after them.", + "gui.unicopia.spellbook.chapter.ice.p16.3.body": "My immediate thoughts were 'was it a fire'? Was my lab in danger?", + "gui.unicopia.spellbook.chapter.ice.p16.4.body": "I tell you what, I wish that were the case. What I actually found was much, much, worse, and even thinking of it makes my blood run cold anew.", + "gui.unicopia.spellbook.chapter.ice.p17.title": "Frozen Lake IV", + "gui.unicopia.spellbook.chapter.ice.p17.1.body": "When we were getting near the lake from earlier, I saw a large crowd growing along its banks. Ladders and emergency equipment were out and scattered on the shoreline and a loud uproar had erupted about what to do.", + "gui.unicopia.spellbook.chapter.ice.p17.2.body": "Getting closer, though, I realised what had happened soon enough--and I made a beeline for for the water-- The ice was broken and the lake was freezing cold.", + "gui.unicopia.spellbook.chapter.ice.p17.3.body": "I jumped in anyway, pulling my saddlebags open with my magic, and grabbed the last of the gems i had with my and cast the unfinished spell it had inside.", + "gui.unicopia.spellbook.chapter.ice.p17.4.body": "The waters receded away from me as I galloped down the slop and across the drying lake bed and dove to catch the colts that had fallen in.", + "gui.unicopia.spellbook.chapter.ice.p18.title": "Frozen Lake V", + "gui.unicopia.spellbook.chapter.ice.p18.1.body": "They were freezing and wet, even as the spell's effects worked to pull the water away from their coats, we carred them up to the shore and got them covered in blankets with hot drinks in their hooves.", + "gui.unicopia.spellbook.chapter.ice.p18.2.body": "The townponies insisted on giving me a blanket of my own, even though I hadn't - couldn't have- gotten wet.", + "gui.unicopia.spellbook.chapter.ice.p18.3.body": "It was only much later, when the shock began to wear of and I was feeling my head start to pound that I remembered to cancel the spell.", + "gui.unicopia.spellbook.chapter.ice.p18.4.body": "Thank the princesses we got there in time.", + "gui.unicopia.spellbook.chapter.ice.p19.title": "18th Trot '12", + "gui.unicopia.spellbook.chapter.ice.p19.1.body": "No sign of the colts this morning, I assume they won't be coming near this lake for a long while. The water had frozen again in the night and looked peaceful.", + "gui.unicopia.spellbook.chapter.ice.p19.2.body": "The unseasy feeling I had yesterday was gone today so I was able to relax on its banks with Luna. She didn't want to swim in this lake any more, and I don't blame her. I wouldn't either.", + "gui.unicopia.spellbook.chapter.ice.p20.title": "Sandcastle", + "gui.unicopia.spellbook.chapter.ice.p20.1.body": "Luna started a sand castle, and whilst she was busy with that I decided to sketch out the details of my new spell.", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.1.body": "By combining the abilities of a shield spell with that of the frost gem, the results are... Admittedly strange.", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.2.body": "I'm calling this water repulsion because it does just that: It pushes water away from the caster.", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.modifier.1": "* By adding more focus you can extend the duration of the spell", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.modifier.2": "* Add the generosity trait to tie this spell to a location rather than a user", + + "gui.unicopia.spellbook.chapter.air.p1.title": "Ch.4 Air Magic", + "gui.unicopia.spellbook.chapter.air.p2.title": "1st Hoof '12", + "gui.unicopia.spellbook.chapter.air.p1.1.body": "A new month, a new chapter. Little Luna was getting bored of sitting in the tower all day (and who's to blame her? We've been on this assignment for over two months at this point).", + "gui.unicopia.spellbook.chapter.air.p1.2.body": "So as a little treat, we've decided to go on a little trip to the Grand Marepid Falls to visit my friend, Commander Hurricane.", + "gui.unicopia.spellbook.chapter.air.p1.3.body": "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.", + "gui.unicopia.spellbook.chapter.air.p3.title": "2nd Hoof '12", + "gui.unicopia.spellbook.chapter.air.p3.1.body": "Apologies for the, um, unusual entry in the appendices for today. It appears some little gremlin managed to obscond with my journal.", + "gui.unicopia.spellbook.chapter.air.p4.title": "Air Magic I", + "gui.unicopia.spellbook.chapter.air.p4.1.body": "Pegasus Magic", + "gui.unicopia.spellbook.chapter.air.p4.2.body": "Air magic is to pegasi like fire is to unicorns. They're both equally hard to control but where fire is primarily focused around force, destruction, or protection, air is all about flexibility and free motion.", + "gui.unicopia.spellbook.chapter.air.p4.3.body": "Command Hurricane has very kindly given me some tips on how to identify this trait in everyday objects.", + "gui.unicopia.spellbook.chapter.air.p4.4.body": "Anything relating to flight, or that originated from creatures that fly, or that comes from up above can be considered a source of the air trait.", + "gui.unicopia.spellbook.chapter.air.p4.5.body": "Eg. Feathers.", + "gui.unicopia.spellbook.chapter.air.catapult.1.body": "This is a straightforward application of a unicorn's telekineses. The catapult gem allows a caster to grab any block or creature and fling them into the air.", + "gui.unicopia.spellbook.chapter.air.catapult.2.body": "Use it again on something already thrown to push it away from you.", + "gui.unicopia.spellbook.chapter.air.catapult.modifier.1": "* One can add apply more force by adding the strength trait", + "gui.unicopia.spellbook.chapter.air.bubble.1.body": "A defensive and utility spell. Bubble will trap the target in a giant soap bubble, rendering them defensless.", + "gui.unicopia.spellbook.chapter.air.bubble.2.body": "Use it again will pop the bubble.", + "gui.unicopia.spellbook.chapter.air.p7.title": "8th Hoof '12", + "gui.unicopia.spellbook.chapter.air.p7.1.body": "I thought I would take a short moment to write down an entry to record my findings whilst Luna and The Commander are out.", + "gui.unicopia.spellbook.chapter.air.p7.2.body": "Pegasi magic really is a fascinating beast. It's not like our magic, which is more of a study, with rigid rules and practices.", + "gui.unicopia.spellbook.chapter.air.p8.title": "Pegasi", + "gui.unicopia.spellbook.chapter.air.p8.1.body": "Pegasus magic is more about feeling. It's an art form. They don't think about what they want to do, it just happens, but it all still follows the same principle.", + "gui.unicopia.spellbook.chapter.air.p9.title": "Pegasi II", + "gui.unicopia.spellbook.chapter.air.p9.1.body": "Take for example their cloud homes. There are no spells I can read that would let me do this, but if you feel, not with your hooves or your horn, but properly, with your mind, your heart, you will find magic buzzing all throughout.", + "gui.unicopia.spellbook.chapter.air.p9.2.body": "Clouds are teeming with the air and water trait, but also others, like power, strength, life, earth. All traits of the material the clouds are mimicking.", + "gui.unicopia.spellbook.chapter.air.p10.title": "Pegasi III", + "gui.unicopia.spellbook.chapter.air.p10.1.body": "I wish I could study this further, but I'm afraid to interfere in this magic I don't yet fully understand.", + "gui.unicopia.spellbook.chapter.air.p10.2.body": "Commander Hurricane may never forgive me if I destroy her home the first time I'm here.", + "gui.unicopia.spellbook.chapter.air.feather_fall.1.body": "Expanding on the defensive capabilities of the protection gem, I've attempted to extend its advantages to party members.", + "gui.unicopia.spellbook.chapter.air.feather_fall.2.body": "This one is unusual because of its complexity, but in theory it should allow one to slow their own and friends' descent.", + "gui.unicopia.spellbook.chapter.air.p12.title": "10th Hoof '12", + "gui.unicopia.spellbook.chapter.air.p12.1.body": "Went to dinner with Commander Huricane and Luna. We got to talking about architecture and Hurricane mentioned the Taz Marehall.", + "gui.unicopia.spellbook.chapter.air.p12.2.body": "Luna thought it was a rather funny name of a castle. I had to remind her that not all cultures are the same.", + "gui.unicopia.spellbook.chapter.air.p13.title": "21st Hoof '12", + "gui.unicopia.spellbook.chapter.air.p13.1.body": "I'm writing this on the eve of my return to §kTrotholm§r. Though my time in Cloudopolis has been elightening, I look forward to a return to the familiar surroundings and a proper rest in my own solid bed.", + "gui.unicopia.spellbook.chapter.air.p13.2.body": "I cannot say the same for Luna, though. She is currently sitting on my bed beside me pouting over every little thing she sees me put into my saddlebag.", + "gui.unicopia.spellbook.chapter.air.p14.title": "Returning Home", + "gui.unicopia.spellbook.chapter.air.p14.1.body": "She keeps insisting that we stay a little longer §mto hang out with that pegasus colt I saw her with the other day, no doubt", + "gui.unicopia.spellbook.chapter.air.p15.title": "22nd Hoof '12", + "gui.unicopia.spellbook.chapter.air.p15.1.body": "As we're flying above the mountain tops, I can't help but feel in awe the beauty that §kMother Faust§r has given us.", + "gui.unicopia.spellbook.chapter.air.p15.2.body": "I had to spend the whole time keeping Luna from falling out, and of course answering questions about all the different kinds of clouds. Thankfully, the balloon operator was there to help with the subtleties.", + "gui.unicopia.spellbook.chapter.air.p15.3.body": "Being out here, above all of our troubles, really makes me forget the rest of the world for a moment. It takes me back to a simpler time, before the--", + "gui.unicopia.spellbook.chapter.air.p15.4.body": "Of course the flashes of explosions to the west has to ruin it all. There appears to be a storm on the horizon. I certainly hope it not to be a bad omen...", + + "gui.unicopia.spellbook.chapter.dark_magic.p1.title": "Ch.5 The Arcane", + "gui.unicopia.spellbook.chapter.dark_magic.p2.title": "30th Hoof '12", + "gui.unicopia.spellbook.chapter.dark_magic.p2.1.body": "A new communication from the crown today. The situation seems to be worsening in the west and though they've been satisfied with my work until now, they are putting pressure on me to produce something more destructive.", + "gui.unicopia.spellbook.chapter.dark_magic.p2.2.body": "I tried to tell the messenger that I couldn't-", + "gui.unicopia.spellbook.chapter.dark_magic.p3.title": "3rd Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p3.1.body": "Let it be known that it was never my intention that anypony use my work for nefarious purposes. I am a researcher, above and beyond all else. My intentions are pure, and my wants are nothing more than this world to be at peace.", + "gui.unicopia.spellbook.chapter.dark_magic.p4.title": "4th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p4.1.body": "No review of magic is ever complete without a glimpse into the other side.", + "gui.unicopia.spellbook.chapter.dark_magic.p4.2.body": "Dark magic, or as I'm going to refer to it as The Arcane are tip-toeing the line between the normal light magic we normally know and the more nefarious side of reality.", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.1.body": "If someone were to ask you 'what is the opposite of a repulsion spell, what would you say? An attraction spell, of course!", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.2.body": "By twisting the purpose of the protection spell, I've been able to reverse its function to create a spell that pulls objects and entities closer to the caster.", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.modifier.1": "+ 10x knowledge to narrow the effect's range to items", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.modifier.2": "+ add focus trait to increase duration\n+ add power trait to increase range", + "gui.unicopia.spellbook.chapter.dark_magic.p6.title": "8th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p6.1.body": "Additional Notes for the Attraction Spell", + "gui.unicopia.spellbook.chapter.dark_magic.p6.2.body": "I caught Luna playing with my spellcrafting grid today, even though I expressly forbid her from entering my study when I'm not there.", + "gui.unicopia.spellbook.chapter.dark_magic.p6.3.body": "Apparently it was over some dispute with Celly, I don't really remember, but it culminated in Luna sneaking into the study whilst I was out to get some bread.", + "gui.unicopia.spellbook.chapter.dark_magic.p7.title": "Arcane Attraction II", + "gui.unicopia.spellbook.chapter.dark_magic.p7.1.body": "This isn't really about that, though. She's been scolded and sent back to her room, however as I was cleaning up the mess she'd made I noticed something in the piles of gems.", + "gui.unicopia.spellbook.chapter.dark_magic.p7.2.body": "It's hard to describe, really. This is still distincly an attraction gem, but it's different.", + "gui.unicopia.spellbook.chapter.dark_magic.p7.3.body": "It has traits I hadn't considered before, and the way it behaves... ", + "gui.unicopia.spellbook.chapter.dark_magic.p8.title": "Arcane Attraction II Cont.", + "gui.unicopia.spellbook.chapter.dark_magic.p8.1.body": "Well I'll leave that up to tomorrow. I'm still tired from everything that's happened this week.", + "gui.unicopia.spellbook.chapter.dark_magic.p8.2.body": ">0 generosity --> ??", + "gui.unicopia.spellbook.chapter.dark_magic.p8.3.body": ">20 order trait --> ???", + "gui.unicopia.spellbook.chapter.dark_magic.p9.title": "20th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p9.1.body": "As per their agreement, the council have sent certain...supplimental materials to aid in the new direction my research is taking. I was a little shocked at first.", + "gui.unicopia.spellbook.chapter.dark_magic.p9.2.body": "This... thing... Whatever it is. Was, rather.", + "gui.unicopia.spellbook.chapter.dark_magic.p9.3.body": "Is this really what we're fighting in the west?", + "gui.unicopia.spellbook.chapter.dark_magic.p10.title": "21st Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p10.1.body": "I've put the... thing. In the basement. Locked the door.", + "gui.unicopia.spellbook.chapter.dark_magic.p10.2.body": "I just couldn't stand looking at it any longer. It's vaguely pony-shaped, but also...", + "gui.unicopia.spellbook.chapter.dark_magic.p10.3.body": "I couldn't well let Luna see it. I've sent her out to spend the next few nights with Celly whilst I sort out what to do with this.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.title": "25th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p11.1.body": "I'm sorry for the long delays. Things have been... busy.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.2.body": "I've learned a lot about these creatures. Attached are some illustrations, done best I could so I wouldn't have to look at the thing directly.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.3.body": "Its body is black and vaguely §kinsect-like§r with ponish proportions. It has no fur.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.4.body": "The magic they use is unusual. Not unusual, like what I've been studying. It's unnatural. Wild.", + "gui.unicopia.spellbook.chapter.dark_magic.p11.5.body": "There is definitely something I might be able to harness here, but I shudder... Should I?", + "gui.unicopia.spellbook.chapter.dark_magic.p11.6.body": "I fear that this may be a line that shouldn't be crossed.", + "gui.unicopia.spellbook.chapter.dark_magic.transformation.1.body": "I've begun by simply harnessing their ability. It's unfocused and hard to control. I can rarely predict what is going to happen, but this gem has very clear transmodrification properties.", + "gui.unicopia.spellbook.chapter.dark_magic.transformation.2.body": "Throwing this at any creature has the chance to transform it into any other creature.", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.1.body": "Dispell Illusion is the first line of defense against transformation/illusion spells.", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.2.body": "When cast it will force any nearby disguised changelings in its range to reveal their true form.", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.modifier.1": "* Increase range by adding the power trait", + "gui.unicopia.spellbook.chapter.dark_magic.p14.title": "27th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p14.1.body": "Sleep the last few nights has become... elusive.", + "gui.unicopia.spellbook.chapter.dark_magic.p14.2.body": "I don't know how to describe it, really. Luna appears unaffected, but every night after the sun goes down I find myself lying awake at night unable to sleep. It doesn't help that that sounds in the village have resumed.", + "gui.unicopia.spellbook.chapter.dark_magic.p15.title": "Lost Sleep", + "gui.unicopia.spellbook.chapter.dark_magic.p15.1.body": "Last night, especially, I found myself pacing in the observatory. The air became chill, more than is normal for this time of year, and beyond anything that raging fire in corner of the room could combat.", + "gui.unicopia.spellbook.chapter.dark_magic.p15.2.body": "The room where I keep the--my patient opposite the wall behind me.", + "gui.unicopia.spellbook.chapter.dark_magic.p16.title": "Lost Sleep Cont.", + "gui.unicopia.spellbook.chapter.dark_magic.p16.1.body": "I've long since taken to keeping that door locked because every so often I could swear I heard something moving in there...", + "gui.unicopia.spellbook.chapter.dark_magic.p16.2.body": "Gods, am I going crazy?", + "gui.unicopia.spellbook.chapter.dark_magic.p17.title": "29th Slep '12", + "gui.unicopia.spellbook.chapter.dark_magic.p17.1.body": "Bits, there it is again!", + "gui.unicopia.spellbook.chapter.dark_magic.p17.2.body": "I thought last night was a fluke, but I just heard it again--I'm shaking. My hooves, I can barely hold this book as I struggle to pen these words.", + "gui.unicopia.spellbook.chapter.dark_magic.p17.3.body": "There's something--I heard something. Like a chittering--", + "gui.unicopia.spellbook.chapter.dark_magic.p18.title": "1st Croptober '12", + "gui.unicopia.spellbook.chapter.dark_magic.p18.1.body": "I made a call into town. One of the local blacksmiths have agreed to install a new lock, one of the heavy kind that not even earth ponies can bust.", + "gui.unicopia.spellbook.chapter.dark_magic.p18.2.body": "I fear it may not be enough, though, it--whatever it is, clearly has magic. I may be forced to research a magical solution to my insomnia.", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.1.body": "This spell will create a magical shroud that can be used to protect from other spellcasters.", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.2.body": "No one else will be able to use magic within its radius.", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.modifier.1": "* Increase range by adding the power trait", + "gui.unicopia.spellbook.chapter.dark_magic.displacement.1.body": "By casting this spell, a unicorn is able to swap their location with any other creature.", + "gui.unicopia.spellbook.chapter.dark_magic.p21.title": "3rd Croptober '12", + "gui.unicopia.spellbook.chapter.dark_magic.p21.1.body": "The locks have been installed, and with the addition of some extra wardings, I'm feeling a little more at ease.", + "gui.unicopia.spellbook.chapter.dark_magic.p21.2.body": "The motions at night have all but stopped, though I feel like I can almost hear it at times...", + "gui.unicopia.spellbook.chapter.dark_magic.p22.title": "4th Cropt-", + "gui.unicopia.spellbook.chapter.dark_magic.mimic.1.body": "I've been able to tap into some of this strange creature's abilities. There's still a lot to figure out here, but for now I've merely distilled its essense into a gem.", + "gui.unicopia.spellbook.chapter.dark_magic.mimic.modifier.1": "* Add the focus trait to increase the effect's duration", + "gui.unicopia.spellbook.chapter.dark_magic.p24.1.body": "There's been a wave of darkness that has come over the town. Nothing's been the same since that gods-forsaken creature arrived.", + "gui.unicopia.spellbook.chapter.dark_magic.p24.2.body": "Ponies in town have begun remarking on lack of sleep, and it's showing. Just today I saw a poor stallion walking around in a daze. Bags under his eyes, barely any colour in his cheeks.", + "gui.unicopia.spellbook.chapter.dark_magic.p25.1.body": "He looked almost like a zombie the way he went through the motions, not really paying any attention even after he nearly ran into me.", + "gui.unicopia.spellbook.chapter.dark_magic.p26.title": "Scrap", + "gui.unicopia.spellbook.chapter.dark_magic.p26.1.body": "§kIt's not enough. Never enough. Crawling. I feel crawling. Oh gods the crawling won't stop.", + "gui.unicopia.spellbook.chapter.dark_magic.p27.1.body": "The insomnia. I can't take it. I lie in my bed every night waiting to go to sleep.", + "gui.unicopia.spellbook.chapter.dark_magic.p27.2.body": "I thought I could stop it, keep it at bay, but I still hear it. That creature. Cold, unfeeling.", + "gui.unicopia.spellbook.chapter.dark_magic.p27.3.body": "I feel myself being drained any time I'm around it. Is it... feeding on me?", + "gui.unicopia.spellbook.chapter.dark_magic.p27.4.body": "No, it couldn't be.", + "gui.unicopia.spellbook.chapter.dark_magic.p28.1.body": "I found this incantation under some old notes whilst clearing out the lab. It's... simplistic and hard to manage, but it gets the job done.", + "gui.unicopia.spellbook.chapter.dark_magic.p27.2.body": "* Add the power trait to increase the effect's range", + + "gui.unicopia.spellbook.chapter.otherworldly.p1.title": "Ch.6 The Beyond", + "gui.unicopia.spellbook.chapter.otherworldly.p2.title": "2nd Croptober '12", + "gui.unicopia.spellbook.chapter.otherworldly.p2.1.body": "This chapter serves as an exploration of the worlds beyond our own and a delving into what most unicorns would normally shy away from.", + "gui.unicopia.spellbook.chapter.otherworldly.p2.2.body": "In this section can be found the most powerful of the powerful spells, but also the most danger. Read further at your own peril, as this is not for the light of mind.", + "gui.unicopia.spellbook.chapter.otherworldly.siphoning.1.body": "A simple spell that siphons life force out of a living entity and uses it to revitalise the caster.", + "gui.unicopia.spellbook.chapter.otherworldly.necromancy.1.body": "This area effect spell taps into the great beyond to summon life to serve its master.", + "gui.unicopia.spellbook.chapter.otherworldly.necromancy.2.body": "This spell is not very useful when used on its own, but combined with other traits may become a powerful tool against the §kChangeling Swarms§r.", + "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.1.body": "Dipping below the fabric of reality, this spell taps into the deep arcane powers of the beyond to punch a hole in reality.", + "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.2.body": "The resulting hole is a hungry mass that consumes all who approach. It grants massive energy, but feed it at your peril.", + "gui.unicopia.spellbook.chapter.otherworldly.portal.1.body": "Combining the effects of the displacement spell created by my predecessor and the dark vortex gem, one is able to tame its chaotic nature.", + "gui.unicopia.spellbook.chapter.otherworldly.portal.2.body": "The arcane rift spell allows the caster to link two locations together to create a bridge across the ether. Anything that enters one end will appear at the other maintaining its velocity.", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.1.body": "Continuing my predecessor's research into the abilities of the Changeling Spawn, I have enhanced his mimic spell by adding a chaotic twist.", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.2.body": "Mind Swap extends the effects of mimic to cover two individuals, in effect swapping their bodies for a limited time.", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.3.body": "* Add the focus trait to increase the effect's duration", + + "gui.unicopia.spellbook.chapter.artefacts.p1.title": "Ch.7 Artefacts", + "gui.unicopia.spellbook.chapter.artefacts.p2.title": "2nd Mare '12", + "gui.unicopia.spellbook.chapter.artefacts.p2.1.body": "What follows is a compendium of research into certain objects of interest identified through my research.", + "gui.unicopia.spellbook.chapter.artefacts.p2.2.body": "Not all of these have gotten anywhere, as they have been proven to either be pure legend, or perhaps a hoax, so I cannot put stock into their stories.", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.1.body": "Crafted by a group of unicorns long ago, its origin and current location is unknown.", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.2.body": "What few accounts exist claim it was a powerful tool of protection and support, as it would funnel life force from enemies towards the caster and their allies.", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.title": "5th Mare '12", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.3.body": "Other accounts say that this artefact only functions when mounted on a specific pedestal of diamond blocks, like a beacon.", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.title": "Torn Page", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body": "Building Materials:", + "gui.unicopia.spellbook.chapter.artefacts.crystal_podium.title": "Crystal Podium", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.2.body": "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.", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.title": "2nd Hoof '12", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.3.body": "P.S. Uncle Starswirly is a dunderhead.", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.1.body": "Used in the past by spellcasters to communicate over long distances, this band has long since lost its old use.", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.2.body": "Unicorns who sign and hand out this band can use it to share certain magic effects with their friends.", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.title": "13th Mare '12", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.3.body": "Anyone wearing a bangle you have signed will be able to benefit from the positive effects of your spells, or will be allowed through protection and shield spells.", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.4.body": "Mana costs are also shared equally between all nearby members.", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.1.body": "Commander Hurricane informed me of this, though I've found little texts to back up his claims.", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.2.body": "The Pegasus Amulet is claimed to grant the wearer temporary flight, like a pegasus.", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.title": "21st Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.3.body": "It was intended as an aide for early unicorn ambassadors to Cloud Heights, but was lost after negotiations broke down.", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.1.body": "A precursor to magic staffs, the meadwobrook's staff is an upright support structure commonly used by warlocks during long incantation sessions.", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.2.body": "It features a twisting and mottled shape with a dense and sturdy core capable of supporting the weight of an average-sized, adult male.", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.title": "22nd Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.3.body": "Due to its dense structure and flamability, this object also serves a secondary purpose as an offensive weapon and fuel source should the situation demand.", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.4.body": "To use correctly in combat, one must begin by gripping the staff by the narrow end in both hands, followed by a swift swing from above one's head whilst yelling 'Fus Roh DAH!'", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.1.body": "Magical aides for non-magical users. Magic staffs work in a similar way to a unicorns horn in that they can be used to channel and harness the innate magic stored within gems.", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.title": "22nd Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.2.body": "Not all spells work in the same way, but for those that do, a good staff is an essential tool for any beginner magi.", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.3.body": "The simplest way to create these is to put a gem on the end of a stick. Yes, very revolutionary, I know.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.1.body": "A powerful artifact once thought to be the source of King Grogar's power.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.2.body": "In skilled hooves, the bell may be used to transfer magical energy from one being to another.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.title": "22nd Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.3.body": "It's theorised this artifact was forged from the claws of an Ursa Major during the era of Discord's reign, possibly as a weapon against the tyrant.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.4.body": "By its nature, the bell a corrupting influence inherent to it that will destroy the minds of anyone who dares weird its power.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.2.title": "Grogar's Bell II", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.5.body": "Legend says that after its first bearer, King Grogar, was driven to madness, the bell was stowed far away.", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.6.body": "beyond most ponies' grasp, guarded inside an ancient city by an unbeatable beast.", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.1.body": "Like the crystal heart, little is known of this artefact and thus nothing, not even its existance can be confirmed.", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.title": "23nd Trot '12", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.2.body": "The alicorn amulet is a powerful force of dark magic created created by an unknown mage as their attempt to create the perfect being.", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.3.body": "It combines the traits of all races into one powerful form, but it hard to control and addictive in nature. Anyone who uses it quickly becomes reliant on it and few attempt to remove it survive the ordeal.", + "gui.unicopia.action.spells_cleared": "Removed all spells", "gui.unicopia.action.no_spells_cleared": "You have no active spells", diff --git a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh index 09b197aa..bdb8fec6 100644 --- a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh +++ b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.fsh @@ -3,11 +3,8 @@ #moj_import uniform sampler2D Sampler0; -uniform sampler2D Sampler1; uniform vec4 ColorModulator; -uniform float GameTime; -uniform int EndPortalLayers; in vec4 texProj0; in vec4 vertexColor; diff --git a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json index 4c978b4f..6744399f 100644 --- a/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json +++ b/src/main/resources/assets/unicopia/shaders/core/rendertype_portal_surface.json @@ -8,13 +8,10 @@ "fragment": "unicopia:rendertype_portal_surface", "attributes": [], "samplers": [ - { "name": "Sampler0" }, - { "name": "Sampler1" } + { "name": "Sampler0" } ], "uniforms": [ { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, - { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, - { "name": "GameTime", "type": "float", "count": 1, "values": [ 0.0 ] }, - { "name": "EndPortalLayers", "type": "int", "count": 1, "values": [ 15 ] } + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] } ] } diff --git a/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json index 9c416f4b..bdf768bc 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json @@ -5,46 +5,46 @@ "content": { "pages": [ { - "title": "Ch.4 Air Magic", + "title": "gui.unicopia.spellbook.chapter.air.p1.title", "level": 1, "elements": [ { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/air_magic.png" } ] }, { - "title": "1st Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p2.title", "level": 1, "elements": [ - "A new month, a new chapter. Little Luna was getting bored of sitting in the tower all day (and who's to blame her? We've been on this assignment for over two months at this point).", - "So as a little treat, we've decided to go on a little trip to the Grand Marepid Falls to visit my friend, Commander Hurricane." + "gui.unicopia.spellbook.chapter.air.p1.1.body", + "gui.unicopia.spellbook.chapter.air.p1.2.body" ] }, { "title": "", "level": 1, "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.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p1.3.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "2nd Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p3.title", "level": 0, "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" + "gui.unicopia.spellbook.chapter.air.p3.1.body", + "gui.unicopia.spellbook.author1.sign_off.b", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Air Magic I", + "title": "gui.unicopia.spellbook.chapter.air.p4.title", "level": 3, "elements": [ - "Pegasus Magic", - "Air magic is to pegasi like fire is to unicorns. They're both equally hard to control but where fire is primarily focused around force, destruction, or protection, air is all about flexibility and free motion.", - "Command Hurricane has very kindly given me some tips on how to identify this trait in everyday objects.", + "gui.unicopia.spellbook.chapter.air.p4.1.body", + "gui.unicopia.spellbook.chapter.air.p4.2.body", + "gui.unicopia.spellbook.chapter.air.p4.3.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] @@ -53,18 +53,18 @@ "title": "", "level": 3, "elements": [ - "Anything relating to flight, or that originated from creatures that fly, or that comes from up above can be considered a source of the air trait.", - "Eg. Feathers.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p4.4.body", + "gui.unicopia.spellbook.chapter.air.p4.5.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Catapult", + "title": "spell.unicopia.catapult", "level": 4, "elements": [ - "This is a straightforward application of a unicorn's telekineses. The catapult gem allows a caster to grab any block or creature and fling them into the air.", - "Use it again on something already thrown to push it away from you.", + "gui.unicopia.spellbook.chapter.air.catapult.1.body", + "gui.unicopia.spellbook.chapter.air.catapult.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] @@ -74,7 +74,7 @@ "level": 4, "elements": [ { "recipe": "unicopia:spells/catapult" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:flame" }, @@ -82,15 +82,15 @@ { "count": 9, "trait": "unicopia:air" } ] }, - "* One can add apply more force by adding the strength trait" + "gui.unicopia.spellbook.chapter.air.catapult.modifier.1" ] }, { - "title": "Bubble", + "title": "spell.unicopia.bubble", "level": 2, "elements": [ - "A defensive and utility spell. Bubble will trap the target in a giant soap bubble, rendering them defensless.", - "Use it again will pop the bubble.", + "gui.unicopia.spellbook.chapter.air.bubble.1.body", + "gui.unicopia.spellbook.chapter.air.bubble.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/water.png" } ] @@ -100,7 +100,7 @@ "level": 2, "elements": [ { "recipe": "unicopia:spells/bubble" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:catapult" }, @@ -111,44 +111,44 @@ ] }, { - "title": "8th Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p7.title", "level": 4, "elements": [ - "I thought I would take a short moment to write down an entry to record my findings whilst Luna and The Commander are out.", - "Pegasi magic really is a fascinating beast. It's not like our magic, which is more of a study, with rigid rules and practices." + "gui.unicopia.spellbook.chapter.air.p7.1.body", + "gui.unicopia.spellbook.chapter.air.p7.2.body" ] }, { - "title": "Pegasi", + "title": "gui.unicopia.spellbook.chapter.air.p8.title", "level": 5, "elements": [ - "Pegasus magic is more about feeling. It's an art form. They don't think about what they want to do, it just happens, but it all still follows the same principle." + "gui.unicopia.spellbook.chapter.air.p8.1.body" ] }, { - "title": "Pegasi II", + "title": "gui.unicopia.spellbook.chapter.air.p9.title", "level": 6, "elements": [ - "Take for example their cloud homes. There are no spells I can read that would let me do this, but if you feel, not with your hooves or your horn, but properly, with your mind, your heart, you will find magic buzzing all throughout.", - "Clouds are teeming with the air and water trait, but also others, like power, strength, life, earth. All traits of the material the clouds are mimicking." + "gui.unicopia.spellbook.chapter.air.p9.1.body", + "gui.unicopia.spellbook.chapter.air.p9.2.body" ] }, { - "title": "Pegasi III", + "title": "gui.unicopia.spellbook.chapter.air.p10.title", "level": 7, "elements": [ - "I wish I could study this further, but I'm afraid to interfere in this magic I don't yet fully understand.", - "Commander Hurricane may never forgive me if I destroy her home the first time I'm here.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p10.1.body", + "gui.unicopia.spellbook.chapter.air.p10.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Feather Falling", + "title": "spell.unicopia.feather_fall", "level": 5, "elements": [ - "Expanding on the defensive capabilities of the protection gem, I've attempted to extend its advantages to party members.", - "This one is unusual because of its complexity, but in theory it should allow one to slow their own and friends' descent.", + "gui.unicopia.spellbook.chapter.air.feather_fall.1.body", + "gui.unicopia.spellbook.chapter.air.feather_fall.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] @@ -158,7 +158,7 @@ "level": 5, "elements": [ { "recipe": "unicopia:spells/feather_fall" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:shield" }, @@ -171,68 +171,47 @@ ] }, { - "title": "10th Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p12.title", "level": 6, "elements": [ - "Went to dinner with Commander Huricane and Luna. We got to talking about architecture and Hurricane mentioned the Taz Marehall.", - "Luna thought it was a rather funny name of a castle. I had to remind her that not all cultures are the same.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p12.1.body", + "gui.unicopia.spellbook.chapter.air.p12.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "21st Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p13.title", "level": 7, "elements": [ - { - "text": "I'm writing this on the eve of my return to", - "extra": [ - { "text": "Trotholm.", "obfuscated": true }, - "Though my time in Cloudopolis has been elightening, I look forward to a return to the familiar surroundings and a proper rest in my own solid bed." - ] - }, - "I cannot say the same for Luna, though. She is currently sitting on my bed beside me pouting over every little thing she sees me put into my saddlebag." + "gui.unicopia.spellbook.chapter.air.p13.1.body", + "gui.unicopia.spellbook.chapter.air.p13.2.body" ] }, { - "title": "Returning Home", + "title": "gui.unicopia.spellbook.chapter.air.p14.title", "level": 7, "elements": [ - { - "text": "She keeps insisting that we stay a little longer", - "extra": [ - { "text": "to hang out with that pegasus colt I saw her with the other day, no doubt", "strikethrough": true} - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.air.p14.1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "22nd Hoof '12", + "title": "gui.unicopia.spellbook.chapter.air.p15.title", "level": 8, "elements": [ - { - "text": "As we're flying above the mountain tops, I can't help but feel in awe the beauty that", - "extra": [ - { "text": "Mother Faust", "obfuscated": true}, - "has given us." - ] - }, - "I had to spend the whole time keeping Luna from falling out, and of course answering questions about all the different kinds of clouds. Thankfully, the balloon operator was there to help with the subtleties." + "gui.unicopia.spellbook.chapter.air.p15.1.body", + "gui.unicopia.spellbook.chapter.air.p15.2.body" ] }, { "title": "", "level": 8, "elements": [ - "Being out here, above all of our troubles, really makes me forget the rest of the world for a moment. It takes me back to a simpler time, before the--", - "Of course the flashes of explosions to the west has to ruin it all. There appears to be a storm on the horizon. I certainly hope it not to be a bad omen..." + "gui.unicopia.spellbook.chapter.air.p15.3.body", + "gui.unicopia.spellbook.chapter.air.p15.4.body" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json b/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json index 707f95b8..795e6f83 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json @@ -5,41 +5,41 @@ "content": { "pages": [ { - "title": "Ch.7 Artefacts", + "title": "gui.unicopia.spellbook.chapter.artefacts.p1.title", "level": 0, "elements": [ { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/crystal_heart.png" } ] }, { - "title": "2nd Mare '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.p2.title", "level": 0, "elements": [ - "What follows is a compendium of research into certain objects of interest identified through my research.", - "Not all of these have gotten anywhere, as they have been proven to either be pure legend, or perhaps a hoax, so I cannot put stock into their stories.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.artefacts.p2.1.body", + "gui.unicopia.spellbook.chapter.artefacts.p2.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "The Crystal Heart", + "title": "item.unicopia.crystal_heart", "level": 0, "elements": [ { "item": { "item": "unicopia:crystal_heart" } }, - "Status: Lost", - "Crafted by a group of unicorns long ago, its origin and current location is unknown.", - "What few accounts exist claim it was a powerful tool of protection and support, as it would funnel life force from enemies towards the caster and their allies." + "gui.unicopia.spellbook.chapter.artefacts.status.lost", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.1.body", + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.2.body" ] }, { - "title": "5th Mare '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.title", "level": 0, "elements": [ - "Other accounts say that this artefact only functions when mounted on a specific pedestal of diamond blocks, like a beacon." + "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.3.body" ] }, { - "title": "Torn Page", + "title": "gui.unicopia.spellbook.chapter.artefacts.torn_page.title", "level": 0, "elements": [ { @@ -58,7 +58,7 @@ { "text": "Aasa sasa fwefsd q43rgfd wqklmsdfl as, klasn.", "obfuscated": "true" }, - "Building Materials:", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body", { "ingredients": [ { "count": 2, "item": "minecraft:end_rod" }, @@ -69,7 +69,7 @@ ] }, { - "title": "Crystal Podium", + "title": "gui.unicopia.spellbook.chapter.artefacts.crystal_podium.title", "level": 0, "elements": [ { "x": 40, "item": { "item": "minecraft:diamond_block" } }, @@ -86,134 +86,134 @@ ] }, { - "title": "Dragon's Breath Scroll", + "title": "item.unicopia.dragon_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" + "gui.unicopia.spellbook.chapter.artefacts.status.confirmed", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.2.body", + "gui.unicopia.spellbook.author3.name" ] }, { - "title": "2nd Hoof '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.title", "level": 0, "elements": [ - "P.S. Uncle Starswirly is a dunderhead.", + "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.3.body", { "recipe": "unicopia:dragon_breath_scroll" } ] }, { - "title": "Bangle of Comradery", + "title": "item.unicopia.friendship_bracelet", "level": 0, "elements": [ { "item": { "item": "unicopia:friendship_bracelet" } }, - "Status: Confirmed", - "Used in the past by spellcasters to communicate over long distances, this band has long since lost its old use.", - "Unicorns who sign and hand out this band can use it to share certain magic effects with their friends." + "gui.unicopia.spellbook.chapter.artefacts.status.confirmed", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.1.body", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.2.body" ] }, { - "title": "13th Mare '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.title", "level": 0, "elements": [ - "Anyone wearing a bangle you have signed will be able to benefit from the positive effects of your spells, or will be allowed through protection and shield spells.", - "Mana costs are also shared equally between all nearby members." + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.3.body", + "gui.unicopia.spellbook.chapter.artefacts.friendship_bracelet.4.body" ] }, { - "title": "Wings of Icarus", + "title": "item.unicopia.pegasus_amulet", "level": 0, "elements": [ { "item": { "item": "unicopia:pegasus_amulet" } }, - "Status: Lost", - "Commander Hurricane informed me of this, though I've found little texts to back up his claims.", - "The Pegasus Amulet is claimed to grant the wearer temporary flight, like a pegasus." + "gui.unicopia.spellbook.chapter.artefacts.status.lost", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.1.body", + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.2.body" ] }, { - "title": "21st Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.title", "level": 0, "elements": [ - "It was intended as an aide for early unicorn ambassadors to Cloud Heights, but was lost after negotiations broke down." + "gui.unicopia.spellbook.chapter.artefacts.pegasus_amulet.3.body" ] }, { - "title": "Meadowbrook's Staff", + "title": "item.unicopia.meadowbrooks_staff", "level": 3, "elements": [ { "item": { "item": "unicopia:meadowbrooks_staff" } }, - "Status: Confirmed", - "A precursor to magic staffs, the meadwobrook's staff is an upright support structure commonly used by warlocks during long incantation sessions.", - "It features a twisting and mottled shape with a dense and sturdy core capable of supporting the weight of an average-sized, adult male." + "gui.unicopia.spellbook.chapter.artefacts.status.confirmed", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.1.body", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.2.body" ] }, { - "title": "22nd Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.title", "level": 3, "elements": [ - "Due to its dense structure and flamability, this object also serves a secondary purpose as an offensive weapon and fuel source should the situation demand.", - "To use correctly in combat, one must begin by gripping the staff by the narrow end in both hands, followed by a swift swing from above one's head whilst yelling 'Fus Roh DAH!'" + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.3.body", + "gui.unicopia.spellbook.chapter.artefacts.meadowbrooks_staff.4.body" ] }, { - "title": "Magic Staffs", + "title": "item.unicopia.magic_staff", "level": 5, "elements": [ { "item": { "item": "unicopia:magic_staff" } }, - "Status: Unconfirmed", - "Magical aides for non-magical users. Magic staffs work in a similar way to a unicorns horn in that they can be used to channel and harness the innate magic stored within gems." + "gui.unicopia.spellbook.chapter.artefacts.status.unconfirmed", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.1.body" ] }, { - "title": "22nd Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.magic_staff.title", "level": 5, "elements": [ - "Not all spells work in the same way, but for those that do, a good staff is an essential tool for any beginner magi.", - "The simplest way to create these is to put a gem on the end of a stick. Yes, very revolutionary, I know." + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.2.body", + "gui.unicopia.spellbook.chapter.artefacts.magic_staff.3.body" ] }, { - "title": "Grogar's Bell", + "title": "item.unicopia.grogars_bell", "level": 0, "elements": [ { "item": { "item": "unicopia:grogars_bell" } }, - "Status: Lost", - "A powerful artifact once thought to be the source of King Grogar's power.", - "In skilled hooves, the bell may be used to transfer magical energy from one being to another." + "gui.unicopia.spellbook.chapter.artefacts.status.lost", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.1.body", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.2.body" ] }, { - "title": "22nd Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.title", "level": 80, "elements": [ - "It's theorised this artifact was forged from the claws of an Ursa Major during the era of Discord's reign, possibly as a weapon against the tyrant.", - "By its nature, the bell a corrupting influence inherent to it that will destroy the minds of anyone who dares weird its power." + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.3.body", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.4.body" ] }, { - "title": "Grogar's Bell II", + "title": "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.2.title", "level": 80, "elements": [ - "Legend says that after its first bearer, King Grogar, was driven to madness, the bell was stowed far away.", - "beyond most ponies' grasp, guarded inside an ancient city by an unbeatable beast." + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.5.body", + "gui.unicopia.spellbook.chapter.artefacts.grogars_bell.6.body" ] }, { - "title": "Alicorn Amulet", + "title": "item.unicopia.alicorn_amulet", "level": 0, "elements": [ { "item": { "item": "unicopia:alicorn_amulet" } }, - "Status: Unconfirmed", - "Like the crystal heart, little is known of this artefact and thus nothing, not even its existance can be confirmed." + "gui.unicopia.spellbook.chapter.artefacts.status.unconfirmed", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.1.body" ] }, { - "title": "23nd Trot '12", + "title": "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.title", "level": 999, "elements": [ - "The alicorn amulet is a powerful force of dark magic created created by an unknown mage as their attempt to create the perfect being.", - "It combines the traits of all races into one powerful form, but it hard to control and addictive in nature. Anyone who uses it quickly becomes reliant on it and few attempt to remove it survive the ordeal." + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.2.body", + "gui.unicopia.spellbook.chapter.artefacts.alicorn_amulet.3.body" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json index 1d888807..ded71e87 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json @@ -5,52 +5,44 @@ "content": { "pages": [ { - "title": "Ch.5 The Arcane", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p1.title", "level": 10, "elements": [ { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" } ] }, { - "title": "30th Hoof '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p2.title", "level": 8, "elements": [ - "A new communication from the crown today. The situation seems to be worsening in the west and though they've been satisfied with my work until now, they are putting pressure on me to produce something more destructive.", - "I tried to tell the messenger that I couldn't-" + "gui.unicopia.spellbook.chapter.dark_magic.p2.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p2.2.body" ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "3rd Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p3.title", "level": 9, "elements": [ - "Let it be known that it was never my intention that anypony use my work for nefarious purposes. I am a researcher, above and beyond all else. My intentions are pure, and my wants are nothing more than this world to be at peace." + "gui.unicopia.spellbook.chapter.dark_magic.p3.1.body" ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "4th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p4.title", "level": 10, "elements": [ - "No review of magic is ever complete without a glimpse into the other side.", - "Dark magic, or as I'm going to refer to it as The Arcane are tip-toeing the line between the normal light magic we normally know and the more nefarious side of reality.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p4.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p4.2.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Attraction", + "title": "spell.unicopia.vortex", "level": 5, "elements": [ - "If someone were to ask you 'what is the opposite of a repulsion spell, what would you say? An attraction spell, of course!", - "By twisting the purpose of the protection spell, I've been able to reverse its function to create a spell that pulls objects and entities closer to the caster.", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.vortex.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/knowledge.png" } ] @@ -60,7 +52,7 @@ "level": 5, "elements": [ { "recipe": "unicopia:spells/vortex" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:shield" }, @@ -71,107 +63,93 @@ }, { "ingredients": [ - { "text": "+ 10x knowledge to narrow the effect's range to items" }, - { "text": "+ add focus trait to increase duration\n+ add power trait to increase range" } + { "text": "gui.unicopia.spellbook.chapter.dark_magic.vortex.modifier.1" }, + { "text": "gui.unicopia.spellbook.chapter.dark_magic.vortex.modifier.2" } ] } ] }, { - "title": "8th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p6.title", "level": 6, "elements": [ - "Additional Notes for the Attraction Spell", - "I caught Luna playing with my spellcrafting grid today, even though I expressly forbid her from entering my study when I'm not there.", - "Apparently it was over some dispute with Celly, I don't really remember, but it culminated in Luna sneaking into the study whilst I was out to get some bread." + "gui.unicopia.spellbook.chapter.dark_magic.p6.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p6.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p6.3.body" ] }, { - "title": "Arcane Attraction II", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p7.title", "level": 6, "elements": [ - "This isn't really about that, though. She's been scolded and sent back to her room, however as I was cleaning up the mess she'd made I noticed something in the piles of gems.", - "It's hard to describe, really. This is still distincly an attraction gem, but it's different.", - "It has traits I hadn't considered before, and the way it behaves... " + "gui.unicopia.spellbook.chapter.dark_magic.p7.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p7.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p7.3.body" ] }, { - "title": "Arcane Attraction II Cont.", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p8.title", "level": 6, "elements": [ - "Well I'll leave that up to tomorrow. I'm still tired from everything that's happened this week.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p8.1.body", + "gui.unicopia.spellbook.author1.name" ] }, { "title": "", "level": 6, "elements": [ - ">0 generosity --> ??", - ">20 order trait --> ???" + "gui.unicopia.spellbook.chapter.dark_magic.p8.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p8.3.body" ] }, { - "title": "20th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p9.title", "level": 11, "elements": [ - "As per their agreement, the council have sent certain...supplimental materials to aid in the new direction my research is taking. I was a little shocked at first.", - "This... thing... Whatever it is. Was, rather.", - "Is this really what we're fighting in the west?", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p9.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p9.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p9.3.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "21st Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p10.title", "level": 11, "elements": [ - "I've put the... thing. In the basement. Locked the door.", - "I just couldn't stand looking at it any longer. It's vaguely pony-shaped, but also...", - "I couldn't well let Luna see it. I've sent her out to spend the next few nights with Celly whilst I sort out what to do with this.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p10.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p10.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p10.3.body", + "gui.unicopia.spellbook.author1.name" ] }, + { }, + { }, { - "title": "", - "level": -1, - "elements": [] - }, - { - "title": "", - "level": -1, - "elements": [] - }, - { - "title": "25th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p11.title", "level": 13, "elements": [ - "I'm sorry for the long delays. Things have been... busy.", - "I've learned a lot about these creatures. Attached are some illustrations, done best I could so I wouldn't have to look at the thing directly.", - { - "text": "Its body is black and vaguely", - "extra": [ - { "text": "insect-like", "obfuscated": true }, - "with ponish proportions. It has no fur." - ] - } + "gui.unicopia.spellbook.chapter.dark_magic.p11.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p11.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p11.3.body" ] }, { "title": "", "level": 14, "elements": [ - "The magic they use is unusual. Not unusual, like what I've been studying. It's unnatural. Wild.", - "There is definitely something I might be able to harness here, but I shudder... Should I?", - "I fear that this may be a line that shouldn't be crossed.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p11.4.body", + "gui.unicopia.spellbook.chapter.dark_magic.p11.5.body", + "gui.unicopia.spellbook.chapter.dark_magic.p11.6.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Transmutation", + "title": "spell.unicopia.transformation", "level": 13, "elements": [ - "I've begun by simply harnessing their ability. It's unfocused and hard to control. I can rarely predict what is going to happen, but this gem has very clear transmodrification properties.", - "Throwing this at any creature has the chance to transform it into any other creature.", + "gui.unicopia.spellbook.chapter.dark_magic.transformation.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.transformation.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/life.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] @@ -181,7 +159,7 @@ "level": 13, "elements": [ { "recipe": "unicopia:spells/transformation" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 18, "trait": "unicopia:knowledge" }, @@ -192,11 +170,11 @@ ] }, { - "title": "Dispell Illusion", + "title": "spell.unicopia.reveal", "level": 15, "elements": [ - "Dispell Illusion is the first line of defense against transformation/illusion spells.", - "When cast it will force any nearby disguised changelings in its range to reveal their true form.", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.reveal.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] @@ -206,7 +184,7 @@ "level": 15, "elements": [ { "recipe": "unicopia:spells/reveal" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:shield" }, @@ -215,64 +193,60 @@ { "count": 4, "trait": "unicopia:order" } ] }, - "* Increase range by adding the power trait" + "gui.unicopia.spellbook.chapter.dark_magic.reveal.modifier.1" ] }, { - "title": "27th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p14.title", "level": 14, "elements": [ - "Sleep the last few nights has become... elusive.", - "I don't know how to describe it, really. Luna appears unaffected, but every night after the sun goes down I find myself lying awake at night unable to sleep. It doesn't help that that sounds in the village have resumed." + "gui.unicopia.spellbook.chapter.dark_magic.p14.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p14.2.body" ] }, { - "title": "Lost Sleep", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p15.title", "level": 15, "elements": [ - "Last night, especially, I found myself pacing in the observatory. The air became chill, more than is normal for this time of year, and beyond anything that raging fire in corner of the room could combat.", - "The room where I keep the--my patient opposite the wall behind me." + "gui.unicopia.spellbook.chapter.dark_magic.p15.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p15.2.body" ] }, { - "title": "Lost Sleep Cont.", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p16.title", "level": 15, "elements": [ - "I've long since taken to keeping that door locked because every so often I could swear I heard something moving in there...", - "Gods, am I going crazy?", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p16.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p16.2.body", + "gui.unicopia.spellbook.author1.name" ] }, + { }, { - "title": "", - "level": -1, - "elements": [] - }, - { - "title": "29th Slep '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p17.title", "level": 18, "elements": [ - "Bits, there it is again!", - "I thought last night was a fluke, but I just heard it again--I'm shaking. My hooves, I can barely hold this book as I struggle to pen these words.", - "There's something--I heard something. Like a chittering--", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p17.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p17.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p17.3.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "1st Croptober '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p18.title", "level": 14, "elements": [ - "I made a call into town. One of the local blacksmiths have agreed to install a new lock, one of the heavy kind that not even earth ponies can bust.", - "I fear it may not be enough, though, it--whatever it is, clearly has magic. I may be forced to research a magical solution to my insomnia.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p18.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p18.2.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Arcane Protection", + "title": "spell.unicopia.arcane_protection", "level": 16, "elements": [ - "This spell will create a magical shroud that can be used to protect from other spellcasters.", - "No one else will be able to use magic within its radius.", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] @@ -282,7 +256,7 @@ "level": 16, "elements": [ { "recipe": "unicopia:spells/arcane_protection" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:shield" }, @@ -291,14 +265,14 @@ { "count": 1, "trait": "unicopia:darkness" } ] }, - "* Increase range by adding the power trait" + "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.modifier.1" ] }, { - "title": "Displacement", + "title": "spell.unicopia.displacement", "level": 17, "elements": [ - "By casting this spell, a unicorn is able to swap their location with any other creature.", + "gui.unicopia.spellbook.chapter.dark_magic.displacement.1.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] @@ -308,7 +282,7 @@ "level": 17, "elements": [ { "recipe": "unicopia:spells/displacement" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "item": "unicopia:gemstone" }, @@ -319,26 +293,24 @@ ] }, { - "title": "3rd Croptober '12", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p21.title", "level": 14, "elements": [ - "The locks have been installed, and with the addition of some extra wardings, I'm feeling a little more at ease.", - "The motions at night have all but stopped, though I feel like I can almost hear it at times...", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.dark_magic.p21.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p21.2.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "4th Cropt-", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p22.title", "level": 15, - "elements": [ - "" - ] + "elements": [] }, { - "title": "Mimic", + "title": "spell.unicopia.mimic", "level": 17, "elements": [ - "I've been able to tap into some of this strange creature's abilities. There's still a lot to figure out here, but for now I've merely distilled its essense into a gem.", + "gui.unicopia.spellbook.chapter.dark_magic.mimic.1.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] @@ -348,7 +320,7 @@ "level": 18, "elements": [ { "recipe": "unicopia:spells/mimic" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:transformation" }, @@ -357,65 +329,50 @@ { "count": 4, "trait": "unicopia:chaos" } ] }, - "* Add the focus trait to increase the effect's duration" + "gui.unicopia.spellbook.chapter.dark_magic.mimic.modifier.1" ] }, + { }, + { }, { - "title": "", - "level": 0, + "title": "??? ???? ??", + "level": 44, "elements": [ - "" - ] - }, - { - "title": "", - "level": 0, - "elements": [ - "" + "gui.unicopia.spellbook.chapter.dark_magic.p24.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p24.2.body" ] }, { "title": "??? ???? ??", "level": 44, "elements": [ - "There's been a wave of darkness that has come over the town. Nothing's been the same since that gods-forsaken creature arrived.", - "Ponies in town have begun remarking on lack of sleep, and it's showing. Just today I saw a poor stallion walking around in a daze. Bags under his eyes, barely any colour in his cheeks." + "gui.unicopia.spellbook.chapter.dark_magic.p25.1.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "??? ???? ??", - "level": 44, - "elements": [ - "He looked almost like a zombie the way he went through the motions, not really paying any attention even after he nearly ran into me.", - "- Starswirl the Bearded" - ] - }, - { - "title": "Scrap", + "title": "gui.unicopia.spellbook.chapter.dark_magic.p26.title", "level": 21, "elements": [ - { - "text": "It's not enough. Never enough. Crawling. I feel crawling. Oh gods the crawling won't stop.", - "obfuscated": true - } + "gui.unicopia.spellbook.chapter.dark_magic.p26.1.body" ] }, { "title": "??? ???? ??", "level": 33, "elements": [ - "The insomnia. I can't take it. I lie in my bed every night waiting to go to sleep.", - "I thought I could stop it, keep it at bay, but I still hear it. That creature. Cold, unfeeling.", - "I feel myself being drained any time I'm around it. Is it... feeding on me?", - "No, it couldn't be." + "gui.unicopia.spellbook.chapter.dark_magic.p27.1.body", + "gui.unicopia.spellbook.chapter.dark_magic.p27.2.body", + "gui.unicopia.spellbook.chapter.dark_magic.p27.3.body", + "gui.unicopia.spellbook.chapter.dark_magic.p27.4.body" ] }, { "title": "????? ????", "level": 19, "elements": [ - "I found this incantation under some old notes whilst clearing out the lab. It's... simplistic and hard to manage, but it gets the job done.", - "- Starswirl the Bearded", + "gui.unicopia.spellbook.chapter.dark_magic.p28.1.body", + "gui.unicopia.spellbook.author1.name", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/kindness.png" } ] @@ -425,7 +382,7 @@ "level": 19, "elements": [ { "recipe": "unicopia:spells/dispel_evil" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:arcane_protection" }, @@ -434,7 +391,7 @@ { "count": 1, "trait": "unicopia:power" } ] }, - "* Add the power trait to increase the effect's range" + "gui.unicopia.spellbook.chapter.dark_magic.p27.2.body" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json index d7014015..9657c486 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json @@ -5,28 +5,28 @@ "content": { "pages": [ { - "title": "Ch.2 Fire Magic", + "title": "gui.unicopia.spellbook.chapter.fire.p1.title", "level": 0, "elements": [ { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/fire_magic.png" } ] }, { - "title": "9th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p2.title", "level": 0, "elements": [ - "It took me longer than I anticipated, nearly a month! Hah! But I present to you, dear reader, my findings for the first elementary form of magic: FIRE.", - "It's a working title, okay?", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p2.1.body", + "gui.unicopia.spellbook.chapter.fire.p2.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Scorch", + "title": "spell.unicopia.scorch", "level": 0, "elements": [ - "Simple and to the point, Scorch does exactly what you'd think. By embuing a gem with the fire trait, one can indute it to glow and become hot to the touch.", - "The effect becomes stronger the more fire you load it with, but take care not to overload it, as it may become volatile.", + "gui.unicopia.spellbook.chapter.fire.scorch.1.body", + "gui.unicopia.spellbook.chapter.fire.scorch.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } ] @@ -36,7 +36,7 @@ "level": 0, "elements": [ { "recipe": "unicopia:spells/scorch" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 10, "trait": "unicopia:fire" } @@ -45,11 +45,11 @@ ] }, { - "title": "Flame", + "title": "spell.unicopia.flame", "level": 1, "elements": [ - "Creates a heating affect up to a radius of 3 hooves from any surfaces it touches.", - "Useful when one needs a flame in a hurry or to fend off a wild wendigo.", + "gui.unicopia.spellbook.chapter.fire.flame.1.body", + "gui.unicopia.spellbook.chapter.fire.flame.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } ] @@ -59,7 +59,7 @@ "level": 1, "elements": [ { "recipe": "unicopia:spells/flame" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "item": "unicopia:gemstone" }, @@ -69,60 +69,51 @@ } ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "10th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p5.title", "level": 0, "elements": [ - "Progress?", - "I've managed to improve the previous spell somewhat, but there is still something lacking. It's all very orderly. Predicatable.", - "Luna has suggested adding more fire, but I'm weary to create more scorch marks on the tower. Faust save me if anypony were to find out what I've been doing..." + "gui.unicopia.spellbook.chapter.fire.p5.1.body", + "gui.unicopia.spellbook.chapter.fire.p5.2.body", + "gui.unicopia.spellbook.chapter.fire.p5.3.body" ] }, { - "title": "Fire Magic III", + "title": "gui.unicopia.spellbook.chapter.fire.p6.title", "level": 2, "elements": [ - "Focusing Magic", - "Some spells normally take a great amount of focus to cast, and a steady wit to control, However I've found objects embued with the focusing trait work wonderfully as a substitute for when the caster is lacking.", + "gui.unicopia.spellbook.chapter.fire.p6.1.body", + "gui.unicopia.spellbook.chapter.fire.p6.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } ] }, { - "title": "Fire Magic III-II", + "title": "gui.unicopia.spellbook.chapter.fire.p7.title", "level": 2, "elements": [ - "Any glass objects you can find, eyes, bottles, whatever incorporates a lense can be used to embue focus on a spell.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p7.1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Scrap 2", + "title": "gui.unicopia.spellbook.chapter.fire.p8.title", "level": 21, "elements": [ - "We went to the market today. Had to get out of that tower, do something, be somewhere. Luna suggested we go in to see what they were selling for the fair so I decided to indulge her.", - "The townsfolk are still rather skeptical of us, though who's to blame them. Luna was getting along well with the other foals, at least." + "gui.unicopia.spellbook.chapter.fire.p8.1.body", + "gui.unicopia.spellbook.chapter.fire.p8.2.body" ] }, + { }, { - "title": "", - "level": 21, - "elements": [ - ] - }, - { - "title": "Fire Bolt", + "title": "spell.unicopia.fire_bolt", "level": 2, "elements": [ - "Creates a series of heated projectiles to fling at a target. Upon impact the target will be set alight.", - "- Increase focus will allow finer grained control of the projectile's trajectory.", - "- With over 50 focus it's almost like they know where the target is (homing?).", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.1.body", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.2.body", + "gui.unicopia.spellbook.chapter.fire.fire_bolt.3.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } ] @@ -132,7 +123,7 @@ "level": 2, "elements": [ { "recipe": "unicopia:spells/fire_bolt" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:flame" }, @@ -143,104 +134,80 @@ ] }, { - "title": "Fire Magic IV", + "title": "gui.unicopia.spellbook.chapter.fire.p10.title", "level": 2, "elements": [ - "Powerful Magic", - "Where some spells take focus, others require power. Either to exert a force, or to generate energy in some form.", - "There are few unicorns that have the inherent strength and power to cast spells of these kinds, but luckily such a trait is not in short supply around us.", + "gui.unicopia.spellbook.chapter.fire.p10.1.body", + "gui.unicopia.spellbook.chapter.fire.p10.2.body", + "gui.unicopia.spellbook.chapter.fire.p10.3.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/strength.png" } ] }, { - "title": "Fire Magic IV-II", + "title": "gui.unicopia.spellbook.chapter.fire.p11.title", "level": 2, "elements": [ - "Earthly elements, stone, many metals and minerals, that are strong under compression will exhibit the strength trait.", - "Electrical/Conductive elements that can be used to power things, or that glow can also be used to obtain the power trait.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p11.1.body", + "gui.unicopia.spellbook.chapter.fire.p11.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "11th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p12.title", "level": 2, "elements": [ - "Got a knock at the door today. Luna was very eager to answer it but I had to send her away as it was a messenger from the crown.", - "My research has garnered a certain bit of notoriety, it appears. The royals are very eager to see what I have concocted.", - "They've gotten it in their heads that they can use it against the West." + "gui.unicopia.spellbook.chapter.fire.p12.1.body", + "gui.unicopia.spellbook.chapter.fire.p12.2.body", + "gui.unicopia.spellbook.chapter.fire.p12.3.body" ] }, { "title": "", "level": 13, "elements": [ - "Gods forbid they succeed. I shudder to think what the council might do if they got their hooves on my work.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p12.4.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { "title": "", "level": 3, "elements": [ - "Addendum", - { - "text": "", - "strikethrough": true, - "extra": [ - {"text": "I am told the crown has started giving directions to find other uses. Ways to..."} - ] - } + "gui.unicopia.spellbook.chapter.fire.p12.5.body", + "gui.unicopia.spellbook.chapter.fire.p12.6.body" ] }, + { }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "20th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p13.title", "level": 9, "elements": [ - "I have word from the crown. They appear satisfied, for now, and have agreed to let my continue my research as I have into the winter.", - "I am under duress to destroy the last several entries, I'm afraid.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p13.1.body", + "gui.unicopia.spellbook.chapter.fire.p13.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "21st Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p14.title", "level": 0, "elements": [ - { - "text": "I shall visit", - "extra": [ - { "text": "Commander Hurricane", "obfuscated": true }, - " tomorrow. Perhaps she may shed light onto my predicament." - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.fire.p14.1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, + { }, { - "title": "", - "level": 0, - "elements": [] - }, - { - "title": "Protection", + "title": "spell.unicopia.shield", "level": 2, "elements": [ - "Casting shields are one of the first things every unicorn learns in self-defense. It's simple and easy to cast, and is an excellent introduction to incanting.", - "Its disadvantage is the energy and mental cost, however we can negate both by attaching it to a gem as per follows...", + "gui.unicopia.spellbook.chapter.fire.shield.1.body", + "gui.unicopia.spellbook.chapter.fire.shield.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" } ] }, @@ -249,7 +216,7 @@ "level": 2, "elements": [ { "recipe": "unicopia:spells/shield" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "item": "unicopia:gemstone" }, @@ -258,35 +225,30 @@ { "count": 10, "trait": "unicopia:power" } ] }, - "+ add power trait to increase effect range" + "gui.unicopia.spellbook.chapter.fire.shield.modifier.1" ] }, { - "title": "Protection II", + "title": "gui.unicopia.spellbook.chapter.fire.p16.title", "level": 4, "elements": [ - "By adding extra traits, I was able to slightly modify the shield to allow or deny certain parties into the effect range.", - "+ add life trait --> all animals may enter\n+ add blood trait --> all monsters may enter\n+ add ice trait --> all ponies may enter" + "gui.unicopia.spellbook.chapter.fire.p16.1.body", + "gui.unicopia.spellbook.chapter.fire.p16.2.body" ] }, { - "title": "Protection III", + "title": "gui.unicopia.spellbook.chapter.fire.p17.title", "level": 5, "elements": [ - "+ add genorosity trait to attach the spell to a location rather than yourself" + "gui.unicopia.spellbook.chapter.fire.p17.1.body" ] }, { - "title": "Scrap: 9th Jum '12", + "title": "gui.unicopia.spellbook.chapter.fire.p18.title", "level": 3, "elements": [ - "Fire magic has proven to a be a little more... unpredictable than anticipated. Every time I feel like I'm making progress it finds a way to set me back.", - { - "text": "I can't stop now, though...I'm told the situation in the west is growing dire. They have asked me to pick up the pace and produce something we can use to get the upper hoof against the", - "extra": [ - { "text": "Changeling Storm.", "obfuscated": true} - ] - } + "gui.unicopia.spellbook.chapter.fire.p18.1.body", + "gui.unicopia.spellbook.chapter.fire.p18.2.body" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json index 1c05cb6b..523f318c 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json @@ -5,28 +5,28 @@ "content": { "pages": [ { - "title": "Ch.3 Ice Magic", + "title": "gui.unicopia.spellbook.chapter.ice.p1.title", "level": 0, "elements": [ { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/ice_magic.png" } ] }, { - "title": "4th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p2.title", "level": 0, "elements": [ - "This is an interesting one. Rather simple, I admit, but Luna insisted I make something cold to help us deal with this darn heat.", - "All you need is a gem and something cold. Like a snowball.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p2.1.body", + "gui.unicopia.spellbook.chapter.ice.p2.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Frost", + "title": "spell.unicopia.frost", "level": 0, "elements": [ - "Creates a chilling affect up to a radius of 3 hooves from any surfaces it touches.", - "Will sap energy out of the immediate environment causing a phase change.", + "gui.unicopia.spellbook.chapter.ice.frost.1.body", + "gui.unicopia.spellbook.chapter.ice.frost.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] @@ -36,7 +36,7 @@ "level": 3, "elements": [ { "recipe": "unicopia:spells/frost" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 15, "trait": "unicopia:ice" } @@ -45,11 +45,11 @@ ] }, { - "title": "Chilling Breath", + "title": "gui.unicopia.spellbook.chapter.ice.p4.title", "level": 0, "elements": [ - "Alters the ability of certain objects to distenguish between hot and cold.", - "This is a very weak spell, but when used with a boat can be exceedingly useful to get out of a sticky stituation.", + "gui.unicopia.spellbook.chapter.ice.p4.1.body", + "gui.unicopia.spellbook.chapter.ice.p4.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/oak_boat.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] @@ -59,7 +59,7 @@ "level": 0, "elements": [ { "recipe": "unicopia:spells/chilling_breath" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:frost" }, @@ -70,44 +70,40 @@ ] }, { - "title": "5th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p5.title", "level": 4, "elements": [ - "The village-ponies had a bonfire last night. I could tell by the strong smell of burning wood and the sound of music.", - "Luna, bless her heart, insisted that we take a break to join them. She had to practically drag me away from my desk to do it.", - "What can I say? She's a light in my heart." + "gui.unicopia.spellbook.chapter.ice.p5.1.body", + "gui.unicopia.spellbook.chapter.ice.p5.2.body", + "gui.unicopia.spellbook.chapter.ice.p5.3.body" ] }, { - "title": "Bonfire", + "title": "gui.unicopia.spellbook.chapter.ice.p6.title", "level": 5, "elements": [ - "We arrived at the bonfire, and of course the first thing they had was a mug of ale in my hoof. I didn't drink it, of course-alcohol is a poison to me. I'd be sick as a mule.", - "Luna enjoyed it-the bonfire, not the ale!-though. She made immediate friends with one of the town's fillies, Celly or something. They played the whole night." + "gui.unicopia.spellbook.chapter.ice.p6.1.body", + "gui.unicopia.spellbook.chapter.ice.p6.2.body" ] }, { - "title": "Bonfire II", + "title": "gui.unicopia.spellbook.chapter.ice.p7.title", "level": 6, "elements": [ - "On the way back Luna was telling me of the stories her friend told her. The town has a lot of legends, as to be expected.", - "One of them was about a scary old warlock who lived in a haunted tower at the edge of town. There's no mystery who that was about.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p7.1.body", + "gui.unicopia.spellbook.chapter.ice.p7.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, - { - "title": "", - "level": -1, - "elements": [] - }, + { }, { "title": "", "level": 6, "elements": [ - "Ice Spell II", - "Creates a cooling affect up to a radius of 3 hooves from any surfaces it touches.", - "Requires:", + "gui.unicopia.spellbook.chapter.ice.p7.3.body", + "gui.unicopia.spellbook.chapter.ice.p7.4.body", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 15, "trait": "unicopia:ice" } @@ -118,21 +114,21 @@ ] }, { - "title": "6th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p8.title", "level": 4, "elements": [ - "There was a strange noise in the village last night. Very strange. I heard a lot ponies shouting and there may have been a fire.", - "I hope everything is okay.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p8.1.body", + "gui.unicopia.spellbook.chapter.ice.p8.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Dancing Lights", + "title": "spell.unicopia.light", "level": 5, "elements": [ - "By combining a fire bolt gem with a splash of life and the chilling effect of ice I've created a spell to help with seeing in the night.", - "Dancing Lights will summon a cluster of glowing orbs to illuminate your path.", + "gui.unicopia.spellbook.chapter.ice.light.1.body", + "gui.unicopia.spellbook.chapter.ice.light.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/light.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] @@ -142,7 +138,7 @@ "level": 5, "elements": [ { "recipe": "unicopia:spells/light" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:fire_bolt" }, @@ -151,138 +147,133 @@ { "count": 30, "trait": "unicopia:ice" } ] }, - "* By adding more focus you can extend the duration of the spell" + "gui.unicopia.spellbook.chapter.ice.light.modifier.1" ] }, { - "title": "12th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p10.title", "level": 4, "elements": [ - "There were more noises last night, this time a lot closer. The town's dimeaner has also changed. A lot of the ponies I see that are normally very cheerful have become sullen.", - "Something has happened, that much is obvious, though few will tell me what.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p10.1.body", + "gui.unicopia.spellbook.chapter.ice.p10.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "15th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p11.title", "level": 4, "elements": [ - "Winter is nearly upon us now. I just saw the earliest flakes of snow outside this window as I write.", - { - "text": "The locals have begun their winter unwrapping and though the usual grumblings about frozen fields abound, I can tell there is still ", - "extra": [ - { "text": "a sense of uneasyness.", "obfuscated": true} - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p11.1.body", + "gui.unicopia.spellbook.chapter.ice.p11.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "17th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p12.title", "level": 6, "elements": [ - "It's rather surprising how quickly the weather starts to change around here. Everything in Catermoore is so very well controlled, with the spells we use to manage temperature and the pegasi assisting with the weather, we sometimes forget what wild seasons can be like.", - "But these Earth Ponies don't ave any of those luxuries. They have to deal with the weather as it comes." + "gui.unicopia.spellbook.chapter.ice.p12.1.body", + "gui.unicopia.spellbook.chapter.ice.p12.2.body" ] }, { - "title": "Frozen Lake", + "title": "gui.unicopia.spellbook.chapter.ice.p13.title", "level": 6, "elements": [ - "Just this morning I was on the way to the stall when I passed the lake at the foot of this tower's hill and saw it was nearly completely frozen over. A few colts had taken to scating across it.", - "Luna asked if she could join them, but I didn't allow it. I couldn't say why at the time, but I had a bad feeling, like something was going to happen..." + "gui.unicopia.spellbook.chapter.ice.p13.1.body", + "gui.unicopia.spellbook.chapter.ice.p13.2.body" ] }, { - "title": "17th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p14.title", "level": 19, "elements": [ - "Oh gods, of princesses. I. I don't know how to describe this. My hooves are shaking, I can barely breeze. I'm freezing cold and I can't stop thinking about what happened.", - "I knew there was something wrong. I b---knew it. I didn't know what it was at the time, but I'm extremely glad I didn't let Luna scate on that lake." + "gui.unicopia.spellbook.chapter.ice.p14.1.body", + "gui.unicopia.spellbook.chapter.ice.p14.2.body" ] }, { - "title": "Frozen Lake II", + "title": "gui.unicopia.spellbook.chapter.ice.p15.title", "level": 19, "elements": [ - "Let me take a step back a bit to describe what happened. Do you remember the frozen lake and those colts that were skating on it?", - "Well, after that I went on to the market and I was looking at the strawberries in one of the stalls--Didn't get any, sadly they were forgotten in the confusion." + "gui.unicopia.spellbook.chapter.ice.p15.1.body", + "gui.unicopia.spellbook.chapter.ice.p15.2.body" ] }, { - "title": "Frozen Lake III", + "title": "gui.unicopia.spellbook.chapter.ice.p16.title", "level": 19, "elements": [ - "Whilst I was debating the vendor about the price, I felt Luna tugging on my tunic and she asked me what was going on. I heard galloping and I saw ponies running by us. None of them stopped to explain, but I heard some very gruff words in old ponish.", - "They were heading in the direction of our tower so I set off after them." + "gui.unicopia.spellbook.chapter.ice.p16.1.body", + "gui.unicopia.spellbook.chapter.ice.p16.2.body" ] }, { "title": "", "level": 20, "elements": [ - "My immediate thoughts were 'was it a fire'? Was my lab in danger?", - "I tell you what, I wish that were the case. What I actually found was much, much, worse, and even thinking of it makes my blood run cold anew." + "gui.unicopia.spellbook.chapter.ice.p16.3.body", + "gui.unicopia.spellbook.chapter.ice.p16.4.body" ] }, { - "title": "Frozen Lake IV", + "title": "gui.unicopia.spellbook.chapter.ice.p17.title", "level": 20, "elements": [ - "When we were getting near the lake from earlier, I saw a large crowd growing along its banks. Ladders and emergency equipment were out and scattered on the shoreline and a loud uproar had erupted about what to do.", - "Getting closer, though, I realised what had happened soon enough--and I made a beeline for for the water-- The ice was broken and the lake was freezing cold." + "gui.unicopia.spellbook.chapter.ice.p17.1.body", + "gui.unicopia.spellbook.chapter.ice.p17.2.body" ] }, { "title": "", "level": 20, "elements": [ - "I jumped in anyway, pulling my saddlebags open with my magic, and grabbed the last of the gems i had with my and cast the unfinished spell it had inside.", - "The waters receded away from me as I galloped down the slop and across the drying lake bed and dove to catch the colts that had fallen in." + "gui.unicopia.spellbook.chapter.ice.p17.3.body", + "gui.unicopia.spellbook.chapter.ice.p17.4.body" ] }, { - "title": "Frozen Lake V", + "title": "gui.unicopia.spellbook.chapter.ice.p18.title", "level": 20, "elements": [ - "They were freezing and wet, even as the spell's effects worked to pull the water away from their coats, we carred them up to the shore and got them covered in blankets with hot drinks in their hooves.", - "The townponies insisted on giving me a blanket of my own, even though I hadn't - couldn't have- gotten wet." + "gui.unicopia.spellbook.chapter.ice.p18.1.body", + "gui.unicopia.spellbook.chapter.ice.p18.2.body" ] }, { "title": "", "level": 20, "elements": [ - "It was only much later, when the shock began to wear of and I was feeling my head start to pound that I remembered to cancel the spell.", - "Thank the princesses we got there in time.", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p18.3.body", + "gui.unicopia.spellbook.chapter.ice.p18.4.body", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "18th Trot '12", + "title": "gui.unicopia.spellbook.chapter.ice.p19.title", "level": 7, "elements": [ - "No sign of the colts this morning, I assume they won't be coming near this lake for a long while. The water had frozen again in the night and looked peaceful.", - "The unseasy feeling I had yesterday was gone today so I was able to relax on its banks with Luna. She didn't want to swim in this lake any more, and I don't blame her. I wouldn't either." + "gui.unicopia.spellbook.chapter.ice.p19.1.body", + "gui.unicopia.spellbook.chapter.ice.p19.2.body" ] }, { - "title": "Sandcastle", + "title": "gui.unicopia.spellbook.chapter.ice.p20.title", "level": 7, "elements": [ - "Luna started a sand castle, and whilst she was busy with that I decided to sketch out the details of my new spell.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.ice.p20.1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Water Repulsion", + "title": "spell.unicopia.hydrophobic", "level": 20, "elements": [ - "By combining the abilities of a shield spell with that of the frost gem, the results are... Admittedly strange.", - "I'm calling this water repulsion because it does just that: It pushes water away from the caster.", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.1.body", + "gui.unicopia.spellbook.chapter.ice.hydrophobic.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] @@ -292,7 +283,7 @@ "level": 20, "elements": [ { "recipe": "unicopia:spells/hydrophobic" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:frost" }, @@ -302,8 +293,8 @@ }, { "ingredients": [ - { "text": "* By adding more focus you can extend the duration of the spell" }, - { "text": "* Add the generosity trait to tie this spell to a location rather than a user" } + { "text": "gui.unicopia.spellbook.chapter.ice.hydrophobic.modifier.1" }, + { "text": "gui.unicopia.spellbook.chapter.ice.hydrophobic.modifier.2" } ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/introduction.json b/src/main/resources/data/unicopia/spellbook/chapters/introduction.json index d34b424d..fe32be55 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/introduction.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/introduction.json @@ -5,112 +5,88 @@ "content": { "pages": [ { - "title": "Preface", + "title": "gui.unicopia.spellbook.chapter.introduction.p1.title", "level": 0, "elements": [ - { - "text": "To whomever holds this tome, beware what you seek for you might not like what you find.", - "extra": [ - { "text": "Hither yonder equs.", "obfuscated": true } - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.introduction.p1.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Ch.1 Magic in Equestria", + "title": "gui.unicopia.spellbook.chapter.introduction.p2.title", "level": 0, "elements": [ - { - "text": "Equestria is filled with magic of all different shapes and forms. Following recent events, however, it's has become plainly obvious that we do not fully understand all that there is about the world of Equestria. That is why the crown has tasked me with researching Magic in all of its forms, so we might utilise it and, I hope, save ourselves from the", - "extra": [ - { "text": "aaaaaaa", "obfuscated": "true" } - ] - }, + "gui.unicopia.spellbook.chapter.introduction.p2.body", { "x": 125, "y": 0, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/profile.png" } ] }, { - "title": "1st Mare '12", + "title": "gui.unicopia.spellbook.chapter.introduction.p3.title", "level": 0, "elements": [ - "Odd Rocks", - "These 'Gemstones' as the locals call them are a common material found around the world. Farm-Ponies dig them up all the time and consider it a local delicacy, but I believe these stones are capable of a lot more than they let on.", + "gui.unicopia.spellbook.chapter.introduction.p3.1.body", + "gui.unicopia.spellbook.chapter.introduction.p3.2.body", { "x": 125, "y": 0, "width": 32, "height": 32, "texture": "unicopia:textures/item/gemstone.png" } ] }, { - "title": "Gemstones", + "title": "gui.unicopia.spellbook.chapter.introduction.p4.title", "level": 0, "elements": [ - { - "text": "My research is still incomplete but I may have stumbled upon something. These stones have high magical potentia! More than I've ever seen before!" - }, - { - "text": "Luna-wants", - "strikethrough": true, - "extra": [ - { "text": "I'm going to keep experimenting. Hooves-crossed, I'll update you tomorrow if I find anything.", "strikethrough": false} - ] - }, - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.introduction.p4.1.body", + "gui.unicopia.spellbook.chapter.introduction.p4.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "2nd Mare '12", + "title": "gui.unicopia.spellbook.chapter.introduction.p5.title", "level": 0, "elements": [ - { - "text": "It worked! Holy", - "extra": [ - { "text": "Celestia's ass-cheeks'", "obfuscated": true}, - { "text": "it actually worked." } - ] - }, - "This is amazing! These can do so much more than I could have ever imagined. Think of the advances I could bring to Equestria. Gem-powered lighting, heating, cooling, I'd no longer have to spend summer sitting on this-", - "I'm getting ahead of myself. Let me explain..." + "gui.unicopia.spellbook.chapter.introduction.p5.1.body", + "gui.unicopia.spellbook.chapter.introduction.p5.2.body", + "gui.unicopia.spellbook.chapter.introduction.p5.3.body" ] }, { - "title": "Spellcrafting", + "title": "gui.unicopia.spellbook.chapter.introduction.p6.title", "level": 0, "elements": [ { "x": 125, "y": 0, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/crafting.png" }, - "I drew a guide at the start of this book to help with the placement.", - "Put a raw gem-it mustn't have any spells already-in the middle and place materials around it in the slots I marked.", - "Each material gives different effects and putting them closer enhances their influence on the gem." + "gui.unicopia.spellbook.chapter.introduction.p6.1.body", + "gui.unicopia.spellbook.chapter.introduction.p6.2.body", + "gui.unicopia.spellbook.chapter.introduction.p6.3.body" ] }, { - "title": "3rd Mare '12", + "title": "gui.unicopia.spellbook.chapter.introduction.p7.title", "level": 0, "elements": [ - "I'm going to start documenting spell combinations as I find them. Some of them are pretty obvious, like gem + fire = fire gem", - "But some are less clear. For instance, what traits would an egg add? Much experimenting is needed. Oh, I'm giddy with excitement!", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.introduction.p7.1.body", + "gui.unicopia.spellbook.chapter.introduction.p7.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] }, { - "title": "Botched Gems", + "title": "gui.unicopia.spellbook.chapter.introduction.p8.title", "level": 1, "elements": [ { "x": 127, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/item/botched_gem.png" }, - "Not every combination works. What's dissapointing is now I have all these useless stones piling up in my chambers.", - "I don't know what to do with them. They're not edible. At least the locals don't think so.", - "They do still have the traits I gave them, so maybe I can find a use other than building a rock-fort with little Luna..." + "gui.unicopia.spellbook.chapter.introduction.p8.1.body", + "gui.unicopia.spellbook.chapter.introduction.p8.2.body", + "gui.unicopia.spellbook.chapter.introduction.p8.3.body" ] }, { - "title": "13th Mare '12", + "title": "gui.unicopia.spellbook.chapter.introduction.p9.title", "level": 0, "elements": [ - "Sorry for the long delay in updates. I've been hard at work researching different spells and desciding my approach.", - "Fire is becomg a very interesting aspect, what with traits for it being readily available.", - "At the princess' behest", - "- Starswirl the Bearded" + "gui.unicopia.spellbook.chapter.introduction.p9.1.body", + "gui.unicopia.spellbook.chapter.introduction.p9.2.body", + "gui.unicopia.spellbook.author1.sign_off", + "gui.unicopia.spellbook.author1.name" ] } ] diff --git a/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json b/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json index aee0fb23..0a046cbe 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json @@ -5,26 +5,26 @@ "content": { "pages": [ { - "title": "Ch.6 The Beyond", + "title": "gui.unicopia.spellbook.chapter.otherworldly.p1.title", "level": 10, "elements": [ { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/the_otherworldly.png" } ] }, { - "title": "2nd Croptober '12", + "title": "gui.unicopia.spellbook.chapter.otherworldly.p2.title", "level": 20, "elements": [ - "This chapter serves as an exploration of the worlds beyond our own and a delving into what most unicorns would normally shy away from.", - "In this section can be found the most powerful of the powerful spells, but also the most danger. Read further at your own peril, as this is not for the light of mind.", - "- Lord Sombra" + "gui.unicopia.spellbook.chapter.otherworldly.p2.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.p2.2.body", + "gui.unicopia.spellbook.author2.name" ] }, { - "title": "Life Sapping", + "title": "spell.unicopia.siphoning", "level": 20, "elements": [ - "A simple spell that siphons life force out of a living entity and uses it to revitalise the caster.", + "gui.unicopia.spellbook.chapter.otherworldly.siphoning.1.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } ] @@ -34,7 +34,7 @@ "level": 20, "elements": [ { "recipe": "unicopia:spells/siphoning" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:infernal" }, @@ -45,16 +45,11 @@ ] }, { - "title": "Necromancy", + "title": "spell.unicopia.necromancy", "level": 21, "elements": [ - "This area effect spell taps into the great beyond to summon life to serve its master.", - { - "text": "This spell is not very useful when used on its own, but combined with other traits may become a powerful tool against the", - "extra": [ - { "text": "Changeling Swarms.", "obfuscated": true } - ] - }, + "gui.unicopia.spellbook.chapter.otherworldly.necromancy.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.necromancy.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } ] @@ -64,7 +59,7 @@ "level": 21, "elements": [ { "recipe": "unicopia:spells/necromancy" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:siphoning" }, @@ -79,11 +74,11 @@ ] }, { - "title": "Dark Vortex", + "title": "spell.unicopia.dark_vortex", "level": 23, "elements": [ - "Dipping below the fabric of reality, this spell taps into the deep arcane powers of the beyond to punch a hole in reality.", - "The resulting hole is a hungry mass that consumes all who approach. It grants massive energy, but feed it at your peril.", + "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/chaos.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] @@ -93,7 +88,7 @@ "level": 23, "elements": [ { "recipe": "unicopia:spells/dark_vortex" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:vortex" }, @@ -106,11 +101,11 @@ ] }, { - "title": "Arcane Rift", + "title": "spell.unicopia.portal", "level": 24, "elements": [ - "Combining the effects of the displacement spell created by my predecessor and the dark vortex gem, one is able to tame its chaotic nature.", - "The arcane rift spell allows the caster to link two locations together to create a bridge across the ether. Anything that enters one end will appear at the other maintaining its velocity.", + "gui.unicopia.spellbook.chapter.otherworldly.portal.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.portal.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] @@ -120,7 +115,7 @@ "level": 24, "elements": [ { "recipe": "unicopia:spells/portal" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "item": "unicopia:gemstone" }, @@ -133,11 +128,11 @@ ] }, { - "title": "Mind Swap", + "title": "spell.unicopia.mind_swap", "level": 27, "elements": [ - "Continuing my predecessor's research into the abilities of the Changeling Spawn, I have enhanced his mimic spell by adding a chaotic twist.", - "Mind Swap extends the effects of mimic to cover two individuals, in effect swapping their bodies for a limited time.", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.1.body", + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.2.body", { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] @@ -147,7 +142,7 @@ "level": 28, "elements": [ { "recipe": "unicopia:spells/mind_swap" }, - "Requires:", + "gui.unicopia.spellbook.recipe.requires", { "ingredients": [ { "count": 1, "spell": "unicopia:mimic" }, @@ -156,7 +151,7 @@ { "count": 40, "trait": "unicopia:chaos" } ] }, - "* Add the focus trait to increase the effect's duration" + "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.3.body" ] } ] From dc6bc9fe9e20d2f17b91f32fd596137553b011dd Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 31 Jan 2024 14:04:29 +0000 Subject: [PATCH 85/98] Add a dust cloud when earth ponies stomp sand, gravel, and concrete powder --- .../com/minelittlepony/unicopia/UTags.java | 1 + .../ability/EarthPonyStompAbility.java | 15 +++- .../unicopia/client/URenderers.java | 2 + .../AbstractGeometryBasedParticle.java | 12 ++- .../client/particle/DustCloudParticle.java | 86 +++++++++++++++++++ .../unicopia/particle/UParticles.java | 2 + .../data/c/tags/blocks/concrete_powders.json | 21 +++++ .../unicopia/tags/blocks/kicks_up_dust.json | 9 ++ 8 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java create mode 100644 src/main/resources/data/c/tags/blocks/concrete_powders.json create mode 100644 src/main/resources/data/unicopia/tags/blocks/kicks_up_dust.json diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java index e8d0b5d7..08da479c 100644 --- a/src/main/java/com/minelittlepony/unicopia/UTags.java +++ b/src/main/java/com/minelittlepony/unicopia/UTags.java @@ -44,6 +44,7 @@ public interface UTags { TagKey CRYSTAL_HEART_BASE = block("crystal_heart_base"); TagKey CRYSTAL_HEART_ORNAMENT = block("crystal_heart_ornament"); TagKey UNAFFECTED_BY_GROW_ABILITY = block("unaffected_by_grow_ability"); + TagKey KICKS_UP_DUST = block("kicks_up_dust"); TagKey POLEARM_MINEABLE = block("mineable/polearm"); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index 1a5c72ef..e81f5a0d 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.AwaitTickQueue; import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.entity.Living; @@ -17,6 +18,7 @@ import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.server.world.BlockDestructionManager; import com.minelittlepony.unicopia.util.PosHelper; +import com.minelittlepony.unicopia.util.VecHelper; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -26,6 +28,7 @@ import net.minecraft.entity.LivingEntity; import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.particle.BlockStateParticleEffect; import net.minecraft.registry.tag.BlockTags; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Identifier; @@ -33,6 +36,7 @@ import net.minecraft.util.math.BlockBox; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraft.world.WorldEvents; @@ -156,7 +160,10 @@ public class EarthPonyStompAbility implements Ability { spawnEffectAround(player, center, radius, rad); ParticleUtils.spawnParticle(player.getWorld(), UParticles.GROUND_POUND, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0); - ParticleUtils.spawnParticle(player.getWorld(), UParticles.SHOCKWAVE, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0); + BlockState steppingState = player.getSteppingBlockState(); + if (steppingState.isIn(UTags.KICKS_UP_DUST)) { + ParticleUtils.spawnParticle(player.getWorld(), new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), player.getBlockPos().down().toCenterPos(), Vec3d.ZERO); + } iplayer.subtractEnergyCost(rad); iplayer.asEntity().addExhaustion(3); @@ -218,6 +225,12 @@ public class EarthPonyStompAbility implements Ability { } else { w.syncWorldEvent(WorldEvents.BLOCK_BROKEN, pos, Block.getRawIdFromState(state)); } + + if (state.isIn(UTags.KICKS_UP_DUST)) { + if (w.random.nextInt(4) == 0 && w.isAir(pos.up()) && w.getFluidState(pos.up()).isEmpty()) { + ParticleUtils.spawnParticle(w, new BlockStateParticleEffect(UParticles.DUST_CLOUD, state), pos.up().toCenterPos(), VecHelper.supply(() -> w.random.nextTriangular(0, 0.1F))); + } + } } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index ebe72254..0d4509bb 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -10,6 +10,7 @@ import com.minelittlepony.unicopia.block.cloud.CloudChestBlock; import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle; import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle; import com.minelittlepony.unicopia.client.particle.DiskParticle; +import com.minelittlepony.unicopia.client.particle.DustCloudParticle; import com.minelittlepony.unicopia.client.particle.FloatingBubbleParticle; import com.minelittlepony.unicopia.client.particle.GroundPoundParticle; import com.minelittlepony.unicopia.client.particle.HealthDrainParticle; @@ -80,6 +81,7 @@ public interface URenderers { ParticleFactoryRegistry.getInstance().register(UParticles.GROUND_POUND, GroundPoundParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.CLOUDS_ESCAPING, CloudsEscapingParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.LIGHTNING_BOLT, LightningBoltParticle::new); + ParticleFactoryRegistry.getInstance().register(UParticles.DUST_CLOUD, DustCloudParticle::new); AccessoryFeatureRenderer.register( BraceletFeatureRenderer::new, AmuletFeatureRenderer::new, GlassesFeatureRenderer::new, diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java index 1714d93a..cb5a2c51 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/AbstractGeometryBasedParticle.java @@ -11,6 +11,7 @@ import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; public abstract class AbstractGeometryBasedParticle extends Particle { @@ -38,6 +39,16 @@ public abstract class AbstractGeometryBasedParticle extends Particle { te.draw(); } + protected final void renderQuad(MatrixStack matrices, Tessellator te, BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta) { + int light = getBrightness(tickDelta); + buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); + for (RenderUtil.Vertex corner : corners) { + var position = corner.position(matrices.peek().getPositionMatrix()); + buffer.vertex(position.x, position.y, position.z).texture(corner.texture().x, corner.texture().y).color(red, green, blue, alpha).light(light).next(); + } + te.draw(); + } + protected final void renderQuad(Tessellator te, BufferBuilder buffer, RenderUtil.Vertex[] corners, float alpha, float tickDelta) { int light = getBrightness(tickDelta); buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); @@ -47,7 +58,6 @@ public abstract class AbstractGeometryBasedParticle extends Particle { te.draw(); } - protected final void renderQuad(VertexConsumer buffer, Vector3f[] corners, float alpha, float tickDelta) { int light = getBrightness(tickDelta); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java new file mode 100644 index 00000000..e39bb0b0 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/DustCloudParticle.java @@ -0,0 +1,86 @@ +package com.minelittlepony.unicopia.client.particle; + +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.client.render.RenderUtil; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.particle.BlockStateParticleEffect; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RotationAxis; + +public class DustCloudParticle extends AbstractBillboardParticle { + //private static final Identifier TEXTURE = new Identifier("textures/particle/big_smoke_3.png"); + + protected static final int SEGMENTS = 20; + protected static final int SEPARATION = 270 / SEGMENTS; + + private float scaleFactor; + + protected Sprite sprite; + private final RenderUtil.Vertex[] vertices; + + public DustCloudParticle(BlockStateParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { + super(world, x, y, z, velocityX, velocityY, velocityZ); + maxAge = 1000; + gravityStrength = 1; + red = 0.6F; + green = 0.6F; + blue = 0.6F; + alpha = (float)world.getRandom().nextTriangular(0.6, 0.2); + scaleFactor = (float)world.getRandom().nextTriangular(2, 1.2); + sprite = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(effect.getBlockState()); + vertices = new RenderUtil.Vertex[]{ + new RenderUtil.Vertex(-1, -1, 0, sprite.getMinU(), sprite.getMinV()), + new RenderUtil.Vertex(-1, 1, 0, sprite.getMaxU(), sprite.getMinV()), + new RenderUtil.Vertex( 1, 1, 0, sprite.getMaxU(), sprite.getMaxV()), + new RenderUtil.Vertex( 1, -1, 0, sprite.getMinU(), sprite.getMaxV()) + }; + if (!effect.getBlockState().isOf(Blocks.GRASS_BLOCK)) { + int i = MinecraftClient.getInstance().getBlockColors().getColor(effect.getBlockState(), world, BlockPos.ofFloored(x, y, z), 0); + red *= Color.r(i); + green *= Color.g(i); + blue *= Color.b(i); + } + } + + @Override + protected Identifier getTexture() { + return sprite.getAtlasId(); + } + + @Override + public void tick() { + super.tick(); + scaleFactor += 0.001F; + scale(MathHelper.clamp(age / 5F, 0, 1) * scaleFactor); + } + + @Override + protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) { + float scale = getScale(tickDelta); + float alpha = this.alpha * (1 - ((float)age / maxAge)); + MatrixStack matrices = new MatrixStack(); + matrices.translate(x, y, z); + matrices.scale(scale, scale * 0.5F, scale); + + float angle = ((this.age + tickDelta) % 360) / SEGMENTS; + + for (int i = 0; i < SEGMENTS; i++) { + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees((i * angle) % 360)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees((SEPARATION * i + angle) % 360)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((SEPARATION * i + angle) % 360)); + float ringScale = 1 + MathHelper.sin(((i * 10) + age + tickDelta) * 0.05F) * 0.1F; + matrices.scale(ringScale, ringScale, ringScale); + renderQuad(matrices, te, buffer, vertices, alpha, tickDelta); + matrices.pop(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java index 17554070..9bec2e5c 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.particle; import com.minelittlepony.unicopia.Unicopia; import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes; +import net.minecraft.particle.BlockStateParticleEffect; import net.minecraft.particle.DefaultParticleType; import net.minecraft.particle.ParticleType; import net.minecraft.registry.Registry; @@ -13,6 +14,7 @@ public interface UParticles { ParticleType UNICORN_MAGIC = register("unicorn_magic", FabricParticleTypes.complex(MagicParticleEffect.FACTORY)); DefaultParticleType CHANGELING_MAGIC = register("changeling_magic", FabricParticleTypes.simple()); DefaultParticleType BUBBLE = register("bubble", FabricParticleTypes.simple()); + ParticleType DUST_CLOUD = register("dust_cloud", FabricParticleTypes.complex(BlockStateParticleEffect.PARAMETERS_FACTORY)); ParticleType RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); ParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY)); diff --git a/src/main/resources/data/c/tags/blocks/concrete_powders.json b/src/main/resources/data/c/tags/blocks/concrete_powders.json new file mode 100644 index 00000000..e3142856 --- /dev/null +++ b/src/main/resources/data/c/tags/blocks/concrete_powders.json @@ -0,0 +1,21 @@ +{ + "replace": false, + "values": [ + "minecraft:white_concrete_powder", + "minecraft:orange_concrete_powder", + "minecraft:magenta_concrete_powder", + "minecraft:light_blue_concrete_powder", + "minecraft:yellow_concrete_powder", + "minecraft:lime_concrete_powder", + "minecraft:pink_concrete_powder", + "minecraft:gray_concrete_powder", + "minecraft:light_gray_concrete_powder", + "minecraft:cyan_concrete_powder", + "minecraft:purple_concrete_powder", + "minecraft:blue_concrete_powder", + "minecraft:brown_concrete_powder", + "minecraft:green_concrete_powder", + "minecraft:red_concrete_powder", + "minecraft:black_concrete_powder" + ] +} diff --git a/src/main/resources/data/unicopia/tags/blocks/kicks_up_dust.json b/src/main/resources/data/unicopia/tags/blocks/kicks_up_dust.json new file mode 100644 index 00000000..5a3a6783 --- /dev/null +++ b/src/main/resources/data/unicopia/tags/blocks/kicks_up_dust.json @@ -0,0 +1,9 @@ +{ + "replace": false, + "values": [ + "#minecraft:sand", + "minecraft:gravel", + "minecraft:suspicious_gravel", + "#c:concrete_powders" + ] +} From a36aa10085b24cd942415a129eb340dd83f6450c Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 31 Jan 2024 14:05:14 +0000 Subject: [PATCH 86/98] Add the dust cloud when pegasi take off from sand, gravel, and concrete power + fix dust particles flying away too quickly from where a pegasus takes off --- .../unicopia/entity/player/PlayerPhysics.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index bb9b68e9..e1a6cd75 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -40,6 +40,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; +import net.minecraft.particle.BlockStateParticleEffect; import net.minecraft.particle.ParticleTypes; import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.registry.RegistryKeys; @@ -564,10 +565,14 @@ public class PlayerPhysics extends EntityPhysics implements Tickab entity.calculateDimensions(); if (entity.isOnGround() || !force) { - Supplier pos = VecHelper.sphere(pony.asWorld().getRandom(), 0.5D); - Supplier vel = VecHelper.sphere(pony.asWorld().getRandom(), 0.15D); - pony.spawnParticles(ParticleTypes.CAMPFIRE_COSY_SMOKE, pos, vel, 5); - pony.spawnParticles(ParticleTypes.CLOUD, pos, vel, 5); + BlockState steppingState = pony.asEntity().getSteppingBlockState(); + if (steppingState.isIn(UTags.KICKS_UP_DUST)) { + pony.addParticle(new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), pony.getOrigin().down().toCenterPos(), Vec3d.ZERO); + } else { + Supplier pos = VecHelper.sphere(pony.asWorld().getRandom(), 0.5D); + Supplier vel = VecHelper.sphere(pony.asWorld().getRandom(), 0.015D); + pony.spawnParticles(ParticleTypes.CLOUD, pos, vel, 5); + } } } From e85bb4b3a5f20028188f2c0d298a513fcabfd757 Mon Sep 17 00:00:00 2001 From: Sollace Date: Wed, 31 Jan 2024 17:00:47 +0000 Subject: [PATCH 87/98] Fix formatting being lost when wrapping text - move page headers outside the scrollable area, - fix text cut off at the bottom of the scroll region --- .../client/gui/ParagraphWrappingVisitor.java | 47 ++++++++++++++++--- .../gui/spellbook/element/DynamicContent.java | 28 +++++++---- .../client/gui/spellbook/element/Panel.java | 22 +++++++-- .../client/gui/spellbook/element/Recipe.java | 9 +--- .../client/gui/spellbook/element/Stack.java | 10 +--- .../spellbook/chapters/air_magic.json | 18 +++---- .../spellbook/chapters/crystal_heart.json | 2 +- .../spellbook/chapters/dark_magic.json | 30 ++++++------ .../spellbook/chapters/fire_magic.json | 24 +++++----- .../spellbook/chapters/ice_magic.json | 22 ++++----- .../spellbook/chapters/introduction.json | 2 +- .../spellbook/chapters/the_otherworldly.json | 22 ++++----- 12 files changed, 141 insertions(+), 95 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/ParagraphWrappingVisitor.java b/src/main/java/com/minelittlepony/unicopia/client/gui/ParagraphWrappingVisitor.java index 0a947e9c..66f38d23 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/ParagraphWrappingVisitor.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/ParagraphWrappingVisitor.java @@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.client.gui; import java.util.*; import java.util.function.BiConsumer; - import it.unimi.dsi.fastutil.ints.Int2IntFunction; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextHandler; @@ -31,12 +30,18 @@ public class ParagraphWrappingVisitor implements StyledVisitor { } @Override - public Optional accept(Style style, String s) { + public Optional accept(Style initialStyle, String s) { + StyleBreakingVisitor visitor = new StyleBreakingVisitor(initialStyle, this::acceptFragment); + TextVisitFactory.visitFormatted(s, 0, initialStyle, initialStyle, visitor); + visitor.flush(); + return Optional.empty(); + } + private Optional acceptFragment(Style inlineStyle, String s) { int remainingLength = (int)(pageWidth - currentLineCollectedLength); while (!s.isEmpty()) { - int trimmedLength = handler.getTrimmedLength(s, remainingLength, style); + int trimmedLength = handler.getTrimmedLength(s, remainingLength, inlineStyle); int newline = s.indexOf('\n'); if (newline >= 0 && newline < trimmedLength) { @@ -57,7 +62,7 @@ public class ParagraphWrappingVisitor implements StyledVisitor { trimmedLength = lastSpace > 0 ? Math.min(lastSpace, trimmedLength) : trimmedLength; } - Text fragment = Text.literal(s.substring(0, trimmedLength).trim()).setStyle(style); + Text fragment = Text.literal(s.substring(0, trimmedLength).trim()).setStyle(inlineStyle); float grabbedWidth = handler.getWidth(fragment); // advance if appending the next segment would cause an overflow @@ -101,14 +106,42 @@ public class ParagraphWrappingVisitor implements StyledVisitor { } public void advance() { + line++; if (progressedNonEmpty || currentLineCollectedLength > 0) { progressedNonEmpty = true; - lineConsumer.accept(currentLine, (++line) * font.fontHeight); + lineConsumer.accept(currentLine, line * font.fontHeight); } - pageWidth = widthSupplier.applyAsInt((++line) * font.fontHeight); + pageWidth = widthSupplier.applyAsInt(line * font.fontHeight); currentLine = Text.empty(); currentLineCollectedLength = 0; } - record StyledString (String string, Style style) {} + static final class StyleBreakingVisitor implements CharacterVisitor { + private final StyledVisitor fragmentVisitor; + private final StringBuilder collectedText = new StringBuilder(); + + private Style currentStyle; + + public StyleBreakingVisitor(Style initialStyle, StyledVisitor fragmentVisitor) { + this.currentStyle = initialStyle; + this.fragmentVisitor = fragmentVisitor; + } + + @Override + public boolean accept(int index, Style style, int codepoint) { + if (!style.equals(currentStyle)) { + flush(); + } + currentStyle = style; + collectedText.append((char)codepoint); + return true; + } + + public void flush() { + if (collectedText.length() > 0) { + fragmentVisitor.accept(currentStyle, collectedText.toString()); + collectedText.setLength(0); + } + } + } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java index c8a64c74..e9dc8bab 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java @@ -139,6 +139,19 @@ public class DynamicContent implements Content { compiled = false; } + public void drawHeader(DrawContext context, int mouseX, int mouseY) { + + if (elements.isEmpty()) { + return; + } + boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level; + int x = bounds.left; + int y = bounds.top - 16; + + DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, x, y, 1.3F, headerColor); + DrawableUtil.drawScaledText(context, Text.translatable("gui.unicopia.spellbook.page.level_requirement", level < 0 ? "???" : "" + (level + 1)).formatted(Formatting.DARK_GREEN), x, y + 12, 0.8F, headerColor); + } + @Override public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) { @@ -149,25 +162,24 @@ public class DynamicContent implements Content { if (!compiled) { compiled = true; int relativeY = 0; + int textHeight = 0; for (PageElement element : elements.stream().filter(PageElement::isInline).toList()) { element.compile(relativeY, container); relativeY += element.bounds().height; + if (element instanceof TextBlock) { + textHeight += element.bounds().height; + } } - bounds.height = relativeY; + bounds.height = textHeight == 0 ? 0 : relativeY; } - boolean needsMoreXp = level < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < level; - MatrixStack matrices = context.getMatrices(); - DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, bounds.left, bounds.top - 10, 1.3F, headerColor); - DrawableUtil.drawScaledText(context, Text.translatable("gui.unicopia.spellbook.page.level_requirement", level < 0 ? "???" : "" + (level + 1)).formatted(Formatting.DARK_GREEN), bounds.left, bounds.top - 10 + 12, 0.8F, headerColor); matrices.push(); - matrices.translate(0, 16, 0); + matrices.translate(0, -8, 0); elements.stream().filter(PageElement::isFloating).forEach(element -> { - Bounds bounds = element.bounds(); matrices.push(); - bounds.translate(matrices); + element.bounds().translate(matrices); element.draw(context, mouseX, mouseY, container); matrices.pop(); }); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java index fc84e687..315e1f7b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Panel.java @@ -24,15 +24,16 @@ class Panel extends ScrollContainer { getContentPadding().top = 15; page = content.getPage(pageIndex); - margin.left = screen.getX() + 30; - margin.top = screen.getY() + 15; - margin.right = screen.width - screen.getBackgroundWidth() - screen.getX() + 20; + int width = screen.getBackgroundWidth() / 2; + margin.top = screen.getY() + 35; margin.bottom = screen.height - screen.getBackgroundHeight() - screen.getY() + 40; + margin.left = screen.getX() + 30; if (pageIndex % 2 == 1) { - margin.left += screen.getBackgroundWidth() / 2 - 10; + margin.left += screen.getBackgroundWidth() / 2 - 20; + margin.right = screen.getX() + 20; } else { - margin.right += screen.getBackgroundWidth() / 2; + margin.right = screen.width - width - margin.left + 30; } init(() -> {}); screen.addDrawable(this); @@ -63,4 +64,15 @@ class Panel extends ScrollContainer { @Override protected void drawDecorations(DrawContext context, int mouseX, int mouseY, float partialTicks) { } + + @Override + protected void drawOverlays(DrawContext context, int mouseX, int mouseY, float partialTicks) { + context.getMatrices().push(); + this.getBounds().translate(context.getMatrices()); + page.ifPresent(p -> { + p.drawHeader(context, mouseX, mouseY); + }); + context.getMatrices().pop(); + super.drawOverlays(context, mouseX, mouseY, partialTicks); + } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java index 79264fa3..4d6439a3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Recipe.java @@ -4,7 +4,6 @@ import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe; import com.minelittlepony.unicopia.client.gui.spellbook.IngredientTree; -import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.client.MinecraftClient; @@ -13,18 +12,14 @@ import net.minecraft.util.Identifier; record Recipe (DynamicContent.Page page, Identifier id, Bounds bounds) implements PageElement { @Override public void compile(int y, IViewRoot container) { - if (container instanceof SpellbookScreen book) { - bounds().left = book.getX(); - bounds().top = book.getY(); - } MinecraftClient.getInstance().world.getRecipeManager().get(id).ifPresent(recipe -> { if (recipe instanceof SpellbookRecipe spellRecipe) { boolean needsMoreXp = page.getLevel() < 0 || Pony.of(MinecraftClient.getInstance().player).getLevel().get() < page.getLevel(); IngredientTree tree = new IngredientTree( - bounds().left + page().getBounds().left, - bounds().top + page().getBounds().top + y + 10, + bounds().left, + bounds().top + y - 10, page().getBounds().width - 20 ).obfuscateResult(needsMoreXp); spellRecipe.buildCraftingTree(tree); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Stack.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Stack.java index 1113d668..f1f8195d 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Stack.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Stack.java @@ -4,19 +4,13 @@ import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.ability.magic.spell.crafting.IngredientWithSpell; import com.minelittlepony.unicopia.client.gui.spellbook.IngredientTree; -import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; record Stack (DynamicContent.Page page, IngredientWithSpell ingredient, Bounds bounds) implements PageElement { @Override public void compile(int y, IViewRoot container) { - int xx = 0, yy = 0; - if (container instanceof SpellbookScreen book) { - xx = book.getX(); - yy = book.getY(); - } IngredientTree tree = new IngredientTree( - bounds().left + xx + page().getBounds().left, - bounds().top + yy + page().getBounds().top + y + 10, + bounds().left + page().getBounds().left, + bounds().top + page().getBounds().top + y - 10, 30 ); tree.input(ingredient.getMatchingStacks()); diff --git a/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json index bdf768bc..d552314e 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/air_magic.json @@ -8,7 +8,7 @@ "title": "gui.unicopia.spellbook.chapter.air.p1.title", "level": 1, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/air_magic.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/air_magic.png" } ] }, { @@ -45,8 +45,8 @@ "gui.unicopia.spellbook.chapter.air.p4.1.body", "gui.unicopia.spellbook.chapter.air.p4.2.body", "gui.unicopia.spellbook.chapter.air.p4.3.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -65,8 +65,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.air.catapult.1.body", "gui.unicopia.spellbook.chapter.air.catapult.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -91,8 +91,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.air.bubble.1.body", "gui.unicopia.spellbook.chapter.air.bubble.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/water.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/water.png" } ] }, { @@ -149,8 +149,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.air.feather_fall.1.body", "gui.unicopia.spellbook.chapter.air.feather_fall.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { diff --git a/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json b/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json index 795e6f83..d16b5763 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json @@ -8,7 +8,7 @@ "title": "gui.unicopia.spellbook.chapter.artefacts.p1.title", "level": 0, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/crystal_heart.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/crystal_heart.png" } ] }, { diff --git a/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json index ded71e87..ff69df04 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json @@ -8,7 +8,7 @@ "title": "gui.unicopia.spellbook.chapter.dark_magic.p1.title", "level": 10, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" } ] }, { @@ -43,8 +43,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.dark_magic.vortex.1.body", "gui.unicopia.spellbook.chapter.dark_magic.vortex.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/knowledge.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/air.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/knowledge.png" } ] }, { @@ -150,8 +150,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.dark_magic.transformation.1.body", "gui.unicopia.spellbook.chapter.dark_magic.transformation.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/life.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/life.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -175,8 +175,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.dark_magic.reveal.1.body", "gui.unicopia.spellbook.chapter.dark_magic.reveal.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -247,8 +247,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.1.body", "gui.unicopia.spellbook.chapter.dark_magic.arcane_protection.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] }, { @@ -273,8 +273,8 @@ "level": 17, "elements": [ "gui.unicopia.spellbook.chapter.dark_magic.displacement.1.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -311,8 +311,8 @@ "level": 17, "elements": [ "gui.unicopia.spellbook.chapter.dark_magic.mimic.1.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] }, { @@ -373,8 +373,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.dark_magic.p28.1.body", "gui.unicopia.spellbook.author1.name", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/kindness.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/kindness.png" } ] }, { diff --git a/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json index 9657c486..491cb6de 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/fire_magic.json @@ -8,7 +8,7 @@ "title": "gui.unicopia.spellbook.chapter.fire.p1.title", "level": 0, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/fire_magic.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/fire_magic.png" } ] }, { @@ -27,8 +27,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.fire.scorch.1.body", "gui.unicopia.spellbook.chapter.fire.scorch.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } ] }, { @@ -50,8 +50,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.fire.flame.1.body", "gui.unicopia.spellbook.chapter.fire.flame.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/fire.png" } ] }, { @@ -85,8 +85,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.fire.p6.1.body", "gui.unicopia.spellbook.chapter.fire.p6.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } ] }, { @@ -114,8 +114,8 @@ "gui.unicopia.spellbook.chapter.fire.fire_bolt.1.body", "gui.unicopia.spellbook.chapter.fire.fire_bolt.2.body", "gui.unicopia.spellbook.chapter.fire.fire_bolt.3.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/focus.png" } ] }, { @@ -140,8 +140,8 @@ "gui.unicopia.spellbook.chapter.fire.p10.1.body", "gui.unicopia.spellbook.chapter.fire.p10.2.body", "gui.unicopia.spellbook.chapter.fire.p10.3.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/strength.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/fire.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/strength.png" } ] }, { @@ -208,7 +208,7 @@ "elements": [ "gui.unicopia.spellbook.chapter.fire.shield.1.body", "gui.unicopia.spellbook.chapter.fire.shield.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" } ] }, { diff --git a/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json index 523f318c..fcf6fa57 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/ice_magic.json @@ -8,7 +8,7 @@ "title": "gui.unicopia.spellbook.chapter.ice.p1.title", "level": 0, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/ice_magic.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/ice_magic.png" } ] }, { @@ -27,8 +27,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.ice.frost.1.body", "gui.unicopia.spellbook.chapter.ice.frost.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { @@ -50,8 +50,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.ice.p4.1.body", "gui.unicopia.spellbook.chapter.ice.p4.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/oak_boat.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/oak_boat.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { @@ -109,8 +109,8 @@ { "count": 15, "trait": "unicopia:ice" } ] }, - { "x": 115, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, - { "x": 115, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + { "x": 115, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/snowball.png" }, + { "x": 115, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { @@ -129,8 +129,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.ice.light.1.body", "gui.unicopia.spellbook.chapter.ice.light.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "minecraft:textures/item/light.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "minecraft:textures/item/light.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { @@ -274,8 +274,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.ice.hydrophobic.1.body", "gui.unicopia.spellbook.chapter.ice.hydrophobic.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/strength.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/ice.png" } ] }, { diff --git a/src/main/resources/data/unicopia/spellbook/chapters/introduction.json b/src/main/resources/data/unicopia/spellbook/chapters/introduction.json index fe32be55..b2cca4f9 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/introduction.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/introduction.json @@ -73,7 +73,7 @@ "title": "gui.unicopia.spellbook.chapter.introduction.p8.title", "level": 1, "elements": [ - { "x": 127, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/item/botched_gem.png" }, + { "x": 127, "y": 0, "width": 32, "height": 32, "texture": "unicopia:textures/item/botched_gem.png" }, "gui.unicopia.spellbook.chapter.introduction.p8.1.body", "gui.unicopia.spellbook.chapter.introduction.p8.2.body", "gui.unicopia.spellbook.chapter.introduction.p8.3.body" diff --git a/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json b/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json index 0a046cbe..57a28e5c 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/the_otherworldly.json @@ -8,7 +8,7 @@ "title": "gui.unicopia.spellbook.chapter.otherworldly.p1.title", "level": 10, "elements": [ - { "x": 15, "y": 0, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/the_otherworldly.png" } + { "x": 15, "y": -10, "width": 128, "height": 128, "texture": "unicopia:textures/gui/container/pages/the_otherworldly.png" } ] }, { @@ -25,8 +25,8 @@ "level": 20, "elements": [ "gui.unicopia.spellbook.chapter.otherworldly.siphoning.1.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } ] }, { @@ -50,8 +50,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.otherworldly.necromancy.1.body", "gui.unicopia.spellbook.chapter.otherworldly.necromancy.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/darkness.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/blood.png" } ] }, { @@ -79,8 +79,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.1.body", "gui.unicopia.spellbook.chapter.otherworldly.dark_vortex.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/chaos.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/chaos.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/darkness.png" } ] }, { @@ -106,8 +106,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.otherworldly.portal.1.body", "gui.unicopia.spellbook.chapter.otherworldly.portal.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/trait/knowledge.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { @@ -133,8 +133,8 @@ "elements": [ "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.1.body", "gui.unicopia.spellbook.chapter.otherworldly.mind_swap.2.body", - { "x": 125, "y": -20, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, - { "x": 125, "y": -20, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } + { "x": 125, "y": -10, "width": 32, "height": 32, "texture": "unicopia:textures/gui/container/pages/dark_magic.png" }, + { "x": 125, "y": -10, "width": 16, "height": 16, "texture": "unicopia:textures/gui/trait/chaos.png" } ] }, { From 72b5b3d50c8ab32dc2e833cec5c5897b55cfc6cd Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 1 Feb 2024 12:12:25 +0000 Subject: [PATCH 88/98] Apply text tint to more parts of the spellbook's contents --- .../unicopia/client/gui/MagicText.java | 16 ++++++++++++++++ .../spellbook/SpellbookCraftingPageContent.java | 8 +++----- .../spellbook/SpellbookProfilePageContent.java | 4 ++-- .../gui/spellbook/element/DynamicContent.java | 11 +++++------ .../client/gui/spellbook/element/TextBlock.java | 3 ++- 5 files changed, 28 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/gui/MagicText.java diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/MagicText.java b/src/main/java/com/minelittlepony/unicopia/client/gui/MagicText.java new file mode 100644 index 00000000..4387217f --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/MagicText.java @@ -0,0 +1,16 @@ +package com.minelittlepony.unicopia.client.gui; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.math.MathHelper; + +public interface MagicText { + static int getColor() { + MinecraftClient client = MinecraftClient.getInstance(); + float ticks = client.player.age + client.getTickDelta(); + + float sin = (MathHelper.sin(ticks / 10F) + 1) * 155 * 0.25F; + float cos = (MathHelper.cos((ticks + 10) / 10F) + 1) * 155 * 0.25F; + + return (int)(sin + cos); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java index 6ca19c38..6350bc98 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java @@ -5,6 +5,7 @@ import com.minelittlepony.common.client.gui.ScrollContainer; import com.minelittlepony.common.client.gui.element.Label; import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe; import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.client.gui.MagicText; import com.minelittlepony.unicopia.container.SpellbookState; import com.minelittlepony.unicopia.item.URecipes; import com.mojang.blaze3d.systems.RenderSystem; @@ -42,13 +43,10 @@ public class SpellbookCraftingPageContent extends ScrollContainer implements Spe @Override public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) { - - int headerColor = mouseY % 255; - - DrawableUtil.drawScaledText(context, state.getOffset() == 0 ? INVENTORY_TITLE : RECIPES_TITLE, screen.getFrameBounds().left + screen.getFrameBounds().width / 2 + 20, SpellbookScreen.TITLE_Y, 1.3F, headerColor); + DrawableUtil.drawScaledText(context, state.getOffset() == 0 ? INVENTORY_TITLE : RECIPES_TITLE, screen.getFrameBounds().left + screen.getFrameBounds().width / 2 + 20, SpellbookScreen.TITLE_Y, 1.3F, MagicText.getColor()); Text pageText = Text.translatable("%s/%s", state.getOffset() + 1, TOTAL_PAGES); - context.drawText(textRenderer, pageText, (int)(337 - textRenderer.getWidth(pageText) / 2F), 190, headerColor, false); + context.drawText(textRenderer, pageText, (int)(337 - textRenderer.getWidth(pageText) / 2F), 190, MagicText.getColor(), false); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookProfilePageContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookProfilePageContent.java index 42ac5a74..f630b9f4 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookProfilePageContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookProfilePageContent.java @@ -72,7 +72,7 @@ public class SpellbookProfilePageContent implements SpellbookChapterList.Content float currentScaledLevel = pony.getLevel().getScaled(1); float currentCorruption = pony.getCorruption().getScaled(1); - DrawableUtil.drawScaledText(context, pony.asEntity().getName(), SpellbookScreen.TITLE_X, y, 1.3F, SpellbookScreen.TITLE_COLOR); + DrawableUtil.drawScaledText(context, pony.asEntity().getName(), SpellbookScreen.TITLE_X, y, 1.3F, MagicText.getColor()); DrawableUtil.drawScaledText(context, ExperienceGroup.forLevel( currentScaledLevel, currentCorruption @@ -89,7 +89,7 @@ public class SpellbookProfilePageContent implements SpellbookChapterList.Content matrices.push(); matrices.translate(screen.getBackgroundWidth() / 2 + SpellbookScreen.TITLE_X - 10, y, 0); matrices.scale(1.3F, 1.3F, 1); - context.drawText(font, SpellbookCraftingPageContent.INVENTORY_TITLE, 0, 0, SpellbookScreen.TITLE_COLOR, false); + context.drawText(font, SpellbookCraftingPageContent.INVENTORY_TITLE, 0, 0, MagicText.getColor(), false); matrices.pop(); Bounds bounds = screen.getFrameBounds(); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java index e9dc8bab..804d107d 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java @@ -5,6 +5,7 @@ import java.util.*; import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.client.gui.DrawableUtil; +import com.minelittlepony.unicopia.client.gui.MagicText; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Content; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Drawable; @@ -12,7 +13,6 @@ import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow; import com.minelittlepony.unicopia.container.SpellbookState; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.network.PacketByteBuf; @@ -39,11 +39,10 @@ public class DynamicContent implements Content { @Override public void draw(DrawContext context, int mouseX, int mouseY, IViewRoot container) { int pageIndex = state.getOffset() * 2; - TextRenderer font = MinecraftClient.getInstance().textRenderer; - headerColor = mouseY % 255; + MinecraftClient client = MinecraftClient.getInstance(); Text pageText = Text.translatable("%s/%s", (pageIndex / 2) + 1, (int)Math.ceil(pages.size() / 2F)); - context.drawText(font, pageText, (int)(337 - font.getWidth(pageText) / 2F), 190, headerColor, false); + context.drawText(client.textRenderer, pageText, (int)(337 - client.textRenderer.getWidth(pageText) / 2F), 190, MagicText.getColor(), false); } @Override @@ -148,8 +147,8 @@ public class DynamicContent implements Content { int x = bounds.left; int y = bounds.top - 16; - DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, x, y, 1.3F, headerColor); - DrawableUtil.drawScaledText(context, Text.translatable("gui.unicopia.spellbook.page.level_requirement", level < 0 ? "???" : "" + (level + 1)).formatted(Formatting.DARK_GREEN), x, y + 12, 0.8F, headerColor); + DrawableUtil.drawScaledText(context, needsMoreXp ? UNKNOWN : title, x, y, 1.3F, MagicText.getColor()); + DrawableUtil.drawScaledText(context, Text.translatable("gui.unicopia.spellbook.page.level_requirement", level < 0 ? "???" : "" + (level + 1)).formatted(Formatting.DARK_GREEN), x, y + 12, 0.8F, MagicText.getColor()); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java index 79867f24..422996d7 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java @@ -6,6 +6,7 @@ import java.util.function.Supplier; import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; +import com.minelittlepony.unicopia.client.gui.MagicText; import com.minelittlepony.unicopia.client.gui.ParagraphWrappingVisitor; import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow; import com.minelittlepony.unicopia.entity.player.Pony; @@ -52,7 +53,7 @@ class TextBlock implements PageElement { MatrixStack matrices = context.getMatrices(); matrices.push(); wrappedText.forEach(line -> { - context.drawText(font, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text().copy(), line.x(), 0, 0, false); + context.drawText(font, needsMoreXp ? line.text().copy().formatted(Formatting.OBFUSCATED) : line.text().copy(), line.x(), 0, MagicText.getColor(), false); matrices.translate(0, font.fontHeight, 0); }); matrices.pop(); From b194f720efacb9a3e3881f7819f18b909c37f3bd Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 1 Feb 2024 12:18:15 +0000 Subject: [PATCH 89/98] Fixed spell equip sound playing when opening the spellbook --- .../unicopia/entity/player/PlayerCharmTracker.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java index 20e91f27..aeaf4d73 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerCharmTracker.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.entity.player; +import java.util.Objects; + import com.google.common.collect.Streams; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; @@ -53,7 +55,9 @@ public class PlayerCharmTracker implements NbtSerialisable { public CustomisedSpellType equipSpell(Hand hand, CustomisedSpellType spell) { CustomisedSpellType previous = handSpells[hand.ordinal()]; handSpells[hand.ordinal()] = spell; - pony.asEntity().playSound(USounds.GUI_SPELL_EQUIP.value(), 0.25F, 1.75F); + if (!Objects.equals(previous, spell)) { + pony.asEntity().playSound(USounds.GUI_SPELL_EQUIP.value(), 0.25F, 1.75F); + } pony.setDirty(); return previous; } From fbe859dcc77bd5feecb963da21fb2c1412fdfb21 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 1 Feb 2024 13:00:38 +0000 Subject: [PATCH 90/98] Fixed zap apple trees not progressing through their stages properly --- .../block/BaseZapAppleLeavesBlock.java | 84 ++++--------------- .../unicopia/block/ZapAppleLeavesBlock.java | 23 +---- .../block/ZapAppleLeavesPlaceholderBlock.java | 31 ++----- .../unicopia/block/ZapStagedBlock.java | 63 ++++++++++++++ .../server/world/ZapAppleStageStore.java | 6 +- 5 files changed, 87 insertions(+), 120 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java diff --git a/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java b/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java index 0b56490b..a971749f 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java @@ -13,7 +13,7 @@ import net.minecraft.util.math.*; import net.minecraft.util.math.random.Random; import net.minecraft.world.*; -public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock { +public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock, ZapStagedBlock { BaseZapAppleLeavesBlock() { super(Settings.create() @@ -29,61 +29,35 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock } @Override - public boolean hasRandomTicks(BlockState state) { - return !state.get(PERSISTENT); - } - - @Override - public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { - super.randomTick(state, world, pos, random); - tryAdvanceStage(state, world, pos, random); - } - - @Override - public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) { - if (state.get(PERSISTENT)) { - return state; + public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { + if (state.get(PERSISTENT) + || oldState.isOf(state.getBlock()) + || oldState.isOf(UBlocks.ZAP_LEAVES) + || oldState.isOf(UBlocks.FLOWERING_ZAP_LEAVES) + || oldState.isOf(UBlocks.ZAP_LEAVES_PLACEHOLDER) + || !(world instanceof ServerWorld sw)) { + return; } - if (world instanceof ServerWorld sw) { - ZapAppleStageStore store = ZapAppleStageStore.get(sw); - ZapAppleStageStore.Stage currentStage = store.getStage(); - if (currentStage == ZapAppleStageStore.Stage.HIBERNATING) { - return currentStage.getNewState(state); - } - } - - return state; + updateStage(state, sw, pos); } @Override public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { super.scheduledTick(state, world, pos, random); - tryAdvanceStage(state, world, pos, random); - if (!state.get(PERSISTENT)) { - world.scheduleBlockTick(pos, this, 1); - } - } - - private void tryAdvanceStage(BlockState state, ServerWorld world, BlockPos pos, Random random) { if (state.get(PERSISTENT)) { return; } - - ZapAppleStageStore store = ZapAppleStageStore.get(world); - ZapAppleStageStore.Stage newStage = store.getStage(); - if (!world.isDay() && getStage(state).mustChangeIntoInstantly(newStage)) { - world.setBlockState(pos, newStage.getNewState(state)); - onStageChanged(store, newStage, world, state, pos, random); - } + tryAdvanceStage(state, world, pos, random); } - protected ZapAppleStageStore.Stage getStage(BlockState state) { + @Override + public ZapAppleStageStore.Stage getStage(BlockState state) { return ZapAppleStageStore.Stage.FLOWERING; } @Override - protected boolean shouldDecay(BlockState state) { + protected final boolean shouldDecay(BlockState state) { return false; } @@ -114,40 +88,10 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock @Override public int getTint(BlockState state, @Nullable BlockRenderView world, @Nullable BlockPos pos, int foliageColor) { - if (pos == null) { return 0x4C7EFA; } return TintedBlock.blend(TintedBlock.rotate(foliageColor, 2), 0x0000FF, 0.3F); } - - static void onStageChanged(ZapAppleStageStore store, ZapAppleStageStore.Stage stage, ServerWorld world, BlockState state, BlockPos pos, Random random) { - boolean mustFruit = Random.create(state.getRenderingSeed(pos)).nextInt(5) < 2; - BlockState below = world.getBlockState(pos.down()); - - if (world.isAir(pos.down())) { - if (stage == ZapAppleStageStore.Stage.FRUITING && mustFruit) { - world.setBlockState(pos.down(), UBlocks.ZAP_BULB.getDefaultState(), Block.NOTIFY_ALL); - store.triggerLightningStrike(pos); - } - } - - if (stage != ZapAppleStageStore.Stage.HIBERNATING && world.getRandom().nextInt(10) == 0) { - store.triggerLightningStrike(pos); - } - - if (stage == ZapAppleStageStore.Stage.RIPE) { - if (below.isOf(UBlocks.ZAP_BULB)) { - world.setBlockState(pos.down(), UBlocks.ZAP_APPLE.getDefaultState(), Block.NOTIFY_ALL); - store.playMoonEffect(pos); - } - } - - if (mustFruit && stage == ZapAppleStageStore.Stage.HIBERNATING) { - if (below.isOf(UBlocks.ZAP_APPLE) || below.isOf(UBlocks.ZAP_BULB)) { - world.setBlockState(pos.down(), Blocks.AIR.getDefaultState()); - } - } - } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java index 4e83cbcf..e94f3475 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesBlock.java @@ -4,11 +4,8 @@ import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; import net.minecraft.block.*; import net.minecraft.item.ItemPlacementContext; -import net.minecraft.server.world.ServerWorld; import net.minecraft.state.StateManager; import net.minecraft.state.property.*; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock { public static final EnumProperty STAGE = EnumProperty.of("stage", ZapAppleStageStore.Stage.class); @@ -17,31 +14,13 @@ public class ZapAppleLeavesBlock extends BaseZapAppleLeavesBlock { setDefaultState(getDefaultState().with(STAGE, ZapAppleStageStore.Stage.GREENING)); } - @Override - public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { - if (state.get(PERSISTENT) - || oldState.isOf(state.getBlock()) - || oldState.isOf(UBlocks.ZAP_LEAVES) - || oldState.isOf(UBlocks.FLOWERING_ZAP_LEAVES) - || oldState.isOf(UBlocks.ZAP_LEAVES_PLACEHOLDER) - || !(world instanceof ServerWorld sw)) { - return; - } - - ZapAppleStageStore store = ZapAppleStageStore.get(sw); - ZapAppleStageStore.Stage currentStage = store.getStage(); - if (currentStage != getStage(state)) { - world.setBlockState(pos, currentStage.getNewState(state)); - } - } - @Override public BlockState getPlacementState(ItemPlacementContext ctx) { return super.getPlacementState(ctx).with(STAGE, ZapAppleStageStore.Stage.GREENING); } @Override - protected ZapAppleStageStore.Stage getStage(BlockState state) { + public ZapAppleStageStore.Stage getStage(BlockState state) { return state.get(STAGE); } diff --git a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java index 5b6a7cdc..7c187001 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java @@ -1,51 +1,36 @@ package com.minelittlepony.unicopia.block; import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore.Stage; import net.minecraft.block.*; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.*; import net.minecraft.util.math.random.Random; -import net.minecraft.world.WorldAccess; +import net.minecraft.world.World; -public class ZapAppleLeavesPlaceholderBlock extends AirBlock { +public class ZapAppleLeavesPlaceholderBlock extends AirBlock implements ZapStagedBlock { ZapAppleLeavesPlaceholderBlock() { super(Settings.create().replaceable().noCollision().dropsNothing().air()); } @Override - public boolean hasRandomTicks(BlockState state) { - return true; + public Stage getStage(BlockState state) { + return ZapAppleStageStore.Stage.HIBERNATING; } @Override - public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) { - + public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { if (world instanceof ServerWorld sw) { - ZapAppleStageStore store = ZapAppleStageStore.get(sw); - ZapAppleStageStore.Stage currentStage = store.getStage(); - if (currentStage != ZapAppleStageStore.Stage.HIBERNATING) { - return currentStage.getNewState(state); - } + updateStage(state, sw, pos); } - - return state; } @Deprecated @Override public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) { super.scheduledTick(state, world, pos, random); - - ZapAppleStageStore store = ZapAppleStageStore.get(world); - ZapAppleStageStore.Stage newStage = store.getStage(); - if (!world.isDay() && ZapAppleStageStore.Stage.HIBERNATING.mustChangeIntoInstantly(newStage)) { - state = newStage.getNewState(state); - world.setBlockState(pos, state); - BaseZapAppleLeavesBlock.onStageChanged(store, newStage, world, state, pos, random); - } - - world.scheduleBlockTick(pos, state.getBlock(), 1); + tryAdvanceStage(state, world, pos, random); } } diff --git a/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java new file mode 100644 index 00000000..a47e5cb2 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java @@ -0,0 +1,63 @@ +package com.minelittlepony.unicopia.block; + +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; + +public interface ZapStagedBlock { + ZapAppleStageStore.Stage getStage(BlockState state); + + default void updateStage(BlockState state, ServerWorld world, BlockPos pos) { + ZapAppleStageStore.Stage currentStage = ZapAppleStageStore.get(world).getStage(); + if (currentStage != getStage(state)) { + state = currentStage.getNewState(state); + world.setBlockState(pos, state); + } + world.scheduleBlockTick(pos, state.getBlock(), 1); + } + + default void tryAdvanceStage(BlockState state, ServerWorld world, BlockPos pos, Random random) { + ZapAppleStageStore store = ZapAppleStageStore.get(world); + ZapAppleStageStore.Stage newStage = store.getStage(); + if (!world.isDay() && getStage(state).mustChangeIntoInstantly(newStage)) { + state = newStage.getNewState(state); + world.setBlockState(pos, state); + onStageChanged(store, newStage, world, state, pos, random); + } + world.scheduleBlockTick(pos, state.getBlock(), 1); + } + + private static void onStageChanged(ZapAppleStageStore store, ZapAppleStageStore.Stage stage, ServerWorld world, BlockState state, BlockPos pos, Random random) { + boolean mustFruit = Random.create(state.getRenderingSeed(pos)).nextInt(5) < 2; + BlockState below = world.getBlockState(pos.down()); + + if (world.isAir(pos.down())) { + if (stage == ZapAppleStageStore.Stage.FRUITING && mustFruit) { + world.setBlockState(pos.down(), UBlocks.ZAP_BULB.getDefaultState(), Block.NOTIFY_ALL); + store.triggerLightningStrike(pos); + } + } + + if (stage != ZapAppleStageStore.Stage.HIBERNATING && world.getRandom().nextInt(10) == 0) { + store.triggerLightningStrike(pos); + } + + if (stage == ZapAppleStageStore.Stage.RIPE) { + if (below.isOf(UBlocks.ZAP_BULB)) { + world.setBlockState(pos.down(), UBlocks.ZAP_APPLE.getDefaultState(), Block.NOTIFY_ALL); + store.playMoonEffect(pos); + } + } + + if (mustFruit && stage == ZapAppleStageStore.Stage.HIBERNATING) { + if (below.isOf(UBlocks.ZAP_APPLE) || below.isOf(UBlocks.ZAP_BULB)) { + world.setBlockState(pos.down(), Blocks.AIR.getDefaultState()); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java b/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java index e1460494..361b59bd 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java @@ -156,12 +156,8 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { return VALUES[MathHelper.clamp(id, 0, VALUES.length)]; } - public boolean mustChangeInto(Stage to) { - return this != to && (getNext() == to || this == HIBERNATING || to == HIBERNATING); - } - public boolean mustChangeIntoInstantly(Stage to) { - return this != to && (this == HIBERNATING || to == HIBERNATING); + return this != to;// && (this == HIBERNATING || to == HIBERNATING); } public BlockState getNewState(BlockState currentState) { From 2e938633e952582867415fb5ca5c0ecde6fba025 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 1 Feb 2024 13:26:26 +0000 Subject: [PATCH 91/98] Clean up and adjust transitions when zap apple trees change stages --- .../block/BaseZapAppleLeavesBlock.java | 10 +---- .../block/ZapAppleLeavesPlaceholderBlock.java | 4 +- .../unicopia/block/ZapStagedBlock.java | 40 +++++++++++++++---- .../gui/spellbook/element/DynamicContent.java | 2 - .../server/world/ZapAppleStageStore.java | 18 --------- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java b/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java index a971749f..902b5a11 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/BaseZapAppleLeavesBlock.java @@ -30,16 +30,10 @@ public class BaseZapAppleLeavesBlock extends LeavesBlock implements TintedBlock, @Override public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { - if (state.get(PERSISTENT) - || oldState.isOf(state.getBlock()) - || oldState.isOf(UBlocks.ZAP_LEAVES) - || oldState.isOf(UBlocks.FLOWERING_ZAP_LEAVES) - || oldState.isOf(UBlocks.ZAP_LEAVES_PLACEHOLDER) - || !(world instanceof ServerWorld sw)) { + if (state.get(PERSISTENT) || oldState.isOf(state.getBlock())) { return; } - - updateStage(state, sw, pos); + updateStage(state, world, pos); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java index 7c187001..40fa9fc2 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/ZapAppleLeavesPlaceholderBlock.java @@ -22,9 +22,7 @@ public class ZapAppleLeavesPlaceholderBlock extends AirBlock implements ZapStage @Override public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) { - if (world instanceof ServerWorld sw) { - updateStage(state, sw, pos); - } + updateStage(state, world, pos); } @Deprecated diff --git a/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java index a47e5cb2..5e3f41d4 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/ZapStagedBlock.java @@ -8,14 +8,18 @@ import net.minecraft.block.Blocks; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.random.Random; +import net.minecraft.world.World; public interface ZapStagedBlock { ZapAppleStageStore.Stage getStage(BlockState state); - default void updateStage(BlockState state, ServerWorld world, BlockPos pos) { - ZapAppleStageStore.Stage currentStage = ZapAppleStageStore.get(world).getStage(); + default void updateStage(BlockState state, World world, BlockPos pos) { + if (!(world instanceof ServerWorld sw)) { + return; + } + ZapAppleStageStore.Stage currentStage = ZapAppleStageStore.get(sw).getStage(); if (currentStage != getStage(state)) { - state = currentStage.getNewState(state); + state = getState(currentStage); world.setBlockState(pos, state); } world.scheduleBlockTick(pos, state.getBlock(), 1); @@ -23,15 +27,35 @@ public interface ZapStagedBlock { default void tryAdvanceStage(BlockState state, ServerWorld world, BlockPos pos, Random random) { ZapAppleStageStore store = ZapAppleStageStore.get(world); - ZapAppleStageStore.Stage newStage = store.getStage(); - if (!world.isDay() && getStage(state).mustChangeIntoInstantly(newStage)) { - state = newStage.getNewState(state); - world.setBlockState(pos, state); - onStageChanged(store, newStage, world, state, pos, random); + ZapAppleStageStore.Stage currentStage = store.getStage(); + if (!world.isDay() && currentStage != getStage(state)) { + int transitionRate = getTransitionRate(currentStage); + if (transitionRate == 0 || random.nextInt(transitionRate) == 0) { + state = getState(currentStage); + world.setBlockState(pos, state); + onStageChanged(store, currentStage, world, state, pos, random); + } } world.scheduleBlockTick(pos, state.getBlock(), 1); } + default int getTransitionRate(ZapAppleStageStore.Stage stage) { + if (stage == ZapAppleStageStore.Stage.HIBERNATING || stage == ZapAppleStageStore.Stage.GREENING) { + return 10; + } + return 2500; + } + + default BlockState getState(ZapAppleStageStore.Stage stage) { + if (stage == ZapAppleStageStore.Stage.HIBERNATING) { + return UBlocks.ZAP_LEAVES_PLACEHOLDER.getDefaultState(); + } + if (stage == ZapAppleStageStore.Stage.FLOWERING) { + return UBlocks.FLOWERING_ZAP_LEAVES.getDefaultState(); + } + return UBlocks.ZAP_LEAVES.getDefaultState().with(ZapAppleLeavesBlock.STAGE, stage); + } + private static void onStageChanged(ZapAppleStageStore store, ZapAppleStageStore.Stage stage, ServerWorld world, BlockState state, BlockPos pos, Random random) { boolean mustFruit = Random.create(state.getRenderingSeed(pos)).nextInt(5) < 2; BlockState below = world.getBlockState(pos.down()); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java index 804d107d..cbcd7392 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java @@ -30,8 +30,6 @@ public class DynamicContent implements Content { private final Panel leftPanel = new Panel(this); private final Panel rightPanel = new Panel(this); - private int headerColor; - public DynamicContent(PacketByteBuf buffer) { pages = buffer.readList(Page::new); } diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java b/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java index 361b59bd..bb936372 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java @@ -5,13 +5,10 @@ import java.util.stream.StreamSupport; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.block.UBlocks; -import com.minelittlepony.unicopia.block.ZapAppleLeavesBlock; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.util.Tickable; -import net.minecraft.block.BlockState; import net.minecraft.entity.EntityType; import net.minecraft.entity.LightningEntity; import net.minecraft.nbt.*; @@ -156,24 +153,9 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { return VALUES[MathHelper.clamp(id, 0, VALUES.length)]; } - public boolean mustChangeIntoInstantly(Stage to) { - return this != to;// && (this == HIBERNATING || to == HIBERNATING); - } - - public BlockState getNewState(BlockState currentState) { - if (this == ZapAppleStageStore.Stage.HIBERNATING) { - return UBlocks.ZAP_LEAVES_PLACEHOLDER.getDefaultState(); - } - if (this == ZapAppleStageStore.Stage.FLOWERING) { - return UBlocks.FLOWERING_ZAP_LEAVES.getDefaultState(); - } - return UBlocks.ZAP_LEAVES.getDefaultState().with(ZapAppleLeavesBlock.STAGE, this); - } - @Override public String asString() { return name().toLowerCase(Locale.ROOT); } } - } From e1f4d775809f1559246b6543eb3120c11a904f36 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 1 Feb 2024 13:26:57 +0000 Subject: [PATCH 92/98] Adjust tree heights to remove the possibility of trees generating absurdly tall --- .../com/minelittlepony/unicopia/server/world/UTreeGen.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java b/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java index 8331e694..0d456e02 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java @@ -24,7 +24,7 @@ import net.minecraft.world.gen.trunk.UpwardsBranchingTrunkPlacer; public interface UTreeGen { Tree ZAP_APPLE_TREE = Tree.Builder.create(Unicopia.id("zap_apple_tree"), new UpwardsBranchingTrunkPlacer( - 7, 2, 3, + 5, 3, 0, UniformIntProvider.create(3, 6), 0.3f, UniformIntProvider.create(1, 3), @@ -47,7 +47,7 @@ public interface UTreeGen { Tree SWEET_APPLE_TREE = createAppleTree("sweet_apple", UBlocks.SWEET_APPLE_LEAVES, 3); Tree SOUR_APPLE_TREE = createAppleTree("sour_apple", UBlocks.SOUR_APPLE_LEAVES, 5); Tree GOLDEN_APPLE_TREE = Tree.Builder.create(Unicopia.id("golden_oak_tree"), - new StraightTrunkPlacer(6, 7, 3), + new StraightTrunkPlacer(6, 1, 3), new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) ) .configure(TreeFeatureConfig.Builder::forceDirt) @@ -90,7 +90,7 @@ public interface UTreeGen { static Tree createAppleTree(String name, Block leaves, int preferredDensity) { return Tree.Builder.create(Unicopia.id(name + "_tree"), - new StraightTrunkPlacer(4, 6, 2), + new StraightTrunkPlacer(4, 3, 2), new BlobFoliagePlacer(ConstantIntProvider.create(3), ConstantIntProvider.create(0), 3) ) .configure(TreeFeatureConfig.Builder::forceDirt) From 8804fa0abafc9d1c2131e565ea8f40253de46e55 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 1 Feb 2024 21:47:05 +0000 Subject: [PATCH 93/98] Added the spectral clock for tracking the zap apple tree's season and make the zap apple tree cyce faster --- assets/spectral_clock_0.xcf | Bin 0 -> 17993 bytes .../unicopia/client/URenderers.java | 1 + .../unicopia/client/UnicopiaClient.java | 15 +++ .../container/SpellbookChapterLoader.java | 2 +- .../minelittlepony/unicopia/item/UItems.java | 1 + .../unicopia/network/Channel.java | 8 +- .../unicopia/network/MsgZapAppleStage.java | 23 +++++ .../handler/ClientNetworkHandlerImpl.java | 7 +- .../server/world/ZapAppleStageStore.java | 90 +++++++++++++----- .../resources/assets/unicopia/lang/en_us.json | 2 + .../unicopia/models/item/spectral_clock.json | 29 ++++++ .../models/item/spectral_clock_1.json | 6 ++ .../models/item/spectral_clock_2.json | 6 ++ .../models/item/spectral_clock_3.json | 6 ++ .../item/spectral_clock_flowering_0.json | 6 ++ .../item/spectral_clock_flowering_1.json | 6 ++ .../item/spectral_clock_flowering_2.json | 6 ++ .../item/spectral_clock_flowering_3.json | 6 ++ .../item/spectral_clock_fruiting_0.json | 6 ++ .../item/spectral_clock_fruiting_1.json | 6 ++ .../item/spectral_clock_fruiting_2.json | 6 ++ .../item/spectral_clock_fruiting_3.json | 6 ++ .../item/spectral_clock_greening_0.json | 6 ++ .../item/spectral_clock_greening_1.json | 6 ++ .../item/spectral_clock_greening_2.json | 6 ++ .../item/spectral_clock_greening_3.json | 6 ++ .../models/item/spectral_clock_ripe_0.json | 6 ++ .../models/item/spectral_clock_ripe_1.json | 6 ++ .../models/item/spectral_clock_ripe_2.json | 6 ++ .../models/item/spectral_clock_ripe_3.json | 6 ++ .../textures/item/spectral_clock_0.png | Bin 0 -> 4695 bytes .../textures/item/spectral_clock_1.png | Bin 0 -> 4697 bytes .../textures/item/spectral_clock_2.png | Bin 0 -> 4703 bytes .../textures/item/spectral_clock_3.png | Bin 0 -> 4697 bytes .../textures/item/spectral_clock_3.xcf | Bin 0 -> 9850 bytes .../item/spectral_clock_flowering_0.png | Bin 0 -> 4711 bytes .../item/spectral_clock_flowering_1.png | Bin 0 -> 4737 bytes .../item/spectral_clock_flowering_2.png | Bin 0 -> 4717 bytes .../item/spectral_clock_flowering_3.png | Bin 0 -> 4716 bytes .../item/spectral_clock_fruiting_0.png | Bin 0 -> 4708 bytes .../item/spectral_clock_fruiting_1.png | Bin 0 -> 4710 bytes .../item/spectral_clock_fruiting_2.png | Bin 0 -> 4714 bytes .../item/spectral_clock_fruiting_3.png | Bin 0 -> 4720 bytes .../item/spectral_clock_greening_0.png | Bin 0 -> 4691 bytes .../item/spectral_clock_greening_1.png | Bin 0 -> 4719 bytes .../item/spectral_clock_greening_2.png | Bin 0 -> 4728 bytes .../item/spectral_clock_greening_3.png | Bin 0 -> 4720 bytes .../textures/item/spectral_clock_ripe_0.png | Bin 0 -> 4711 bytes .../textures/item/spectral_clock_ripe_1.png | Bin 0 -> 4709 bytes .../textures/item/spectral_clock_ripe_2.png | Bin 0 -> 4716 bytes .../textures/item/spectral_clock_ripe_3.png | Bin 0 -> 4686 bytes 51 files changed, 264 insertions(+), 28 deletions(-) create mode 100644 assets/spectral_clock_0.xcf create mode 100644 src/main/java/com/minelittlepony/unicopia/network/MsgZapAppleStage.java create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_3.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_0.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_3.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_0.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_3.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_0.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_3.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_0.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_3.json create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_0.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_1.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_2.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_3.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_3.xcf create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_0.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_1.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_2.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_3.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_0.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_1.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_2.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_3.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_0.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_1.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_2.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_3.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_0.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_1.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_2.png create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_3.png diff --git a/assets/spectral_clock_0.xcf b/assets/spectral_clock_0.xcf new file mode 100644 index 0000000000000000000000000000000000000000..5675c5b009949b593b62693167cb109f44927847 GIT binary patch literal 17993 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 ZkXXxLtC>fKVcHSHepmo&nqPSx^WVkvBNG4s literal 0 HcmV?d00001 diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 0d4509bb..ab09e503 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -116,6 +116,7 @@ public interface URenderers { PolearmRenderer.register(UItems.WOODEN_POLEARM, UItems.STONE_POLEARM, UItems.IRON_POLEARM, UItems.GOLDEN_POLEARM, UItems.DIAMOND_POLEARM, UItems.NETHERITE_POLEARM); ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("affinity"), (stack, world, entity, seed) -> EnchantableItem.isEnchanted(stack) ? EnchantableItem.getSpellKey(stack).getAffinity().getAlignment() : 0); ModelPredicateProviderRegistry.register(UItems.ROCK_CANDY, new Identifier("count"), (stack, world, entity, seed) -> stack.getCount() / (float)stack.getMaxCount()); + ModelPredicateProviderRegistry.register(Unicopia.id("zap_cycle"), (stack, world, entity, seed) -> UnicopiaClient.getInstance().getZapStageDelta()); ColorProviderRegistry.BLOCK.register(URenderers::getTintedBlockColor, TintedBlock.REGISTRY.stream().toArray(Block[]::new)); ColorProviderRegistry.ITEM.register((stack, i) -> getTintedBlockColor(Block.getBlockFromItem(stack.getItem()).getDefaultState(), null, null, i), TintedBlock.REGISTRY.stream().map(Block::asItem).filter(i -> i != Items.AIR).toArray(Item[]::new)); diff --git a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java index 32b01a8e..9ea6a700 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java +++ b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java @@ -22,6 +22,7 @@ import com.minelittlepony.unicopia.entity.player.PlayerCamera; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.handler.ClientNetworkHandlerImpl; import com.minelittlepony.unicopia.server.world.WeatherConditions; +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; import com.minelittlepony.unicopia.util.Lerp; import net.fabricmc.api.ClientModInitializer; @@ -53,6 +54,9 @@ public class UnicopiaClient implements ClientModInitializer { public final Lerp tangentalSkyAngle = new Lerp(0, true); public final Lerp skyAngle = new Lerp(0, true); + private ZapAppleStageStore.Stage zapAppleStage = ZapAppleStageStore.Stage.HIBERNATING; + private long zapStageTime; + public static Optional getCamera() { PlayerEntity player = MinecraftClient.getInstance().player; @@ -84,6 +88,15 @@ public class UnicopiaClient implements ClientModInitializer { instance = this; } + public void setZapAppleStage(ZapAppleStageStore.Stage stage, long delta) { + zapAppleStage = stage; + zapStageTime = delta; + } + + public float getZapStageDelta() { + return zapAppleStage.getCycleProgress(zapStageTime); + } + public float getSkyAngleDelta(float tickDelta) { if (MinecraftClient.getInstance().world == null) { return 0; @@ -135,6 +148,8 @@ public class UnicopiaClient implements ClientModInitializer { world.setRainGradient(gradient); world.setThunderGradient(gradient); } + + zapStageTime++; } private Float getTargetRainGradient(ClientWorld world, BlockPos pos, float tickDelta) { diff --git a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java index 15e0dd3a..d7c7b5dc 100644 --- a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java +++ b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java @@ -56,7 +56,7 @@ public class SpellbookChapterLoader extends JsonDataLoader implements Identifiab dirty = false; MsgServerResources msg = new MsgServerResources(); server.getWorlds().forEach(world -> { - Channel.SERVER_RESOURCES_SEND.sendToAllPlayers(msg, world); + Channel.SERVER_RESOURCES.sendToAllPlayers(msg, world); }); } } diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index 3aa1e5ea..cf71cf2a 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -164,6 +164,7 @@ public interface UItems { Item PALM_BASKET = register("palm_basket", new BasketItem(AirBalloonEntity.BasketType.of(UWoodTypes.PALM_BOAT_TYPE), new Item.Settings().maxCount(1)), ItemGroups.TOOLS); Item GIANT_BALLOON = register("giant_balloon", new HotAirBalloonItem(new Item.Settings().maxCount(1)), ItemGroups.TOOLS); + Item SPECTRAL_CLOCK = register("spectral_clock", new Item(new Item.Settings()), ItemGroups.TOOLS); Item LIGHT_GRAY_BED_SHEETS = register(CloudBedBlock.SheetPattern.LIGHT_GRAY); Item GRAY_BED_SHEETS = register(CloudBedBlock.SheetPattern.GRAY); diff --git a/src/main/java/com/minelittlepony/unicopia/network/Channel.java b/src/main/java/com/minelittlepony/unicopia/network/Channel.java index 48592fa2..67f56df1 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/Channel.java +++ b/src/main/java/com/minelittlepony/unicopia/network/Channel.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.network; import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.server.world.UnicopiaWorldProperties; +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; import com.sollace.fabwork.api.packets.*; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; @@ -29,11 +30,12 @@ public interface Channel { S2CPacketType> SERVER_SPELLBOOK_UPDATE = SimpleNetworking.serverToClient(Unicopia.id("server_spellbook_update"), MsgSpellbookStateChanged::new); C2SPacketType> CLIENT_SPELLBOOK_UPDATE = SimpleNetworking.clientToServer(Unicopia.id("client_spellbook_update"), MsgSpellbookStateChanged::new); - S2CPacketType SERVER_RESOURCES_SEND = SimpleNetworking.serverToClient(Unicopia.id("resources_send"), MsgServerResources::new); + S2CPacketType SERVER_RESOURCES = SimpleNetworking.serverToClient(Unicopia.id("resources"), MsgServerResources::new); S2CPacketType SERVER_OTHER_PLAYER_CAPABILITIES = SimpleNetworking.serverToClient(Unicopia.id("other_player_capabilities"), MsgOtherPlayerCapabilities::new); S2CPacketType SERVER_PLAYER_ANIMATION_CHANGE = SimpleNetworking.serverToClient(Unicopia.id("other_player_animation_change"), MsgPlayerAnimationChange::new); S2CPacketType SERVER_SKY_ANGLE = SimpleNetworking.serverToClient(Unicopia.id("sky_angle"), MsgSkyAngle::new); + S2CPacketType SERVER_ZAP_STAGE = SimpleNetworking.serverToClient(Unicopia.id("zap_stage"), MsgZapAppleStage::new); static void bootstrap() { ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { @@ -50,8 +52,10 @@ public interface Channel { Unicopia.LOGGER.info("Setting {}'s race to {} due to host setting", handler.player.getDisplayName().getString(), Race.REGISTRY.getId(race).toString()); } } - sender.sendPacket(SERVER_RESOURCES_SEND.id(), new MsgServerResources().toBuffer()); + sender.sendPacket(SERVER_RESOURCES.id(), new MsgServerResources().toBuffer()); sender.sendPacket(SERVER_SKY_ANGLE.id(), new MsgSkyAngle(UnicopiaWorldProperties.forWorld(handler.getPlayer().getServerWorld()).getTangentalSkyAngle()).toBuffer()); + ZapAppleStageStore store = ZapAppleStageStore.get(handler.player.getServerWorld()); + sender.sendPacket(SERVER_ZAP_STAGE.id(), new MsgZapAppleStage(store.getStage(), store.getStageDelta()).toBuffer()); }); } } diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgZapAppleStage.java b/src/main/java/com/minelittlepony/unicopia/network/MsgZapAppleStage.java new file mode 100644 index 00000000..10bad6c1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgZapAppleStage.java @@ -0,0 +1,23 @@ +package com.minelittlepony.unicopia.network; + +import com.minelittlepony.unicopia.server.world.ZapAppleStageStore; +import com.sollace.fabwork.api.packets.Packet; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.PacketByteBuf; + +public record MsgZapAppleStage ( + ZapAppleStageStore.Stage stage, + long delta + ) implements Packet { + + public MsgZapAppleStage(PacketByteBuf buffer) { + this(buffer.readEnumConstant(ZapAppleStageStore.Stage.class), buffer.readLong()); + } + + @Override + public void toBuffer(PacketByteBuf buffer) { + buffer.writeEnumConstant(stage); + buffer.writeLong(delta); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java b/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java index d2dd56cd..6082dca9 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java +++ b/src/main/java/com/minelittlepony/unicopia/network/handler/ClientNetworkHandlerImpl.java @@ -36,8 +36,9 @@ public class ClientNetworkHandlerImpl { Channel.SERVER_BLOCK_DESTRUCTION.receiver().addPersistentListener(this::handleBlockDestruction); Channel.CANCEL_PLAYER_ABILITY.receiver().addPersistentListener(this::handleCancelAbility); Channel.UNLOCK_TRAITS.receiver().addPersistentListener(this::handleUnlockTraits); - Channel.SERVER_RESOURCES_SEND.receiver().addPersistentListener(this::handleServerResources); + Channel.SERVER_RESOURCES.receiver().addPersistentListener(this::handleServerResources); Channel.SERVER_SKY_ANGLE.receiver().addPersistentListener(this::handleSkyAngle); + Channel.SERVER_ZAP_STAGE.receiver().addPersistentListener(this::handleZapStage); Channel.SERVER_PLAYER_ANIMATION_CHANGE.receiver().addPersistentListener(this::handlePlayerAnimation); Channel.SERVER_REQUEST_PLAYER_LOOK.receiver().addPersistentListener(this::handleCasterLookRequest); } @@ -93,6 +94,10 @@ public class ClientNetworkHandlerImpl { UnicopiaClient.getInstance().tangentalSkyAngle.update(packet.tangentalSkyAngle(), 200); } + private void handleZapStage(PlayerEntity sender, MsgZapAppleStage packet) { + UnicopiaClient.getInstance().setZapAppleStage(packet.stage(), packet.delta()); + } + @SuppressWarnings("unchecked") private void handleServerResources(PlayerEntity sender, MsgServerResources packet) { SpellTraits.load(packet.traits()); diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java b/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java index bb936372..cb27244f 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/ZapAppleStageStore.java @@ -5,6 +5,8 @@ import java.util.stream.StreamSupport; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.network.Channel; +import com.minelittlepony.unicopia.network.MsgZapAppleStage; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.util.Tickable; @@ -20,10 +22,13 @@ import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.PersistentState; import net.minecraft.world.World; +import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.event.GameEvent; public class ZapAppleStageStore extends PersistentState implements Tickable { private static final Identifier ID = Unicopia.id("zap_apple_stage"); + static final long DAY_LENGTH = World.field_30969; + static final long MOON_PHASES = DimensionType.MOON_SIZES.length; public static ZapAppleStageStore get(World world) { return WorldOverlay.getPersistableStorage(world, ID, ZapAppleStageStore::new, ZapAppleStageStore::new); @@ -32,7 +37,9 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { private final World world; private Stage lastStage = Stage.HIBERNATING; - private int countdown; + private long stageDelta; + private long lastTime; + private boolean stageChanged; private boolean playedMoonEffect; private int nextLightningEvent = 1200; @@ -40,8 +47,8 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { ZapAppleStageStore(World world, NbtCompound compound) { this(world); lastStage = Stage.VALUES[Math.max(0, compound.getInt("stage")) % Stage.VALUES.length]; + stageDelta = compound.getLong("stageDelta"); stageChanged = compound.getBoolean("stageChanged"); - countdown = compound.getInt("countdown"); playedMoonEffect = compound.getBoolean("playedMoonEffect"); nextLightningEvent = compound.getInt("nextLightningEvent"); } @@ -55,32 +62,38 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { if (!world.isDay()) { if (nextLightningEvent > 0) { nextLightningEvent--; - markDirty(); } if (!stageChanged && (lastStage != Stage.HIBERNATING || (world.getMoonPhase() == 0))) { stageChanged = true; - if (countDay()) { - lastStage = lastStage.getNext(); - countdown = 1; - playedMoonEffect = false; - markDirty(); - onStageChanged(); - } + lastStage = lastStage.getNext(); + stageDelta = 0; + playedMoonEffect = false; + sendUpdate(); } } else if (stageChanged) { stageChanged = false; - markDirty(); } - } - private boolean countDay() { + long timeOfDay = world.getTimeOfDay(); + if (stageDelta != 0 && (timeOfDay < lastTime || timeOfDay > lastTime + 10)) { + long timeDifference = timeOfDay - lastTime; + Unicopia.LOGGER.info("Times a changing {}!", timeDifference); + while (timeDifference < 0) { + timeDifference += DAY_LENGTH; + } + stageDelta += timeDifference; + sendUpdate(); + } + lastTime = timeOfDay; + + + stageDelta++; markDirty(); - return countdown-- <= 0; } - protected void onStageChanged() { - //world.setRainGradient(0.5F); + protected void sendUpdate() { + Channel.SERVER_ZAP_STAGE.sendToAllPlayers(new MsgZapAppleStage(getStage(), stageDelta), world); } public void playMoonEffect(BlockPos pos) { @@ -112,7 +125,6 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { /** * Returns true during nights that the zap apples must change their states. - * @return */ public boolean hasStageChanged() { return stageChanged; @@ -125,30 +137,60 @@ public class ZapAppleStageStore extends PersistentState implements Tickable { return lastStage; } + public long getStageDelta() { + return stageDelta; + } + + public float getStageProgress() { + return getStage().getStageProgress(getStageDelta()); + } + + public float getCycleProgress() { + return getStage().getCycleProgress(getStageDelta()); + } + @Override public NbtCompound writeNbt(NbtCompound compound) { compound.putInt("stage", lastStage.ordinal()); + compound.putLong("stageDelta", stageDelta); compound.putBoolean("stageChanged", stageChanged); - compound.putInt("countdown", countdown); compound.putBoolean("playedMoonEffect", playedMoonEffect); compound.putInt("nextLightningEvent", nextLightningEvent); return compound; } public enum Stage implements StringIdentifiable { - HIBERNATING, - GREENING, - FLOWERING, - FRUITING, - RIPE; + HIBERNATING(MOON_PHASES * DAY_LENGTH), + GREENING(DAY_LENGTH), + FLOWERING(DAY_LENGTH), + FRUITING(DAY_LENGTH), + RIPE(DAY_LENGTH); - static final long DAY_LENGTH = 24000; static final Stage[] VALUES = values(); + private final long duration; + + Stage(long duration) { + this.duration = duration; + } + public Stage getNext() { return byId((ordinal() + 1) % VALUES.length); } + public Stage getPrevious() { + return byId(((ordinal() - 1) + VALUES.length) % VALUES.length); + } + + public float getStageProgress(long time) { + return (time % duration) / (float)duration; + } + + public float getCycleProgress(long time) { + float ordinal = ordinal(); + return MathHelper.lerp(getStageProgress(time), ordinal, ordinal + 1) / 5F; + } + public static Stage byId(int id) { return VALUES[MathHelper.clamp(id, 0, VALUES.length)]; } diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 348b9423..a607365d 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -44,6 +44,8 @@ "item.unicopia.palm_chest_boat": "Palm Boat with Chest", "item.unicopia.spellbook": "Spellbook", + "item.unicopia.spectral_clock": "Spectral Clock", + "emi.category.unicopia.spellbook": "Spellbook", "emi.category.unicopia.cloud_shaping": "Shaping", "emi.category.unicopia.growing": "Growing", diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock.json b/src/main/resources/assets/unicopia/models/item/spectral_clock.json new file mode 100644 index 00000000..7e95d97d --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock.json @@ -0,0 +1,29 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_0" + }, + "overrides": [ + { "predicate": { "unicopia:zap_cycle": 0.00}, "model": "unicopia:item/spectral_clock" }, + { "predicate": { "unicopia:zap_cycle": 0.05}, "model": "unicopia:item/spectral_clock_1" }, + { "predicate": { "unicopia:zap_cycle": 0.10}, "model": "unicopia:item/spectral_clock_2" }, + { "predicate": { "unicopia:zap_cycle": 0.15}, "model": "unicopia:item/spectral_clock_3" }, + { "predicate": { "unicopia:zap_cycle": 0.20}, "model": "unicopia:item/spectral_clock_greening_0" }, + { "predicate": { "unicopia:zap_cycle": 0.25}, "model": "unicopia:item/spectral_clock_greening_1" }, + { "predicate": { "unicopia:zap_cycle": 0.30}, "model": "unicopia:item/spectral_clock_greening_2" }, + { "predicate": { "unicopia:zap_cycle": 0.35}, "model": "unicopia:item/spectral_clock_greening_3" }, + { "predicate": { "unicopia:zap_cycle": 0.40}, "model": "unicopia:item/spectral_clock_flowering_0" }, + { "predicate": { "unicopia:zap_cycle": 0.45}, "model": "unicopia:item/spectral_clock_flowering_1" }, + { "predicate": { "unicopia:zap_cycle": 0.50}, "model": "unicopia:item/spectral_clock_flowering_2" }, + { "predicate": { "unicopia:zap_cycle": 0.55}, "model": "unicopia:item/spectral_clock_flowering_3" }, + { "predicate": { "unicopia:zap_cycle": 0.60}, "model": "unicopia:item/spectral_clock_fruiting_0" }, + { "predicate": { "unicopia:zap_cycle": 0.65}, "model": "unicopia:item/spectral_clock_fruiting_1" }, + { "predicate": { "unicopia:zap_cycle": 0.70}, "model": "unicopia:item/spectral_clock_fruiting_2" }, + { "predicate": { "unicopia:zap_cycle": 0.75}, "model": "unicopia:item/spectral_clock_fruiting_3" }, + { "predicate": { "unicopia:zap_cycle": 0.80}, "model": "unicopia:item/spectral_clock_ripe_0" }, + { "predicate": { "unicopia:zap_cycle": 0.85}, "model": "unicopia:item/spectral_clock_ripe_1" }, + { "predicate": { "unicopia:zap_cycle": 0.90}, "model": "unicopia:item/spectral_clock_ripe_2" }, + { "predicate": { "unicopia:zap_cycle": 0.95}, "model": "unicopia:item/spectral_clock_ripe_3" }, + { "predicate": { "unicopia:zap_cycle": 1.00}, "model": "unicopia:item/spectral_clock" } + ] +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_1.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_1.json new file mode 100644 index 00000000..400741ed --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_1.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_1" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_2.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_2.json new file mode 100644 index 00000000..787a4b8c --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_2.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_2" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_3.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_3.json new file mode 100644 index 00000000..8913008e --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_3.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_3" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_0.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_0.json new file mode 100644 index 00000000..59db2a20 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_0.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_0" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_1.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_1.json new file mode 100644 index 00000000..cfa05c39 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_1.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_1" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_2.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_2.json new file mode 100644 index 00000000..90307acf --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_2.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_2" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_3.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_3.json new file mode 100644 index 00000000..110a9d8f --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_3.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_flowering_3" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_0.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_0.json new file mode 100644 index 00000000..c076b00b --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_0.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_0" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_1.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_1.json new file mode 100644 index 00000000..8a9faa82 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_1.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_1" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_2.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_2.json new file mode 100644 index 00000000..cae50ca0 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_2.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_2" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_3.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_3.json new file mode 100644 index 00000000..d2a97210 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_3.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_fruiting_3" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_0.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_0.json new file mode 100644 index 00000000..8f4d3428 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_0.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_0" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_1.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_1.json new file mode 100644 index 00000000..3633579d --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_1.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_1" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_2.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_2.json new file mode 100644 index 00000000..d36dd4a4 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_2.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_2" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_3.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_3.json new file mode 100644 index 00000000..03151fdb --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_greening_3.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_greening_3" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_0.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_0.json new file mode 100644 index 00000000..f8322a56 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_0.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_0" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_1.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_1.json new file mode 100644 index 00000000..43b85fd6 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_1.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_1" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_2.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_2.json new file mode 100644 index 00000000..7bd07507 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_2.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_2" + } +} diff --git a/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_3.json b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_3.json new file mode 100644 index 00000000..60a8cfd9 --- /dev/null +++ b/src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_3.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "unicopia:item/spectral_clock_ripe_3" + } +} diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_0.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_0.png new file mode 100644 index 0000000000000000000000000000000000000000..8591477bcbc653f06f96a49337929768d2bafec3 GIT binary patch literal 4695 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 SHn)JJfJ{kgiAUm>7XKIX`+r9O literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_2.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_2.png new file mode 100644 index 0000000000000000000000000000000000000000..b6ca8164b525b02dc92a1d5222619dd437c1b17b GIT binary patch literal 4703 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 S?L&|hkT!aWreRTf(SHD#oqQ?) literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_3.xcf b/src/main/resources/assets/unicopia/textures/item/spectral_clock_3.xcf new file mode 100644 index 0000000000000000000000000000000000000000..93e0c89a057b9e027becf956b6e5839f51988e7d GIT binary patch literal 9850 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_0.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_0.png new file mode 100644 index 0000000000000000000000000000000000000000..31063a116b45d3028765c23d6b2460c51ea90973 GIT binary patch literal 4711 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 Vx16&z&*V!4(x=VQHKr^s{|`P`fA|0Z literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_1.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a69d288d0a186282cbbbed2478ada369e05eeb0d GIT binary patch literal 4737 zcmeHKdr%YC8sG3XR^?J3C|+GsTT$6$vw5&t2!bIKp)DtkRb4PeCI0?bZ40?ok^#g426IPfD{Bk z0v-dJpr7q0@Q3`t*fP+`e?q+96kzepJ^gf@-T-4_DJqsJfj^4L2^1qxDU3-83@7ArfEN6tFCw0) zV%aZW0hVG`qcP|-8rb2qn^+42L7v)zHR_bZhT$der*9OE2#zhq1HNv&qScPg8L`q3 zc~Q8H`21gwhgHibOcJkNnse~r`lEG4pX{g~TE7&2=HJ$FW5XTMyDe42|9Zr8^HKci zy|*8>6f8Nr89jA(rGK@prk$R4@~6zBXQu=NgdPlOW=x}9<&{hA1=rIhm*$k2pFe#$ zyI_8cF(F~XYT4nPD`HB`N^4PP{DR`O^x9{&ol{;PPChwv;TQO{ z=#jBm?ej7*MH}0;xvsrZLaQ@X_63(GD=s%qTukB(4biT3m(lX9BViLt4F=8V5m!pj z)mIoJ^|uF&fA!hDsvkCfw&ktl)qxpbCc0nVSp0C&?o4w2`5D5AOYV<4_B=dr+TMg6 z_wm+at1rX}mY!)^+w$ath^CH-?OnS^s{LMkSNO@j-J3cDRk2MsDk5vPPYGlPmH0jU zIJvdbKgd?tqOjYxxoYE~(bco=2H|)>^UBJ7_dD-@aeB|oA1_q54qK&geyd$GC>P|Q zht0_1vh*_v%5D{twB5*vJyr*QU{KstkAtLg7!EcvX4a+_J=l9d1hceS^npT;>Kz&; zn@w5hWYQO=W>5=rC>1T58ZV6V5P-nSa3t)pT5K-DqZWC231IWX5)tf$a5-vG7RV!w z-O0dmv0RKIS`X{SMDap6&Pkhyw4~&23h<;BWpkW^kVx|K^2B*EvE6BwNL4D81jQs6 zh5!WO%C~W(2eG-{;VFC^NsNnfvJQ^5+hCrPG}?1HwMYc!;hy-c4!ypY-sb970q7y| zkPeAdj7qFlNq-L)r*#98?tp&R!l3t{Vrjx>QW%cmloY{Cs2q_iaTHM*aU7v!vMFu{it*{ z7f0GChNlALVixcyX{E`iG~$Q~xFB*9sz8jCG6s>63K__pwlLc8xTKY%D zQ_+BmR;d^%1qet~Nh5NF%!H_zm>2}bjWU{&f@U&#sc4EwwmYpPI8N3|ni+}1X7*0- zf)j}bomzy6(Y_Ibh2%`YK`ole+HyU86B(?PN#{sjO=%3O#4${TDPnMi48{6DnT*o~ zGLh%>)=uvXe=G#p3=o#&^Ar$x$H86*jguicyEDUXx2Q$ERkwFq53UnUa%2+8F@UtU z4rlb#;VCFepuX^VI=!7{P5J)|&0iik4&cF&r?4)te!h3eccapo1->WWqlNWeB^dTz z3WB73TX2zX#&_O;%Qr=3lQuI0sz-Oa_KdT?lM1NRNKq>FFQ8wM0e-$QP`p4bl=K$EZp}cvf8?k8EdIzPV0d7WH_~@N zt^v8;NP#y34`kPXTyLbn8-WM1>;EQ~uv4LMfdEjL!F4KAzyl4d(XG~85ulHe4 zXwB{!%fZMXN6Ks$1Py+hZvv>gHXIBFb2_~?_>P}o#8^cC!u%Y9{Kn~$5;O1a_3@uOXMi94CH<=3z}I}5Pdx&cwXC` z=&lzF%g@OZALJ5cKRZ_yH8}!mS~a&KzvWD0I&M8*!@7mB5I1!&95QXAf7dR-{<1h` zk?imjQ_zpQqSaLgwqJX^{7l;FPunt2#bn}}@13DK7iH~_J>6gt-g_EQvTAH|%eXNT zcjWC(ZasJLr+JgZny2gu-*f)xO8<@z$8FswtS#H5!()e6O*~yuwrN+f9f;KNv1lE;6D~HccDN!tX<==JloYSpN9s!aE(rHtZ>Zi?H`d^)a BlllMv literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_2.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_2.png new file mode 100644 index 0000000000000000000000000000000000000000..698a20a7772c5c463dfd06eea3380db7e3b12773 GIT binary patch literal 4717 zcmeHKdr(tX8c%qLP+vt<@KIxQ>+>czHwk%20C`7^WP~Eps@%s(xIl74a)E$i5fmRN zqkvkGqOH%O!dM+T#TUNa)kU{4h?>A<)^TPAc742FFm)LNw+qJ=xVSHv;> z?AufYSys}ap+s-sM+aFRPSMJB1ZEhlz2R@-BUMf91ncDW!|kFljfZDR0(J{uEbNSI=;KeiJQ@Kj%GZjnNqDH==RP zwW<vOy&=eCZz*yigVaxC~r ztGfAO(RTsDysLGan|@g^rtb0Nhc9YI>Reubv!d{)n!S&OTW8kYDVebS>*?;a=PH+{ z%OjdgT?bfKG$|SDAxC+jz_0A1M+4Mqw+rh^PqaR7t*HO@%@5y|H4ojOv>y)No*l8y!aW`rX)%Lb(LfrCl6lOQ|;SlNj^DJZfl-j;4p<+nqT zQjiKP5bA)l%6d)7-MFp@P69J+vGGw*>|RKgHg${DYi-;Jznz{7f#F@ez0jR|=fMzV zFlfUVGM!tVK1?U(=GRgTNmE+hG*N1kO0HBQYNZTC6cmsnII2Mrl^j+1qZ&f3An+bk zdaHxQtt8;6Ai0EwJOogxa018>4Uhpu0VotvV}ug%SIAXBEh9(*m-V2Cw$m^x@zkEH z;;1M{g;InH$K@tOp;G%pDiw-oNJ4=S8h=cw@Hd%AIm%N}q&9-FTX1-sv;{WEtY zTzyiYPvHLS>X)le3iJuwpI!eqxxBhxr+^jy7nBKKmaYzMy8>Ud1`u;1!{F<^w_w=z znmMbX$I}+I&>;}KH=JuiL0P#kbb7FQL%7ES7vYGp%EepWRSN_zUiz?EvFgj|#Y>Xr zr}X*Z}NlMDU-usQmx1*~6I_}kJ&A;nkI)=22b&ZU__xj*1zo53` z)Pv3Dz}dcK&s`g4_-_P$!L9BXTWBtjrezQtZzWXh@u0ulmDY6ob5KWNk=tBJvpTlN+^nodfBiv_vSor$FnXBr*9X(vT$*7Fj zlbuu_ZAFF#x1Ps}5(T;W_l3nL{Zq!FS2OI9#n>mg!hIXUrX9HO zVeYNvKhoLzyiYcGr+xCi%*%8y;n z;NO#*3)E3Z8WVM!E$=e7-7h`y%llc^bGD5sdY+K9`uz3cghd`kk-#fTc<#D)Zp5wO k4TD{hu89{GXWxJ8;jyxDYMYzc2@^%251$uS6_S|uA81C3%m4rY literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_3.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_3.png new file mode 100644 index 0000000000000000000000000000000000000000..de3e77a6a137fbde685cd9052fecf64334c742b0 GIT binary patch literal 4716 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_0.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_0.png new file mode 100644 index 0000000000000000000000000000000000000000..9be87cb47b1c52f58b0ddcb53d2e17a91d71a2ac GIT binary patch literal 4708 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 bK6wt!t+)ae#B|*S5dxV_Gm{P_EG+ylj}?sd literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_2.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_2.png new file mode 100644 index 0000000000000000000000000000000000000000..78c13d32b2cf77fc9678a805eea15666afc141d8 GIT binary patch literal 4714 zcmeHKdsGuw8lUinu0mOK>w8GuRn%l2nIw~#@Q{b35{$qi_V{2jnTZ4BVJ0yF7au5! zXb}}fkydTdDsFvCtN2{8)>f_5*6Qwx)}A9iQI{66r`Ej_p6>RXJ)X1ukK|nLH~0ST z?|%39efJ}?+>ko4U&!DP2!i_Qk`vNEA11V*0PuH2>kA5WmzXRatJ95vg1`eniu@pv zfB{X^%k~lZLcU;ZDQMxq7BE5^Eb{mED}g`mw5%xi)Tzy9Y8S6Ku)F{6BDmf6)`Ochcf@~es*HHM!Smgd=!^Bu zkDCe?UfO|PxL4|1W!-ewG~xX1%rlq9`uT;O3i^~`2k~nvifo0~(~)mxtTaD+`g~ep zPLnY5IR4!$=$fpC@R213L;RotS4$dq zuQf*Mn}bKZD8FC%*|zdsze_6f&)6C__xa5QkLT58QaN8u3K_Mq`Po7>;gIH z;WDzES^5+;ZL>%zlg-FTJr=tVFh~>Qu~T#|x(IFa-8W30*Ck1;fc~)NS(TcsiYG4b)h#2-loVi+Y7RaM` zn}dM~DIrBAi5_k)E{+a?H4YQ2PD@DYqySG^@ocBlu0{~I+bwm=q&9~c!Bi?0g5n5{ zO8`Q`=UbhWM`GpQ6ev145*VI#aCRqWv%&%=Wwg1RTCo_+!(IMa?0S7Sy_N4&0qB8v zC_92lQN&_FdavM}iE{x-r$fKGg3rjeGe{c4+guKsNu0}Aop1K0Fwx!X?Jh^2_c$gR zVe%LYfbw8h?4>P*8`r%;kig7Y?A}#C?3a*Ej_ncaCEtV@?{RuN0+x64zJ%^NyB7>l zdc8WqM!N*}bO~Cqu)o@5qdAk>Ybuqjj9^U)32wqr385fp3CSvG2_ZKT7=bJ0MpD^} zN@wMrl$B-#DnKsf0FO~lnv{%OCc#*YkPrleNl2PjNC-@YGDcd)%4Mcr6q6ks$Vw`& zw^ss{2~f#t5+zuSl*mXDmk=tl|&CH zlX37M69rCh?exwFVNrv_0AVR1PXU2<9GpcR?_emW&5>cVYXBaEJelLc{`uaajvJNE%;|XQc+BIx zR|$r_mqJa^9Y^4)xlBi}09VHpJ)5$c8Bjes)3s}y`;}C{Q8^=5$&?b1Iv{>BmXxTB z3Zn#NP=Zt$S(!pz4(eBxd5CtHZ1jQvN z_Md_wod-sQn$dM)4f0=_XuN=4MFyMTp>NLQ;Ze-3UY&&B|3iv3tqGWjFToNfYhb=N*ymX=<=b#-w=*w!6E1yLi$gw6U(dF&?>>fnO=k#{1O zhZKa}(WSg-q4I$2Lr;{LyX^fmzw9Z}$BcE|)lDuuc;n=+{N}q?)s|IQXyi6d4GzHWhr`D|J2j2%7Pe{aby lpD}R|^Z7X2#9dc@@y)@md~sbP7jY0qb literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_3.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_3.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8b07b9f210f5db669a26c13ba65bdb1d5556ce GIT binary patch literal 4720 zcmeHKdsGuw8lUh6*HtW7#aE2@#7SoIP9%ts04W9{L<(JTon&spXkI2mh*S`E()wi)LO(^AFSoLcZPSj=j`#E?SCZaa=-cR z@B7_*fA_l|nK$(*iDN?|LJ$NQt4&g;!G1i`f&$>*84sTmu)7E{wPvk$0TKisAd=&U za2O11oME;P#~1O1zEapGK%2oZErR3E_M4$SrQfcB_PlA_^^{@${9R~Zo zuu)}wa~*{?^9f?VNzh`L2l_;}VyCBOORD8T%8z0Ygx*TT83J}CwU$9k`bX*@+lA7MY3lCgWF8AAW_^L)TbxnAM ze&&^skCcBn^5eK#apZh{W$~J0$NqS7@20m7?)&Y&V(wGlj(cspZ}Vnf-yZhb32*y@ znDb4Y-Pa3>E`Esp<8Fm-t-bCJ8F%hh#>tCOetzSR1)T!s$?mF}Vn;!1y5Q^V&6b`g zy;%jTuNz}yBP&J654|ZXw@7W99>%PFV;i~cX}$N$-D4&!c4a?!`nLNL*l0;Ow={o; zJZ|B{MVWV2We6o5RL6&V@6-rLRffW`wq=2&(4Gu9r{uO!ALw ziappRzj3X~&PNBL91 zB|hDS2{&qd1MC~GOC0u(-1RZYLa$Nr zGGQLYJ6wP(=8O3ls_{}DAulF`8|@;^$~1LCKLvbJ@p5R|sT2tE^78n3BEG|A5#S1i zLVyVcLLmwfsC%8ACcLQKJ&U2}<4^;)$wfJ7%3zqK42HcKZmkDS*z)sH^PC=Rm~@AP1cndtjzACW zodrXbPN!5mOu5YRv}zTPnO{jdOcbePO$;Z2R4O4+BP<7~7$c0RTr46`43iNik;I4t zlW-W7*6yYWy9qEAt8;ZP(n)OA~OceVtg1ys*8eI zNmz%kilHJQl?<4TA{>*VB#DVou~95UO-(Cp*F%ixO@I3QI6~3JF+0+>0{a1qj;VGB_Mo6>qTW4i4+!bs`Cx zP!lwOq=R+XFi?l1FieT{EuW#&IY`R9?th_~%fpR^czEPVlpD^!j&=3jsC2Nl@2T(6 zO0ido%VjTxk}&mc!A*Do$*vP}^$nSF2)hNq>d~LB1AgikQUSvxB49F0P=JdRs2Im( zs8Nm)sF^eqMzO>!C1m1}>~4pd&Ldo4i3REiwSwu%{)ghuV-ITnNOWEfU`_!FhQe&c z@Sh7N=-;q_sTl)1ju!lvCebWlSdoEoeLh&cU@a637Q=qcn6mRLe)`YiSDXP-k52MJ z`i{yqD%T4s@Iv6x>>8Epg%o%p@Mw1Z-{cAzdYuAx_$w$6zAR-`-+TwYXayLP6V>qb zegZPSt}(d`dV-xvS#AUw^AgiINNxQj=nSN_I!)khA5QqR1%EF&J_SL1#%k3|4Dz$N zRoOXJMA+#K`wtd3My~S@ot74Vjr*)BQwXkny(QbAzo_%mEcCpTEe;4Mc~a2K&1_oj zpDjL|MAFOjQ8H_$-8Jhkxq74MaG{`UIG z56lU743>YsGAUvPCpIc$d>m4q!fjZ-zWx2cvKuL$nTg%)1<0<>{j1!2Zl;6j>r+At z6%B_t5hnxrWs!7YzUEd-hc&U>uY7K5$v5{xr>B3>U8{#)mfw31J+k;~YzKMMJ=!_~X4XiBNmsU0@BD;Qlv~P3ZSpl*l z>H_}Qv(|I|T=zk|pbAP4JA)|j$YAZl~=&TG??EPWom}RZfN!&p)`Scav9YSUR_K*||9ZM{i!N2rt}G+a{=6-y}aJ*-+Hz_%Z89N9&cy*A9Gn zch!udu$R-o)WRk@D{0$-&ka89i%z_9wqnKZPq!{FO=$gW`&XZIl=2_7`&?bI;D9$_ zM-%dKUCHmx{Pa#;@8R&y(5TYsvy2~vG~a);IPJ6K)-%TF`h7N zjEG#{w!eIPsUm#hzux_}AX#jyzR(eVY~%5%d0nw literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_0.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_0.png new file mode 100644 index 0000000000000000000000000000000000000000..54c1277e7b215f0663492aaefd9573bae9490ed5 GIT binary patch literal 4691 zcmeHKdsGuw8lNC<>Piuii7t!2A+hNs=0v&VDV|47c|e)qfI z_q*Ty-S2+nt~4bj3=A9*h#<&7Lt<<)?1TB{?+gEqZu_2s-6fD_uow&xh(8=aOOZDs z;%#6P^^v_q1CRkw_W^7nQ08s;HbUeh^lP9zvP&+9a(I`G7nEfpFJvI>D`6v`918or zu(3s*YyAbv{KsGTCPIlv?(P!|Mw6J3%5bS%1^sbCLE;37%fy6?B-Er*1+7IN^+v=a z{jv0_7NRU?^?H**uNOO{w>h+kx}#{1)%8*y=?vWBcO zg;!2^VOCw;j?;UWtv*!si>gBL^8xoB-`V*;)dQR*N3YqMj^Yr;zR|8m@5&!x0yzQEp zw}(ciHP1~Yl=s+sd-gV$V~j3U%Vl4SP+qGUzl754-~YDj!)thH+NqGS#U_(}_>ddL z4OM04FysAz(ciCswE66=^`E>EU*ePU+vxcncb2p+tWKroeK{>~d_l`w^)H6`#MDkX z)S|gvUvfE0RCw|D#>Rio9(nxn_~vh_hw8k({qxe*kE;LhShP9v_?@z_Z3id%umLN) zS{KLPE+61)U)reT?E74mlaS#RvmW|tG~Q=cl^<=0 z=Aeg7Npq(er;{{ilTr+422zjB!5tkG~_wX%)7^ z&={}+8#HynRb{U%$=|ph51xVy*5(krAla{2x>-vvS+B&#&j{P;8whmo!F|QLd+&lV zv@#mWSdPx&!!yL{B>eg$!_h253Zg{|C=H+)l%^;xsvu|usxd1Rs9Y%E(MOBoXMipiij%qao9I9jlp_MT}uC(-lnBin$R#Mi! zQSnd=gi>n=Kv@(tYBA$xR6%Q0s2M16)FM-?3S^&Udy;YyZY&REe2l8xIepoGP(kLpHsjNe+A{j$5Mlwd;uR?zUFBOvG92xf&_1? zo>l}k0gl9XT?i61m~SGaqH-8i`ne6pIKKy8q9G%-!>Dn3_?m7t#73uR&aWt)kvaIC z;G$#3g~8b`>qD1K7UJ|BiqI`+kGlR7KuQq_^t-$)Mbx-9!89d`B1l6p_p^N@*k(e>-B z%~!q{(N1FxO>d!f|EwClzVYnuBeL!9Mq<<%8^op7$*k6XN`s2r@Dv0UUop#!e{xA)G~aOsOLs@8t9W>wIJvQxgk zM@`q4UCvstyJpZ_Jz`xyM|pIUUu*HNy(*%Sfg?WM)P}m+5%BCvNBES88896XLtIj9 JRm|MN{{jHzfvo@l literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_1.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a6ceabe352c7d39f510543597bad4c2e7cbb5ebe GIT binary patch literal 4719 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_3.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_3.png new file mode 100644 index 0000000000000000000000000000000000000000..37fa366af98b379bcde7e8f674efb20ad48ed794 GIT binary patch literal 4720 zcmeHKdsGuw8lM1yps0uXLT!!FRzxS6%;Y&FD3SnCA}JuEXLUPCZo-r#6EYKsS}foj z6%}-Ck=5F&wR(J8-AZeHRdH*(iq=A_+Z7Y;?nM&@ILK1dpu|RAIZ7gZ@&Ax zzx&j<>@Pf!0h30gdJTc2n!nuNGmiirsYoR8r$62nPMD#WEEt|Fx}h!%X*5fRVy z#+LVBpn?T-e7wmJA1`!!7#r;X2;!^AU#(3#Vd}pyC2fmnKuF9IRnTXLujuu|vIi8K zMqCWrNq+EUW8ZSws4?P_qU>YGHh!~x{<8hM`s^waJ`cQIe{*xK=*^mS{b!u?-D-&a zzVhCan*4%u+pzEM7YCNR*4J4lp1qs#&AG8bLH&;PJOyl#Z0VXJCjWYx_v-ukan;+$&no&1&yHSZ%5bG{CnEaqxGZfo*f*h4QM)4xa`5ft@VO+F-LEfjaa{LY!5ng zVZf7l2{+aR2D=Jt&ymQTGOtWpJ?Dqi!|qo?xbka}khW??)8t0X z=#B?jbkZCTNSiYy-7+WJqPB|0$A)QqBqVSGjuQHu4i`)Mv?4z*32ojj5efYeE?XDiNCd})ZSgtXMq@j@i)~c_ z>LKw_Zi!TkNt{ke=NT-g&xItd0sZC-Hhs1mNKygIcs&-N&jl`SWM>MirG38J>v8zE zW3@0cW+_lwDS}X{ZRSoCDIOYTCFSTG z6;EY_R5CSYmJW=shbW&#ijmD*2bwU7yn$4SBCq@9!u#fT&0%d3WZ8ZbbvB| zhlQEQbNXwie}q345^e?xOYwOM3H*Mz7c$-hD30-@GmJwkYOlKO!$x?WtQ1G-C=Ni< z_Bx#2R)@!87>Tun&(j$hD{Y(oztH^U5o#bF9(fYY!u4nST`e~%4dk>uwLCg#|5XwS z{g;BIEG=7Lsa#<7#|gPwhAdf>%MM`mXie8PKm8l204R%s!W3pyVKKv0kSj1$g-a>a zN&rCFtX6`s$-A<%jE&2qJRr^vb%a{M^z{FS5=QwCYD`yjUKZd_0Sbmnsk;|>C4IZ) z>Xz%36nG_YcXoBl^-2o761Y3N{%>-Hb-Ydi7yK2J2Va)9J}B4;U$lbFlM{9D^*#dW zxBlSd#n2P#PMXdlNUzuUCP2z72192EXE5qRY6AoVhV}iR{H@m!Bp}?Ni%VC1>n%;m zD$N;udOR7E$y_qcVJA(Td?rpmX<_nwS47R>Ne}nL?I|-Yt~ySg-x*hLdR~LqRy#8n zzrDwC_|$<6Ve-MPwYushS$8&FK6GN4i=$60;F_OY{DQdBtcb3QPTh zT{1IMbT1ogs13dmxbe)3A&&8tRUl>PWx>efo6`2)VTRvB8n5>uu>wQ?!B+`$M34WZ zxEjX3SmcZP?#f?2+_GZ%soIzepX`r=E3DK?u%!XW7G1>Ymdh*^G*(L9_L9ydpc%JM0!5E z+Rq8az54mSrkX)NZ-{z1Zq}XcFE!JKjh?HojGkJReIX=>6d|u~Dw(i-RY+~&;;_bE jf&=vaYgK(8Huo(|Jip`HT_28uxq=wm literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_0.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_0.png new file mode 100644 index 0000000000000000000000000000000000000000..5e8c4f31aa3d33d89e45e03126b35c5f2c86c530 GIT binary patch literal 4711 zcmeHKeN+=y7M}nrVnwB5rOGizyM9hGGvsRq!iNMQ#b{Py#CGvGnVE#qBoi|O1iOf$ zRt2OgaIiotw9<7^ab46s_1mhr>$j^_&$dO^?y9KuW7P^;j{9c#5O;gd9?#kSM{*wT z&VBd(?tQ;^-zBr!GCg@<$lDRKi-A0%Q!WGY_)ePuAfvJXXdsxY!N7n# z6wLKtuq!)a9RRZU36{PoKoXI==46xEBFB^{qKwi4e*{w#2u2{N9772VCp2ncEvtE% zk%&~GTHgXtma~b87E@xP-0R~Utc!sle|6qEL&_1$&=oV%>J;JqqF3pIe!u5RQqsr; z;f0pb7elrY8}>aORH+_2L0M9;;PBy%$Lg0AH8zAc6v$fxuiw44>6YT1=8B>39rgeH zVeFZ`Hyw^sYV+B6_6&X@79bq#->S?Zj6YTBYt z^;0Gfi%xHuXTvnt+3TC@Tgp_l!G?1SFHO{3+A}Vn)NkKDng8SxvOfK2*w|GTOJYR$ zl~v~(O6{Y~H~WuzvGz{IpX=6cd1GoxA8TzwcKfyb2OsRVk@NqW8ZvIly|<3H4ev8$ zZ+zoD{ng_o7h+@uXAhM(|2TWZp}XT+p6niG2zd6VrA2pke{ok<5q;=d>FBCIPU^$< zUlH(N@zkqjfqgwon>Czg8($p@MO4nZ-B+&_ETtAo~Pd^?iX z_0Iw|=x43zLb`bxL2+&+Npp5a>34g@1A}7X{9ck;zzA|X<77Ps#r?ep6>^p~C}wNS zh}oOSWU?uXd`#M+=~imd0t%-Uaj_vWegXiv8G)4h-7XJL_zemPmjJS;Rw?9?iLk(+ zNC$P4$oUw#TB%kdaFURXsg;At@VxbSCt(9=tWz%cy2DJeTF8n51mRBaG<@K~vrS-YlO> z+73;r7#HIPraXv>z7kTraorvw1y0uOmAnAiuUHDK<0Vl&rKe!ruEk*;rAM?FqjBh? z=pIle4=<1&iV>jzxRM1p4pO71Q51zqJ&MC>n#N%qBQ-GMh@xo+V@I`Gv)~-H02|);?BYhFh%+ahP_kUT7mq#80?7@+zusn#LE2%neR2s9e;Fbq$jjF$#shu@<$#wZ=(~t!@S@e%o|m>yc%-&@`9>=Q1q7Ll30D2dtaURo z*OMcU$8EpRw>{!xOm}1WWc3RAV(2c1<$Qwiwk73w4RLzj%HV*0MMEMlCDdN2&3MLN zsXZL3IA2=>YfIP4!`9nAzJGMX5Nqn+=H^G9$|0_{dk)@gTO4<`;{NAnf3>{{L_WXGG8T-~o{j$1~XPTSap}U!lmQ&@q5s`JF zsOF2{*y@1!H^%J9{;JV7@e_W-_L%rm^P10Gwwl15Gn3**?%wt2g0Cuhf7(9V%{isq z%B9f=*x_z{&U}nMMnC*pPsjM^$6n=pKMQtcG!j7JT7qj)^N@-dvWmx z^`Xx~qmdu9V-D(|)+g;BdA_B-`O8e=?33R$PHs5x>-Px%Lnb2ZMbR5y+oK~5nROwl zEx-L?@k#ZpXnIsx&C|i-oA?2`R@%uKphO^3(sX0Plz9dJ1__ph`Tzg` literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_1.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_1.png new file mode 100644 index 0000000000000000000000000000000000000000..561ea8b3e807117767c83d29035fa93e92468048 GIT binary patch literal 4709 zcmeHKeN+=y7M}!ANM+?vWkFnxQ;W4a$z+m{4+!#+04WApK}5G&=PO|}UuFh~wy0qJ zLPe`6T|if@R;w%2>cJ|tl?qi?Wu;o{+6sPb>lZ~_>@HTV`(^@&r#)wn=WPEYoR@d! zzI%V~-ru|LhFOu3o)Rn>CV?O**qmyd4CWAi1P%b-BRikqU^+|Zn5|~>t56_#0G1+u zNW|NKA?hQGMShSU&@BZc6v(^{KZc0{gn1Q^BfI4?AivUWBL=cUB!+^)Tm=ROz*Z-w)_u`fNtAAHrEba2U)^=t4P59-O@*&e3yjSnV zHPzkdXenHDb{Bf)c8Onwv+@>|c>4S7#v9@GM=#IFNQxMG zad~s~dSaC2M$pJ7Ywwhw*s*rcpybkktgQ+8kFPB3c(XPepZ#T;WXz)W-yeSv9*|fU zzrS7Edc5>ptf=_!M>n+mdurs-wlTMUtPL}We>zyS@=ophZKCp+qgU3Cs{CkN028!C z+_50JwajmTv#3Swa_(iT;-H9%DYpk`wfCna$-cxx_%Mt#OL`bL$GDtQ9up^Ad7J@;!Me03K8M?4>4kT)-7Ek; z25SA=x>1_IoBai6j7$z3o8 zRu+ri=pys@@XSU7%kc^o)6PR4=TXt~>I z6Bc;F^$8hf1B}Vgr^SnBVk3Jn^q#gr(DDO74D=G#cX z%ceans6-x9=$(Q)e=K^C3?LTg>l6S8dXS4g$wT9u%ai4D*$r@S*X>=lfa^rz9B#xp z8i4lp;jErMJPrlj*cUzzXK_)Cb>9E7<}Z&l7TAL$Pi0uJ|2#qEyHT0+T;G%L(as20 zNh%dCg&rq;DX@4xO$l)VEZ-8Di#u&J=pNnW+M{QFtrQdlK@+3~p+QlBQou1pi;*Z2 zjYi`tEk!9Yt&i4Tbk=3%3UCjdU;{h?u0VMTzfjUwgo7IUY%B%2G=BdCeOuh zKVAKFJr@JdW!zs~{d7GS1J7mLUtRw#ER_#$z71Zq1`uf}M(}zc z3WZeGrY!@SAb09C7J^=Qksl(cqADCH2XbbMY2Zz`9El32oj6U#)K^Gsl0WQ za$o#o$gkhs-XWf5PN%LVidHFe&OM+=By0Ouu&) zzFEJ%<+To}WLV|K3~^$xDqJ->xX8HUQagP4uglijqALf*dOKSleHvj+-nO#wxGa3{ zg0|~b=f^f(u!L_KB-%1nHRu?G>WjAL-#fJRn77zjax(O~e6x71Hm2y*o)0(fQ9OJ( z<*yjsw=)Y@?uKn?1V??Q8R?XC_ zKinugd*V!6^U07#Dk$$%{GXf95ATdBWY7I`;m>!!E6U6)W7TmD)uHaL;Isfwhh+a6 zE-Y#$-garX>UZcLIVi?@H`eE+HuP+Agj6AY! z-@7u^&)=LrFk7^&HGJgo?~P9uMO-3Q*psP)P({e1s9iy+loE=Xn-U)rxFvnlj@j>% aZ4=a;&GzZrj{gG`2xK;;8>?+gSNIvKN@nK~3 z$=eSamM*V3fd1|0^?tjZdz$H4m;RM_zGmux0U;FuRg5i+&)>0fIR5q4mV&>0zV+bmVm1%7emXO={l+H`{%|yrSaM}f(4^(9qc6N1GjP_) z8HZc7jTbgw)r(ese`aUHvjyR2?oMicdUTXQ+;(imhL)pW+!Yl}KXYUIggxI*9moc* z5kFWK)40QLkaI7%?Zf=XpP5|XjAPJ{IR$?v2RmNrNis4S?( z70sluu{mxgA!nYI%1Nbkv@|L*Nbkh~frIf760al8$>Uyw)W?ehTW}*%i4WpQHAs^{ z9z}C*MxvA{WhiX&vYD7PGDxC#(>6Tb7}H4so($3ykH>{0NLE&sEK4Ed+;&8+)9DZt zLof^m2$;`ydI&G<=&g_#7^arJLT#cd7vN zK)i$tk;_oT;Xr!N;60{HK++k|ug>7D*)9f&XLv5dO);iS#_4&dHw8^~&v#|G(|p^Z zDTGO5901CLRpot_6mDGi3_$`r>u~vI0kQiaJ*=%qtUj>`BfjnQ4g^f^=Iw*-+Pe=7 zP!;iMRW3XscKz@t%XY#J@8fNd0}ft6Y%3X?W)u4*-@By>8A)+u$pDCWCakd;JQ z@2CVS8c-=z42F?99gI>W3M(~gB}{5i6s8plN~t2%a=E61il*=w&g~$;ak37=&LA$Q z-M5AyI6gDZY>;9yw8s;dMtE#sfb2CS@uN$?PYnsN=Q)?x}&jw)4J4W{Y= zB{FUvWTL?7tDU|P;aG6686Yens@pwm0oREpJcN<( zFo3kX4qLnG@KhAV(T?y1Itxd$w(S3f7A}uO5Afi~V_6=opY3yX+^7U5z2m9lF^%7Mh2n{LM^kwHcnWyrkpgc7?$54%x!y>DHv;!(*Z)nfpq|$$#tHrk$^tJ-mZ}YRz>C%(a?Wfcc)bsW zLiQY;vkrIyU9lhV5HxtW&_vMg!ZE<i4{Reb~g9Uu-g8)sUz7c=tb zr{t%Pxe)d5@YXHP655#`GC8}hq}G(SHDeob>_GjaxN*q^nE~R_=QodCFnGz>+T)Vg zlFAh8+VIe`L&B23J$-uK#^akVN?!I@p-&HPdQw+6#hTrA z{LI<{boJTCm; z9`^m>1*I44mAPMrl9n$%RK#B|Q28mIFPt*E^whrZOlKmeq~g20M^-PBRc*UvDr{4{ z(myCIKgkbs8j+CUEn`=TK+k!`RU$q;msTOHv~gX z_aC)iudX<~-QwSRb;y(Gn44{jp=I~l-&-@P9%>0&QO_;PZ)h*M`qiUrnZrWis4;B& z@{97AVrhBNu;hobmZV$Dm&O-8+bg{pc{$K=n3f$({0B55V7WLR`s;_I_D5eIUK1=% hxk6K#dd2hcInyp(`SE78KgbivY?@~*oAvR^{{XxbmOcOg literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_3.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_3.png new file mode 100644 index 0000000000000000000000000000000000000000..9f228247b13e5f458847c716c3b7755d3cf203e2 GIT binary patch literal 4686 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 BfBXOd literal 0 HcmV?d00001 From 7cb8e9c859f62c9461e1f85b2e9a5980b8d2b7f2 Mon Sep 17 00:00:00 2001 From: Sollace Date: Thu, 1 Feb 2024 23:17:46 +0000 Subject: [PATCH 94/98] Fixed cloud block placement desync when on multiplayer --- .../com/minelittlepony/unicopia/InteractionManager.java | 4 ++++ .../unicopia/ability/AbilityDispatcher.java | 1 + .../unicopia/client/ClientInteractionManager.java | 9 +++++++++ .../unicopia/item/cloud/CloudBlockItem.java | 4 +++- .../resources/data/unicopia/tags/items/falls_slowly.json | 3 ++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java index 736cf4a3..7c60d0d5 100644 --- a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java @@ -86,4 +86,8 @@ public class InteractionManager { public PlayerEntity createPlayer(World world, GameProfile profile) { return new DummyPlayerEntity(world, profile); } + + public void sendPlayerLookAngles(PlayerEntity player) { + + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java b/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java index d9a977b4..8a291d0a 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java @@ -209,6 +209,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable { warmup = 0; if (data.isPresent()) { + InteractionManager.instance().sendPlayerLookAngles(player.asEntity()); Channel.CLIENT_PLAYER_ABILITY.sendToServer(new MsgPlayerAbility<>(ability, data, ActivationType.NONE)); } else { player.asEntity().playSound(USounds.GUI_ABILITY_FAIL, 1, 1); diff --git a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java index 3a4105b9..837b65fd 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java @@ -26,6 +26,7 @@ import com.mojang.authlib.GameProfile; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.sound.AggressiveBeeSoundInstance; import net.minecraft.client.sound.MovingMinecartSoundInstance; import net.minecraft.client.sound.PassiveBeeSoundInstance; @@ -37,6 +38,7 @@ import net.minecraft.entity.passive.BeeEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.vehicle.AbstractMinecartEntity; import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket; import net.minecraft.sound.SoundCategory; import net.minecraft.util.Identifier; import net.minecraft.util.math.random.Random; @@ -152,4 +154,11 @@ public class ClientInteractionManager extends InteractionManager { public ParticleSpawner createBoundParticle(UUID id) { return new ClientBoundParticleSpawner(id); } + + @Override + public void sendPlayerLookAngles(PlayerEntity player) { + if (player instanceof ClientPlayerEntity c) { + c.networkHandler.sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(player.getYaw(), player.getPitch(), player.isOnGround())); + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudBlockItem.java b/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudBlockItem.java index 4652d8e9..f208e723 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudBlockItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudBlockItem.java @@ -1,5 +1,7 @@ package com.minelittlepony.unicopia.item.cloud; +import com.minelittlepony.unicopia.InteractionManager; + import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; @@ -30,7 +32,7 @@ extends BlockItem { @Override public TypedActionResult use(World world, PlayerEntity user, Hand hand) { - + InteractionManager.instance().sendPlayerLookAngles(user); Vec3d targetPos = user.getEyePos().add(user.getRotationVec(1).multiply(1, 1.5, 1).normalize().multiply(2)); ItemPlacementContext context = new ItemPlacementContext(user, hand, user.getStackInHand(hand), new BlockHitResult( targetPos, diff --git a/src/main/resources/data/unicopia/tags/items/falls_slowly.json b/src/main/resources/data/unicopia/tags/items/falls_slowly.json index d413b877..49ac7cee 100644 --- a/src/main/resources/data/unicopia/tags/items/falls_slowly.json +++ b/src/main/resources/data/unicopia/tags/items/falls_slowly.json @@ -2,6 +2,7 @@ "replace": false, "values": [ "minecraft:feather", - "#unicopia:magic_feathers" + "#unicopia:magic_feathers", + "unicopia:cloud_lump" ] } From 82428f77b78e7be443585f6c5f1c5f57e427f87b Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 2 Feb 2024 08:16:33 +0000 Subject: [PATCH 95/98] Add more stages to the spectral clock --- assets/spectral_clock_0.xcf | Bin 17993 -> 34751 bytes .../unicopia/models/item/spectral_clock.json | 62 ++++++++++++------ ...al_clock_1.json => spectral_clock_05.json} | 2 +- ...al_clock_2.json => spectral_clock_10.json} | 2 +- ...al_clock_3.json => spectral_clock_15.json} | 2 +- ...ock_ripe_0.json => spectral_clock_20.json} | 2 +- .../models/item/spectral_clock_25.json | 6 ++ .../models/item/spectral_clock_30.json | 6 ++ .../models/item/spectral_clock_35.json | 6 ++ .../item/spectral_clock_flowering_0.json | 6 -- .../item/spectral_clock_flowering_00.json | 6 ++ .../item/spectral_clock_flowering_05.json | 6 ++ .../item/spectral_clock_flowering_1.json | 6 -- .../item/spectral_clock_flowering_10.json | 6 ++ .../item/spectral_clock_flowering_15.json | 6 ++ .../item/spectral_clock_flowering_2.json | 6 -- .../item/spectral_clock_flowering_20.json | 6 ++ .../item/spectral_clock_flowering_25.json | 6 ++ .../item/spectral_clock_flowering_3.json | 6 -- .../item/spectral_clock_flowering_30.json | 6 ++ .../item/spectral_clock_flowering_35.json | 6 ++ .../item/spectral_clock_fruiting_0.json | 6 -- .../item/spectral_clock_fruiting_00.json | 6 ++ .../item/spectral_clock_fruiting_05.json | 6 ++ .../item/spectral_clock_fruiting_1.json | 6 -- .../item/spectral_clock_fruiting_10.json | 6 ++ .../item/spectral_clock_fruiting_15.json | 6 ++ .../item/spectral_clock_fruiting_2.json | 6 -- .../item/spectral_clock_fruiting_20.json | 6 ++ .../item/spectral_clock_fruiting_25.json | 6 ++ .../item/spectral_clock_fruiting_3.json | 6 -- .../item/spectral_clock_fruiting_30.json | 6 ++ .../item/spectral_clock_fruiting_35.json | 6 ++ .../item/spectral_clock_greening_0.json | 6 -- .../item/spectral_clock_greening_00.json | 6 ++ .../item/spectral_clock_greening_05.json | 6 ++ .../item/spectral_clock_greening_1.json | 6 -- .../item/spectral_clock_greening_10.json | 6 ++ .../item/spectral_clock_greening_15.json | 6 ++ .../item/spectral_clock_greening_2.json | 6 -- .../item/spectral_clock_greening_20.json | 6 ++ .../item/spectral_clock_greening_25.json | 6 ++ .../item/spectral_clock_greening_3.json | 6 -- .../item/spectral_clock_greening_30.json | 6 ++ .../item/spectral_clock_greening_35.json | 6 ++ .../models/item/spectral_clock_ripe_00.json | 6 ++ .../models/item/spectral_clock_ripe_05.json | 6 ++ .../models/item/spectral_clock_ripe_1.json | 6 -- .../models/item/spectral_clock_ripe_10.json | 6 ++ .../models/item/spectral_clock_ripe_15.json | 6 ++ .../models/item/spectral_clock_ripe_2.json | 6 -- .../models/item/spectral_clock_ripe_20.json | 6 ++ .../models/item/spectral_clock_ripe_25.json | 6 ++ .../models/item/spectral_clock_ripe_3.json | 6 -- .../models/item/spectral_clock_ripe_30.json | 6 ++ .../models/item/spectral_clock_ripe_35.json | 6 ++ ...tral_clock_0.png => spectral_clock_00.png} | Bin .../textures/item/spectral_clock_05.png | Bin 0 -> 4685 bytes ...tral_clock_1.png => spectral_clock_10.png} | Bin .../textures/item/spectral_clock_15.png | Bin 0 -> 4702 bytes ...tral_clock_2.png => spectral_clock_20.png} | Bin .../textures/item/spectral_clock_25.png | Bin 0 -> 4699 bytes .../textures/item/spectral_clock_3.xcf | Bin 9850 -> 0 bytes ...tral_clock_3.png => spectral_clock_30.png} | Bin .../textures/item/spectral_clock_35.png | Bin 0 -> 4708 bytes ..._0.png => spectral_clock_flowering_00.png} | Bin .../item/spectral_clock_flowering_05.png | Bin 0 -> 4696 bytes ..._1.png => spectral_clock_flowering_10.png} | Bin .../item/spectral_clock_flowering_15.png | Bin 0 -> 4709 bytes ..._2.png => spectral_clock_flowering_20.png} | Bin .../item/spectral_clock_flowering_25.png | Bin 0 -> 4714 bytes ..._3.png => spectral_clock_flowering_30.png} | Bin .../item/spectral_clock_flowering_35.png | Bin 0 -> 4723 bytes ...g_0.png => spectral_clock_fruiting_00.png} | Bin .../item/spectral_clock_fruiting_05.png | Bin 0 -> 4727 bytes ...g_1.png => spectral_clock_fruiting_10.png} | Bin .../item/spectral_clock_fruiting_15.png | Bin 0 -> 4703 bytes ...g_2.png => spectral_clock_fruiting_20.png} | Bin .../item/spectral_clock_fruiting_25.png | Bin 0 -> 4712 bytes ...g_3.png => spectral_clock_fruiting_30.png} | Bin .../item/spectral_clock_fruiting_35.png | Bin 0 -> 4719 bytes ...g_0.png => spectral_clock_greening_00.png} | Bin .../item/spectral_clock_greening_05.png | Bin 0 -> 4735 bytes ...g_1.png => spectral_clock_greening_10.png} | Bin .../item/spectral_clock_greening_15.png | Bin 0 -> 4706 bytes ...g_2.png => spectral_clock_greening_20.png} | Bin .../item/spectral_clock_greening_25.png | Bin 0 -> 4722 bytes ...g_3.png => spectral_clock_greening_30.png} | Bin .../item/spectral_clock_greening_35.png | Bin 0 -> 4701 bytes ..._ripe_0.png => spectral_clock_ripe_00.png} | Bin .../textures/item/spectral_clock_ripe_05.png | Bin 0 -> 4717 bytes ..._ripe_1.png => spectral_clock_ripe_10.png} | Bin .../textures/item/spectral_clock_ripe_15.png | Bin 0 -> 4702 bytes ..._ripe_2.png => spectral_clock_ripe_20.png} | Bin .../textures/item/spectral_clock_ripe_25.png | Bin 0 -> 4702 bytes ..._ripe_3.png => spectral_clock_ripe_30.png} | Bin .../textures/item/spectral_clock_ripe_35.png | Bin 0 -> 4685 bytes 97 files changed, 255 insertions(+), 115 deletions(-) rename src/main/resources/assets/unicopia/models/item/{spectral_clock_1.json => spectral_clock_05.json} (53%) rename src/main/resources/assets/unicopia/models/item/{spectral_clock_2.json => spectral_clock_10.json} (53%) rename src/main/resources/assets/unicopia/models/item/{spectral_clock_3.json => spectral_clock_15.json} (53%) rename src/main/resources/assets/unicopia/models/item/{spectral_clock_ripe_0.json => spectral_clock_20.json} (51%) create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_25.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_30.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_35.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_0.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_00.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_05.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_10.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_15.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_20.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_25.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_3.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_30.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_flowering_35.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_0.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_00.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_05.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_10.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_15.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_20.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_25.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_3.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_30.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_fruiting_35.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_0.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_00.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_05.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_10.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_15.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_20.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_25.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_3.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_30.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_greening_35.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_00.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_05.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_1.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_10.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_15.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_2.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_20.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_25.json delete mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_3.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_30.json create mode 100644 src/main/resources/assets/unicopia/models/item/spectral_clock_ripe_35.json rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_0.png => spectral_clock_00.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_05.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_1.png => spectral_clock_10.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_15.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_2.png => spectral_clock_20.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_25.png delete mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_3.xcf rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_3.png => spectral_clock_30.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_35.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_flowering_0.png => spectral_clock_flowering_00.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_05.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_flowering_1.png => spectral_clock_flowering_10.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_15.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_flowering_2.png => spectral_clock_flowering_20.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_25.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_flowering_3.png => spectral_clock_flowering_30.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_35.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_fruiting_0.png => spectral_clock_fruiting_00.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_05.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_fruiting_1.png => spectral_clock_fruiting_10.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_15.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_fruiting_2.png => spectral_clock_fruiting_20.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_25.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_fruiting_3.png => spectral_clock_fruiting_30.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_35.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_greening_0.png => spectral_clock_greening_00.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_05.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_greening_1.png => spectral_clock_greening_10.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_15.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_greening_2.png => spectral_clock_greening_20.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_25.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_greening_3.png => spectral_clock_greening_30.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_35.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_ripe_0.png => spectral_clock_ripe_00.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_05.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_ripe_1.png => spectral_clock_ripe_10.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_15.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_ripe_2.png => spectral_clock_ripe_20.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_25.png rename src/main/resources/assets/unicopia/textures/item/{spectral_clock_ripe_3.png => spectral_clock_ripe_30.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_35.png diff --git a/assets/spectral_clock_0.xcf b/assets/spectral_clock_0.xcf index 5675c5b009949b593b62693167cb109f44927847..82bac275d33399aa2c9944c350417afc2419e4c8 100644 GIT binary patch delta 12649 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 DdwSY6 delta 834 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_1.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_10.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_1.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_10.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_15.png new file mode 100644 index 0000000000000000000000000000000000000000..7573eac7d68550e3aa56a5bfa7964663da4b63c1 GIT binary patch literal 4702 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 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_3.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_30.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_3.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_30.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_35.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_35.png new file mode 100644 index 0000000000000000000000000000000000000000..4d7b00ac738e5e76f3b6efeee3c4a8f376d0dd49 GIT binary patch literal 4708 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 Q4srsrS|%p$9yhDxKL*!;XaE2J literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_1.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_10.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_1.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_10.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_15.png new file mode 100644 index 0000000000000000000000000000000000000000..d469774a0349d39acbcb1524a7068b6aec9a719a GIT binary patch literal 4709 zcmeHKdsGuw8lMCz6rX_Y3Kq%6C+K7zNrogPiY5qAqB#(iy4yWFGnv2$c})h0Y*mUV zK3YMJTA^AORMu9dRgZ4fBTA`{wye^+#TPH}fwjA*w#u>|7uh?*d$;H8@to~{OmgRb zbMNo|?stFpyI(RJEsGLH2ZaYg5H#A9XiNt6IH3fN0Qbnp-6W{$nN*YAWQv9Y!2>`_ z{2_^e0Yx&%mP-5}KhU-wln`JG7@>qqUJ~nffIYF--UjR$y%;I5F^Lo!4eDK>;J_Xa z>W@HS3w!3O2DWeoil0Pa3Cw+Ug2`-=;R*~@CJ+tpsSv9|t@V zm_qpu4PYr?4F-$JV34^vmz~XIAjr3C)mD9CZQO+QEtL!6ql2=?WnHrD{#%87%kiN2 z1LIb#TN;t1=vq5%v}x1ky{*gLkJ64jY;8MqaP2`^;PX7q>zb%ji5pv{u6q@0_B7W1 zq2(FZ86i_eCmlQ0mREVpt}7jP?Bo*cNzmK&z{wMeOPM-Ta!~c1?Qs`)>S&(&C|DhQgSF{Pw3*E{M@#Ls+p6{dK9N-J*wMKYPu4d zu;DxvFf*cVLFN2d^J`1gyMJ1D=wfs`e0ll)3(Gt2BBRUM??h&Ig`6DEZLhg>BrjTZsVeecV^?=JL?@YZO}G8)yV^97bX@wc z(i$Zh@Mw zJb0fw*9zC4zTa^;Ys=LDnc>WWCMclroNw#VYpa@@uHN|bnvCUL&m>cFxI*fijS(OQ zeXKQ=Pc7@m~*9GOn9&Zn1)ygFbD&4^qkLih~5JQc)| z!NoB$l|rRJ;dmdLjmu{T$znO1U6*W}-%A0W^zw9`ck2)&Cnradqg1%KGz8OXwFrtM zI1U2@>|N>PNgwR=P8KM7IE;*!=2$n+x|}kBlcZc(yk0H`{jxs)9B#9@pWf;1RRQRM z_((T`DNw}WKn9QS^6}Y#q}QRJ9pSaEbTddY<8@_mG!viAIQhwgDQtBAcy|_;DK5uG zBTOda08lTO6&o_8aN_z$2oj{R4!1Z8h&=?!v-Sb8hWIA*h|3x52pHbaI|SXgb`cCv zX0y)dqO%0|Oh&z2m|tge(X34;nlwt#B+8&Lqtv3Xim|I;Z45!dF_>Mc#_TpKCdNL9 z%H;I&q?2X@DnPDa0gpyW$EYX@gH;3uZW;{%Q)(22aTHb4F&M2S2x<_;VvYq-NoEfA zN}#d|U3h&mlQxZW_=LXa&Mk{DqRu5H~7nC^{#d5q1Fx2BSFm{-6vw1MOWav{>640|;b(#|h<_3p(lI0K*_p5%q}9hPfY zt`}0^g}}qnH7wT)Deywz;pqCm$rUv4I>k7_zn~oOvJ`oz^(XM6HG*20U<9xCA<($@ zPb@41Eibzh-|#}v$WWn3pt4;PKw|)JGRFrzkV*qbs2@BkI|)HjsmVCks`+YF36>s8 zj=#9(T7ed3NW1%PBDbvNiN3nCdsX)jXQqF$a7tlCZpQ2m z#nkKHYI8y_T&#uCUwNge_+hrJJS8@}qNusJsBPZHzZOX2o~$mObfb2D_2oO;W52jI z*8JvsyEUHN55_(W3w^EB(p(IsLmGjT^=UfBoyS z;-~jUIr8pWtVKnIQHpI)pm}jy(Yr_A>I{Z%-6Wt2!@gY~KPFbcD{-B5dWy>|56;Y{ z?mxdiU&C5BVh$+!;iNk z&*#i;t$FciYiyd66-%cZM?8%Exz?|?b5jAUj)DRS!1~N;);$LjRmc_FCVT* zzI^SCksV=*H}QLKH8s~fJGo>;p`ZVbhIiU_RM@?eT-(^v%$m9g=zPn)*T1}AZ%Z|N zwq`-aSARElANM=gP&B8~y(bOXd_x-ZyUQgXt_qo@_dV{KSQpk_b35!sV3mryxKeQ~V-h)x6*5{~IpMhOqzu literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_2.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_20.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_2.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_20.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_flowering_25.png new file mode 100644 index 0000000000000000000000000000000000000000..9ab9b43e5f0d0a0a422b8c050ac99b07ba8be9a5 GIT binary patch literal 4714 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_1.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_10.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_1.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_10.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_15.png new file mode 100644 index 0000000000000000000000000000000000000000..75124f0eaf4e39cea45a62be0555bf91fe3a2319 GIT binary patch literal 4703 zcmeHKdsGuw8lQj~L_JcZEJ~4Sd?9v{$$K&qfhGu1qX;Odt1_7fjFJbF2}E3#wN<3P zALWSRYSCh~7G

(xSWS`l8ip7qu=Av6XJ!Jz~|eJu2><;puMA+2c9e|44FkzxnR> z{qA>v_q$(m3lpcu5Ah%24?)lnOM*EG^Z`Qi8vwq;AH5(zca=`FFcwQB6rcFNm;9MNgx17xMd2O0wU63|pY4g&o_ z(Aa{GxoUtcy!^y>0+0mcu0GykO@vi)B`R0zfIq6z;HV0GU=+hqJ+4N9wPa6EL;})R zTT`0=%Db$|lxQ)TU>E0P*en`?yd}#v8xzjNgsg5Xn-Ul4pA+D@khtp~rLqmj{Np|f zShQ+(*fja`cg7B}ytj65(_B|;M#T?J&4)gI=VREfJs+EhjX#-C*f?fYu(BchZ)cV^ zKIPiNU`^z-qbHm5%WgAxalp~4S<1)$zi0eLg={LOYb{CsHFxiil+-ky^A@eB-@SLn z7xM=DtvQ>`Y}Y*;@UXVb?rIpf;mDRlfxCyz*v`i`$CpnSc`93e@~zg|HN}=I!{XPR zvq>j})lMm!9A$lDmUh>Vs}7xyY(c)9`{7^awtcG@-16joy8QZxxWKh(EBn3XzbIm_ zPpI-6Ptsfa{vLJbZg|e~z^Wkb{Xbo($dA-qC=Y)&ba`7{yS{Flv5Aw0U&Q<@ocy*g-xGNsfVNmr@fp1cw<>Z{q-o zN`(Lm#J$A96JEsO9wk6@V3=t)$+0e;bvj@Hldw5Gyiq0t<8W7ec9+%K4exMwvH7IXeNileH5Ww8G`c z5C;Up@z_L*QKpikJ(|QUf@gq(Q8tZrc)UFWDXg7N<_UpKr5@E`I;BdBVyIe&>U)f4 z)0`V*qJSyZPH{xo791=F5K9Po3IIesSPO3AXo7cgDNbjWQPy2`yN9jdI8g*omXC&-Q^xQQH^65|9|9YbU$;mDvt_2^93EP{G$evj z6v|*)!hm(|UpKnj$?&-ZN5^IW9syS%J;h%rI6~a0@h``cn@J1100=`+75MxoVT#TL zD}(N0(Kss zS^zWyT?upC5Hx6*&?L}~k`SPj@)m2H^m`v4Kk3N%sYxfmZTgwT9Gik&_H0?0nKbi_ z^U)1&g^;Z?1G;(Az0?{e#R$>LpPiIEghT} z5*XBfYPRW8#l-;^ZNm=VyXlErac}mG+h@MG{MV(qO{ag(Ufy`+LEFTZ39i#qO2U&L zKYU!UFTLo7XA?SUWdE3QZDLUW7^UyPUCm#mM~@q2+;&}7aceYm*s<=;L&JffF@Goz zwtTj9bO@C9@YZeh>e+4IMSnXi+xKkgjB9HLZFq3=lbrhFN_5#q$L_6_m*-eXcGkv? z@#FW$9iRN?ZDcG|^xNq6)JExP$FZ5nl-l#-Lbz2yCMlTVC&$q9S5~>^SFc-HTByD@ zayUD5D&-R!u1=h)<)$;z-{JGo&#d#Ll|}77 zN0QU%{hwOWP3Cf~tK`I$t@k}Ob$4#v-8ngYU)_&q>t~jgFKD`y9-cq8Ja|mes9a*f zKRHcAap-t;TXE}=@E6S|@h$QLCBeszo^Khwa{^oO`krUi_|e%v=d`3;U*EPa^zt3~ zwuL`n(f6x&w61v+FsLeU>*0l4>f0ki4n14`Xi)8emewN+pRBHyR@J4ynVR%qVr?Vy z-J7WiRY4(5rInRo2Sc%ATW%Z-g%H1(L0=!-G54bHr-fF@E;xUzHMsIoS=9?DxZrEs U&8@R2kQ$IBZo0Yr?evxZ2GLc4umAu6 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_2.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_20.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_2.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_20.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_25.png new file mode 100644 index 0000000000000000000000000000000000000000..3f4048b69ff3b7f0c599df665ebec8229812d477 GIT binary patch literal 4712 zcmeHKdsGuw8lS`mF-n(3x4J4NMtp&jnF)E9)X*eAlqdzPh^{)B2MpvzGI_w31*?d* zt}7G}t5&PUTD7dSwOSw0>T2Cxi!PjHwF)Bkptkk3zSecEduMo`_MAPQv;B`GH}{)+ zfA@F4`@7%$lDj5lZepl-ycmL@P)m|271Uu|5e*05k$0cspgK!sSZIqS8WMpAfD{ac z1RMqwK|fn47y=CeZL2^D2R4V{%6P#rzFrIL3Eg%Du%~xpguuoGLMRl}n?aESdkm;| zgTj<{%~cOiNxVg)N1qcO!|G)mka7tPoy2nud4EYl|)H;h|#Y3FQnwAdf!JC(BK`|Z+o`^Dy6 zVL2tQMkLFgte6^VdE?C;?V0Y|S#{sGckJD>Vvj`hL$T&X&5T1yYc5SGc^+%ayLkM! zmmYf_Mo1LV$sZr;DBgL2)@=;?__O)gJ@Il{G-V_s;;lO39Q`}FFKmNTOh zSD&zj#6+B)y)z-sI&r>o%Rfu@ei?lgZp^GXnfdS=WYpFBl~ir>cysuh8Ltohr8sB$ z4&emsYhUWj_pZlXz7pww68_m3PvsY<>WZTkr)ner^NZg-Y>7^`7FaG1J^Q3X6G<4d z$7Z)gTzz~l@9k-Z+rK{8bm51bA=OW!j<&UIPc%GLx$|0ED)jB1kZ}caGRH@9I>+^nD*CvquPVxzJcr(SyNaD!CBkb3C?r4_Zh zjZ`+1RN$e~3g)I01-XQll+Jod92e990tdz7lAy!x^y-3oDbK3|HrI?uB|L=9)k`x# z92s36N}`Y{WGHM7GJd)AC9x#VL(;lbQ$jZdc+yL=S=OyXkU$_H3&hG?o-72@YPAR| zN91xCAYgC4lf{Fu(>s}?=;AO@Uc$q;S;pm*aGbc!OKX_ZP?K~glVQK4!W#Z)%67Wkvm&^#4M=n`BW2M)HAao|}L;&x{7 z9h~60_!Ns?Dwm;sEh%=KrNIEbG?{Vwf_)w7jDt#JaZXK4jjA*%Os>>oN|jQh?gPC_ zdAuMJIZi%z@;%(P=)hutus9c|fPilYYtb1!6wbOl=`NRDFYV2`z1>!DoJgF-O*l&d z(%w9r-jj!8P*jI@xzEvAT_i*2|1UImcqDNE54Jps@q+pD`KGQDl}6=tJ#{_W8U83q zB>bV!;Y8OGyttntc|QSHR~M0uJF_T|J-WlSr=9tUP#`o~Ic5V|Phm25=7*p9ORTT7mH7f1xDP`Hh+}5FN;-xLp8(!KfU3dIZyU z3x;$r7~yh8&x+%aA88WD1NtQyFs`c&BrlK)k=|t3tr?ege#Te#Ui^$R0P4X>o=M+9 zxd!EWCIy}eJQ!Vray^p*&jcQfuK$}{;=bz?v8If*84y$^@N zHhns$6ts+RCoS|s(8$qT5kPNm9tRpjSc}yha!n`{g-jEse!UNZgu^VR_;k%*edX5d z(XWgtX|OcSOoqQ*yOyz!oc+cy;brq|7wY{G2C+wGW#c{=NTB&D65`#n6ETQRC-K2)tgpesRXlWmBr?2Pa~;Ply~kr($W*)8Us-=95Pn%0kZ@ z;idSLD(8^{6*K;FylLC>6E(-K*AJny)6C&{I}Q)qW`x-F&#jK?fTnsE8Dd1+=5G1> d?}LkO6p3VKHX^0R!F3*jEathU+LxET{$CxZgR1}l literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_3.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_30.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_3.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_30.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_35.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_fruiting_35.png new file mode 100644 index 0000000000000000000000000000000000000000..246831ece58e021b99ee99518ced6c07546f63ed GIT binary patch literal 4719 zcmeHKc~BH*7Vkj?1c`{2x*(x59thgg#~jSGjE*t`3^GapBWtXfp6;HRR_36mXBeQo z!(qIl(Uf?U7hzG6Xf#Gmd6gk?zWuA7Pnx~^-`{dzcrY^t`b+Wf~bAK&Zu zz4!ax`+e_w&`UC=q=bb;hCmP$W=z#*f;n6q!GYj+Ov_6OOc&T3qs3^9hl0Tau$1(M zB%%!%k}h(9q!-i+=t{td0J3N!jz~!#|GW&yQSI^?Adhah2>>!K34p@Dyb%ln$b-PV z6AZ4jZLgz17C*uMpHv`;$Q^Tv(UbubG8~i1Rlpx36eLD~FN~>4EP+&LfVE`HtB6D- z-KoTMfbs#Cl$2piN`hUy)57Jl5aio9ca=8vq;6L=WWszUxXN{ z!ei%Ig-gJbd4wY53!ig_hvqLq95K&lod9j^6smo06lAwZ2d0-`Ko; z>e+Yu1($uDXIZCu82Io^rQOvKyJFAk>WIw)r>+wwHmB5#dGlDF?C{tJXUfWr=Le>g z)tZCGM4w5kOinZno2K0KsJQyu_&dnC8J~VLCG*_w-yhKQv(i+T?TnLT=Y zKoow>lR38H?};~W#dupI4h`bho;bC4LA>HrP0X+T=RK{9Pd61BZ}z^>+N_SDb+#e4 zy68L4f6QASt9$ULgFjqb9lmUaDMqH|o}CoA{uig#?r3{S-#FWq8&FW`ao?)Xugq<@aq;Sx^XJTHeJ&Z1&zG8iSP~6t z(8p!x2sx&7l6Kl<6yr3rGN0Wg9vGB3!RMmrIjjJiSu5wzO7DMlObT<1Rytj2!c49t z*2bk4@@!V&lx(_i4y|FN6UK)m`bYp^X9WuO*>fFk(x;XBaY-PHYE%mQO@ujGX%48P zBqz_p3YkKNAqF4kC8Xm+;6$FWkeT}Ab_npKmD&WsMWSeae!eVUE_3o$6xV1pC`OEUVC;AI`c@U9RH-RbZ0@VWkU7#d}B zSvxRwgI)1%TZ%WX(?g`d%Gq6hFFYcPl49}?7O2z$2#z}Jw=@-?QTCG&EYJ|oa98p-z8bnQ~Rft@!!YD$m(BO=+3zX5} z7AObJickPt#sM7MLf`~0S0iSPoJJIk97i-tMFOHSs}gWa0;W_d@GcNjc@9)1mD@Ec z5sCp&gu<+$lnD%itHfxOathI4v>8z;)N(nkv?vrT;fG>qGTF)7DR7*eowBm1%VG5| zh=h|9GmKg(A;Vs2GIA-w0vxo`bk5=Ny;{iT>}-}miEQEtm`bg}36(~nAh3kCwEcMR zu)G^oqKN75oql)mSV)izAeIvA6ae`3AQv);XDPwSXFHv_T4`t3?OZm2>%>q3rKbcI zfOhub?2bM>2E$0KEqoEquVvg*UH><_LSDU2u@3NGP(FBBqMzx?z>8L(d2)&#yxvDZ;TsN2 zE(MytuGBxfAt-d9I3&>ejYEMlNHCfVL3aZJf`jmXmN;)hP(VMUeqy%zv}g5XTh_!u zCC4^Y$S!@8vgpcc+mh86CdJsk&y3uBFnDWO)ur7t25cQLGp=~hC0V@+9<+bgmUYF3 zA#VvcLznL#nKpRR;$+&?0$0Q>`YiS1WlMc%LH!F1JGt@Y26^c$bF^vL;SsaX-*Q%t z+)bV=Ouk(KgH%j6H6 zYbvfj`uy8iebe5%DsP#f%bY%{W#+-%fp0(3h8tH-T^92+_$->UX>g&eNb=*M=uf8n z9C*B>{4yTJOHl3H%qF_er$yq=m{__}#V0)3HT&XQu6HI)7k#&hn8ZMVroQf@`<=*dera zC0{wew@zuj)#vE=!&Tn$H?Nnn2O+z6pnFwCVO_6^c~)mF z(dhJMGuNJ;zN?A~Kb8jlIe@Ui}DJ z<2L*>{IB=sF1{z#fTVsoH+R^a#;pFk-yS`9+AL?l=y0gd%@&i6(vuKm-@kT;BCa;vXH#2gd~^^y+cWEuy6DkC&bGg>qmO!K z z5BGwLnYumVUf13$r3Q0r;I@-H%e;!c6Zg;&ow4P?v%bvaR)_RlDcGyL<{ev5r*RAR zyAod-BUjB_CMxn>9DK_<Q^_fG zifV~WYcg8^R7sRs?3pOIu(~| zA(!SRBx`e5Yo!DyY$022mjMC;N#n5Hpf_4&b_K`5D+4w&%;Ufg2)$atNdb8jX|j;8 zkSpY(NVJ{G=5rRZVY!9S$ oD|?m!AYlSvy8{H*=$^!fNQd(@i3`W%0u})J|6)H z#F}HIaXVtP&S5A9IHE|a)yD;=E;NSp!vqK7p($4v5)NUJH!q9voVNh3XHBn6=z@;7H$^p5QiS{|t< z4FGBdt74;;WNzG$2O~imWiUIufY_suG^HCBYgBB^jAJ_^0|D+syra;Adv|~VN~MxT znY396^fQh)~&gyI^VR*Zx zLm)artC31V1)(U8k7l=;bhHh(kP&G>N1zo*Psd*Y9Vi^7&|PJ*O(L-6L>tk#^oB50%HP?XV?EtF81*26lnzi1=+yM(vfAvLGYsGs#zQx1zzvH zAkWXL7H(n*+Wc0PxPJ1=x|84MH{aJhGWeh9dLZqR_qRO_><?`idRl>TjRa zcBjoPk>A8GTvz);0mlnGUBZ1nJQUMg^Og6}a|t&Jr~j>THdJMl*K|}={NYNk=AmFm zKK0f0A6|6KpMCeeNp)rJb!7!ziMyYrV8Na5KR6sWC2U&9;@Tgdre@5}IPpnUb7+oF z&W4bNln}qE6*Kb7Hia~tPV~5Ozv9id-yV3~bJE1<^rPFKX7SMmBheC9Rdrk%^9{LaQiG{^d#`G{&? zS1SI|u<(?8?e?9^iLRe8Xw`nz-FMa$RX6DQ+iS@H&zy_##>gARC#SX?IMTLt&Ev%Z zXG-IrTUj%?!N>fQ`opHdeSvH8)^s=C>8&U#7i6cVstVfPI`%T`)q_QKxf}oae5$SM zca=MvKi(JfpT}RE@H~D|(f8!CzNu)}Y0j~Sf4aQ3wx@CHp|-o;@5+yyY05f1EA?h& z(~*tuotvc2hms?fh3glv)PdrO!u8PQT`L*_Jgoh!Rqf4<-(uxVhC#~cgsAdG@2~$a D?ctk* literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_1.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_10.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_1.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_10.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_15.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_15.png new file mode 100644 index 0000000000000000000000000000000000000000..679026c9ccb6c52071b51c8755fd150f8fcf965c GIT binary patch literal 4706 zcmeHKdsGuw8lQlL81=|Yr7C4kw1~hYlOfMB5imgr5~L7XtKCy4GZPrhi(~?U*7_Dj z>sqT-Dz0yjN0ilCpZHw0x~J^Q0gtvQph8#NRafnUbrIYlxVyNf}l~xR6`c%gSaLP0KX%Dd_{n+mY!p@8jUfK5Ig{+U>GFe zFrWzr*nR?k$RCWY1T6&E9ENM-1#j^ET3}D;u{Q!cx(DM2Y?;6h8U^|a&@fmBcH7KZ?n56a!xvRpY1tR$I$t0;9rv_mRk`tcXv(T9 zmcZDEnzVg0wWf(VifzwV9K0Iy0Qu|Oz2D4jZ-=H7Ji+L6UE@@rL*FGqjUelsTBRARh0thV#MI+9Gx8<%%8 z;z7rE1z%1{eER0ey4zj({#!aHpKZFiD<$!j(pAuSbEEFAJ21Qiqp}P4!pP1{sOfs! zR`-RwNNx4sem>+~S04!L&!#m(fu$FGpM7;}VN+xM_a_!DnA_PQm|Wy8wbZSS06FMm z%yZZ|rgWTi+9d?#w9pct-NhXkq)qU-2yy|8A>O9Phmn$E0Tx(L>~DJ!03nAt-Co^;|omUZD$X;D#;q$pnEbmvNC8jVJZVp0r4 z00Qw8J6OVpI6RX$if#@A?IGQai)EY+nBycYPA{tyi@`kHyFa_jWa^`LczRR-dPseQ zODdC~QoCI`(8I$f7Xp%=4gIQz$6V~9rCGGc>2;HIav|+tCk>>a$Uc9U*KOm&p-3rh zqwN6d0jtUeEy>-uJ`YZUT*mI=y@1$*kSt^E7i-YoxEVg2fg1tteY}Ivy|MFPfHIkI zgOl`f+cO$;Vs3q$a*_;%^CpT~2#bcaAf!q~BXR|)K-5Z1gD5aLMOz4s#-dgYpfWl< zEa4z&jtY=V7{H^nQW$VnA~Zz;3JoPk2*80@Q7qmfi>G7+W*I;++s%NiBy0nB#Zgg! zO0Kplh?{Wz;&VsmM{=34M_Xy zu(`Jm$D$~Xc5k1fGdU^7TKvDz+~t9_01u8lmGOY}i}|7M8;YIU<)4N<>4+lnADv zNR`H-z)-7ZFuTWTWs3+mos3?aWZvM6zu^21r>pprO@lYD+VuG0hU=Q2Jm_x0tIb7 zKC2Xr40olz?}4BZp8hplsu%~+Ne5!VJ!-gWtyupue+a#CQ@of!>D zVK!S<+Q=|w$4-mBt_2RBnveN3r7i2KKlt~)=HEAWeA3i@wmnoG<&Ix-;Kd&rqlGQc zE3OWYpLG<9eh%g22yPU>g!zoCYvN97ec`D~-`{!@D*WU0i;>S!mn!ncMjEk{4Qr#zPA-((nLh56x%|SO($Uk#=v!(l zbHA`Mo2MOYy;=O?%r}qcRUcpS_Vz}7{xAP}=_+5Ys%ludzdf!@3_11d?MO(*bZDk>i4YxL!avFYESMW=U)_nf>+G=5KSFFx4v8#l&woUr@V(FzH z44u2*@9aG46wWTm$ZT{JEPAZlbh^6rKOzq1oIq;T&A1-Eq3Ht|{eD&o_lO zoh;EPuk3xw#NG(oJF{`W!@n&EirC|y_9?zNSvwXL`GsRt<-EwP9g(eP>ho*5J|6?J O12QIO7^-H>U-}>Nn}MbP literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_2.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_20.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_2.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_20.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_greening_25.png new file mode 100644 index 0000000000000000000000000000000000000000..3b38257ca36b4c3b01665b0a83047dc0399cdb12 GIT binary patch literal 4722 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_2.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_20.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_2.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_20.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_25.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_25.png new file mode 100644 index 0000000000000000000000000000000000000000..50eb8942b62ccab57f43fbe7f7a7e60d3415bd26 GIT binary patch literal 4702 zcmeHKdsq`!7N4LcF%?B{D?X4h+N$UzGkGUXR3r!i0zp%yRJ%-OCNN4KOa>C@YN?3$ z?$)C9LDyE4ZL3nB+lm!ztt;!34=p}HLATn~ZSAg#U4-_|1Q5UN_wDERZT};gn|tQm zbAI>S-#z!hU2aW@4-t5RfrJ5EKIXa?s>J9t!$T zK%+|obJYNu`-u2&0+2Z5?mpgZvBGjGhDsG`;E&3cdQ=V`7}e^*gQ(pUb0`S%moI$Bm~eDj#L{b(iE+{5{Ls8Q>-%@NO4b|@$9)t! zzhrh~vb3Z4r4aLql@-@BJomEp{NsA-&K<=&U{Pn0_GRtF>V)OjUMzV5YtFrT^o?un zOj{(Zj85KH-C9(6#i8FAy6@mC*nRO^4$;_%KW?N>nP-Y?Zrm9ysJV8`zy8gpEfuM! zUmGY|_I0jfgZjtdA5T>}J|VPwY)g3R1~#TOzG~9w+FWV%;QfO}JU*FNHjUuLH56f9aQ%WOTF(#`&01d&BZbkb{2O zmd>VIlJ$h!DaA>*os#;U9&W=R!&JWqCl*jFY^Snlmr?S=p;`${lSauLl?AnUVySF8 zp^%|w6{gsT!UcqmluVr>Hu&`bz)7(7T>3q3liWoL9q(eW`G@}awycs3gEbGzBWImry>QhMFOqLAO>2xwwE|bd< zV1alGTrBQKT;8!9L;%A?c?pL0u(aC+bC|f@oyQs_5-<*T$LI7|EIsfpZx;)I51AkL z$S^4?b2??cJ-lpOJ^<+o=vO_wwgL|&n@M@yc??0tiv$AxD$8718OdeTkMq@EWELPe<5B!$@RmR9aN6!!bF5_JT6I zye#e_C=LpMOKE_EYjk#p163dnhuV%Pb(jvZtH9o>38fs>*hvz@uwD?U3=Ogp&*>c% z2SoxXtwydR>h=ss+JR4LW3bnRHZ|dO05FXsX$T? z3Z)iRVLFwP3eehv?sYp@AI?xQS%62t6-ZD1FBCk1->8XC$KuPTxLp8*A*dWY-Gphn z2$OX!SjN?i?iCwk|HX-cH|SMlfL}ldiWjJbvYukt#TkhAxBPVN#cw$U4EIg)O#JrI z)koJeG4M>rec9DV*E2EjOvZiL^?##F{Ny@CxxjxxK5$v8ezg8PxM&62lj2R_dLIUb zZvIPBDbNh?B)sZ{py$K6CV)1TM*yXeHCy6@w}XO2q9KW%S>J-&bi3IUW7D3@Tbq;} zo;Gx8?Y3Q}I~AK6_oyQdjEnd%cymdaZFlv67puyo+pQC`=d2J-k52sfjkM4$&ugmY zzPfm&J#B}1d01%fZaUU7e)aH8Q|d3Lm9JWcC&(+CfqT%sc z{N^|FN<%VVh#J1CMOSd<;niWr_MFf^>tcrWtNV0I%jGYc_NJuXym-f}=$wdWmlr;` zXzfqS2Op`d4o>>}o%br{-ko~v-7CW;kcnRmuQAQ>ChR->LECpmXz$4pcXo`49avve z`&?05OUlXXtA1}?DTW#buU>!Lmwi9Vv42cb=EHtBEU!Pj+x(@kVBNfz=fQ+C=IxqH zjnLImX+HGTmbJ;5(zi2g=^3*hO+JGf6{*bqnkHc`W=US=`RCmMXGV>LV6^Rb2R^da zmE8`XyF2KUomNr)d3D2~J@i*;Q#(Jtl$caGZsF(9t|^-3wlmrbf`nSJ=2*$Pk)JSc+ WoX8Lk8d48312V^@n5w2{F8&3^Wqn}) literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_3.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_30.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_3.png rename to src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_30.png diff --git a/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_35.png b/src/main/resources/assets/unicopia/textures/item/spectral_clock_ripe_35.png new file mode 100644 index 0000000000000000000000000000000000000000..e54583ea3cc6dbf994bfa54bcb93a8492090aad3 GIT binary patch literal 4685 zcmeHKdsGuw8lNCaOw}TED=NsGsKtVlnMsDcOaz()h!S8GDGPPmNoEoT@*RBK(|w(Az?inMCAM_gYmT2XxU;ByzcTh`sO;@%map6xk%JZJkK$=uv; z?)`ngd++al_k&xNo|-gKG))9S&_rXhei5jHxgs0~ek1?>gap+&I?HG^8lxd0cmYU3 zASB>0pa@3T0fI5m7|>P$N(ity3|FQJ#`1Lyu%{2$8-P7$022UgTo3?F1oc)>q`-aw z)Vo1pD*MN31~&H*^50}&am<5tlF^h7OC>lak;#ESCMDFE6g)7dRD(xK0JPx!;TdsE zg*(1G09aNrI$gR^r-R)dmzA;85ai#wY@IgwSbSK;^_>e0(W1g&->LMvhaZS*4~q=D zg7eBVBT^)f%U+sjTvNUMMz*`#cIf_%TYLAE?SX}TrOJ8Amz$ESuFo!i5$`Ctdh9pX zdp(aLU?Mu@lcrmxJKL@5O~IcuF2R2gy=fKB3|qU2ZZ$3vHQ# zJCid(xcYd3b))?Iao@M@bhtaBY7eg88?r4lZ6lj-D`|hsjH3mTrn%j%t2Y_Xh9<2( zVGfFkXkD-~QDX{U5?lAT^1Uac?;wB9{_xA}N8h0n?)1Dv@4qG&1jqPaWnX>Q(*EPNc&_=4x1&rcmHjV4a*kNo%Z%OABxrPlr0r z-reG9xq+Pftg~mIul8aPtUI#c8WdF7;$QdirDYx0E`Iyziu~-yy@FXqo=WqDst6E+ zekLP}%`&B^EiQ+Iq+DiN;&-^Y4TCgsem7~!r&-uc+Zd---1XT}G0ad}@vE^W%;eV5 zxlD42hhAKgnqeu)x2Py_+>KjQSx5)ZX?FQotym2D;lcSi+$Pfyz0*6O0?-5X zlWr83V5q}^j`Z-dhC)CxFrlCH@MaXdX><|ob@@CN+E7S4*_k6LD9ez)+vlII|XPYubPxFHWt0vqFS^Ikygr;sdT9Tw}Uxp6)Gaz;)BxDW9@g&tfx4+bcc zNv(HTeBAVmdaalnUro6z45j8xJXUF@q)H1ygE&A4oTd;Jfy)pXNvNy}N-2|LmJw7& zrxs|dY1YyPzrHYgx7E*9oH3Y(V=zpO^-rIpGr1_nTKvDz+~I*W01vi2nel@0i}|Mh6SbHw=zr^fwKM!t zf?@tps7Xuz61-#~-M?9YtG~;VOFC^d$Q}dXI@r$qL?}q*u{bWp6o^blsSv`f#1Rsc z6NuSNDKJW=AaFCyt1!gwby?XW(nBZMfQ~>b5T5*BD0mLPQ7=D@F3P33T>ye1m=rvN zg2fI9h7K$k<#NX0iZ$qeX`u&?e3*aiD2Il*7eg^j9=NtltM+bQ( zeMjXQmFt-lcqZ^@bdAdOObR>`cr?2HZ*qx-uT!)W{1;RNE=#BLitXT{HO{;+Ne`~~ zAyDv^KQ62UE#uwEzw$!RbD>-jK%2LQfyN-#Xfg!d4G0hlr|S2#e+_Qa_l^354CQIx z`ncRsGVIG0RS~-#Y{QOvBI}paCd@I&zR5h;uxjl-ajoER*)Q}#^=sE;Zw-t}*qvJU zaYOmqgzR&9Ip(j#iQYflFJNkBonLi45LZ<`Sax;xCEJgEe>&T>toiLrpU=>BNIg~pk=ixeH-rDW$;Sq6fx$**i$l1MDpc{>@(wlhI(#F>6G--8A zT7-6dYo}XdGcKvB@S{(taSfANOlL~7-g`s-C{i<_<*mDh3XQJewU9lp_ipNZ?!fKS zU)&~|PIkVsE5Eq?uw{ErLO^?@@T6C~bH`X?v2MwvM2YBf$nenqU_;cyLzMM{W|e%Nx`Il)W`M-(hm^j=VwQl zXYO+=de-z@$m|N=yZri_tD4`dxqE0?$K)#~UYyPtj&`l5H$+`cK*E*Bez;o@^Yso& zKC|f&G(lJ@xH5)a|7}g^!Ho{VHMn$AgnZ%D_A5`sonKY_^Fh;95D1XbkgDJR${Q>H E12d6#Gynhq literal 0 HcmV?d00001 From a0ebfbacce6615b99034ce47ab7fe6f3957b4efe Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 2 Feb 2024 08:23:13 +0000 Subject: [PATCH 96/98] Move the unintelligle stuff to translation strings for LingVarr --- .../resources/assets/unicopia/lang/en_us.json | 4 +++- .../spellbook/chapters/crystal_heart.json | 18 ++---------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index a607365d..5870c3ca 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -1114,7 +1114,9 @@ "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.title": "5th Mare '12", "gui.unicopia.spellbook.chapter.artefacts.crystal_heart.3.body": "Other accounts say that this artefact only functions when mounted on a specific pedestal of diamond blocks, like a beacon.", "gui.unicopia.spellbook.chapter.artefacts.torn_page.title": "Torn Page", - "gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body": "Building Materials:", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.1.body": "§kaaa§rential c§ka§rr§kaaa§rnial purpose§kaaa§r?", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body": "§kAasa sasa fwefsd q43rgfd wqklmsdfl as, klasn.§r", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.3.body": "Building Materials:", "gui.unicopia.spellbook.chapter.artefacts.crystal_podium.title": "Crystal Podium", "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.2.body": "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.", "gui.unicopia.spellbook.chapter.artefacts.dragon_breath_scroll.title": "2nd Hoof '12", diff --git a/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json b/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json index d16b5763..602110cb 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/crystal_heart.json @@ -42,23 +42,9 @@ "title": "gui.unicopia.spellbook.chapter.artefacts.torn_page.title", "level": 0, "elements": [ - { - "text": "", - "extra": [ - { "text": "aaa", "obfuscated": "true" }, - { "text": "ential c" }, - { "text": "a", "obfuscated": "true" }, - { "text": "r" }, - { "text": "aaa", "obfuscated": "true" }, - { "text": "nial purpose" }, - { "text": "aaa", "obfuscated": "true" }, - { "text": "?" } - ] - }, - { - "text": "Aasa sasa fwefsd q43rgfd wqklmsdfl as, klasn.", "obfuscated": "true" - }, + "gui.unicopia.spellbook.chapter.artefacts.torn_page.1.body", "gui.unicopia.spellbook.chapter.artefacts.torn_page.2.body", + "gui.unicopia.spellbook.chapter.artefacts.torn_page.3.body", { "ingredients": [ { "count": 2, "item": "minecraft:end_rod" }, From 28e64eebe1592117122f95c6f53d708a7256baff Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 2 Feb 2024 08:26:00 +0000 Subject: [PATCH 97/98] Fix incorrect translation string --- src/main/resources/assets/unicopia/lang/en_us.json | 2 +- .../resources/data/unicopia/spellbook/chapters/dark_magic.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 5870c3ca..90e2a220 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -1088,7 +1088,7 @@ "gui.unicopia.spellbook.chapter.dark_magic.p27.3.body": "I feel myself being drained any time I'm around it. Is it... feeding on me?", "gui.unicopia.spellbook.chapter.dark_magic.p27.4.body": "No, it couldn't be.", "gui.unicopia.spellbook.chapter.dark_magic.p28.1.body": "I found this incantation under some old notes whilst clearing out the lab. It's... simplistic and hard to manage, but it gets the job done.", - "gui.unicopia.spellbook.chapter.dark_magic.p27.2.body": "* Add the power trait to increase the effect's range", + "gui.unicopia.spellbook.chapter.dark_magic.dispel_evil.modifier.1": "* Add the power trait to increase the effect's range", "gui.unicopia.spellbook.chapter.otherworldly.p1.title": "Ch.6 The Beyond", "gui.unicopia.spellbook.chapter.otherworldly.p2.title": "2nd Croptober '12", diff --git a/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json b/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json index ff69df04..fb4e0968 100644 --- a/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json +++ b/src/main/resources/data/unicopia/spellbook/chapters/dark_magic.json @@ -391,7 +391,7 @@ { "count": 1, "trait": "unicopia:power" } ] }, - "gui.unicopia.spellbook.chapter.dark_magic.p27.2.body" + "gui.unicopia.spellbook.chapter.dark_magic.dispel_evil.modifier.1" ] } ] From 5a5ec8b24c3b24b295172c2a79e8cbd249b26bd7 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 2 Feb 2024 21:04:20 +0000 Subject: [PATCH 98/98] Implement method to obtain the spectral clock using the altar --- .../spell/crafting/AltarRecipeMatch.java | 42 ++++++ .../entity/SpellbookEntityRenderer.java | 87 +++++++++++ .../unicopia/entity/mob/SpellbookEntity.java | 140 ++++++++++++++---- 3 files changed, 238 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/AltarRecipeMatch.java diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/AltarRecipeMatch.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/AltarRecipeMatch.java new file mode 100644 index 00000000..820f7667 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/AltarRecipeMatch.java @@ -0,0 +1,42 @@ +package com.minelittlepony.unicopia.ability.magic.spell.crafting; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.item.UItems; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +public record AltarRecipeMatch( + ItemEntity target, + List ingredients, + ItemStack result + ) { + + @Nullable + public static AltarRecipeMatch of(List inputs) { + ItemEntity clock = inputs.stream().filter(item -> item.getStack().isOf(Items.CLOCK)).findFirst().orElse(null); + + if (clock != null) { + return new AltarRecipeMatch(clock, List.of(), UItems.SPECTRAL_CLOCK.getDefaultStack()); + } + + return null; + } + + public boolean isRemoved() { + return target.isRemoved() || ingredients.stream().anyMatch(ItemEntity::isRemoved); + } + + public void craft() { + ItemStack clockStack = result.copyWithCount(target.getStack().getCount()); + clockStack.setNbt(target.getStack().getNbt()); + target.setStack(clockStack); + target.setInvulnerable(true); + ingredients.forEach(Entity::discard); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/SpellbookEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/SpellbookEntityRenderer.java index f04a5494..ceac4e95 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/SpellbookEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/SpellbookEntityRenderer.java @@ -1,19 +1,31 @@ package com.minelittlepony.unicopia.client.render.entity; +import org.joml.Matrix3f; +import org.joml.Matrix4f; + import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.mob.SpellbookEntity; +import com.minelittlepony.unicopia.server.world.Altar; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRendererFactory; import net.minecraft.client.render.entity.LivingEntityRenderer; +import net.minecraft.client.render.entity.feature.FeatureRenderer; +import net.minecraft.client.render.entity.feature.FeatureRendererContext; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; import net.minecraft.util.math.*; public class SpellbookEntityRenderer extends LivingEntityRenderer { private static final Identifier TEXTURE = Unicopia.id("textures/entity/spellbook/normal.png"); + private static final Identifier ALTAR_BEAM_TEXTURE = new Identifier("textures/entity/end_crystal/end_crystal_beam.png"); public SpellbookEntityRenderer(EntityRendererFactory.Context context) { super(context, new SpellbookModel(SpellbookModel.getTexturedModelData().createModel()), 0); + addFeature(new AltarBeamFeature(this)); } @Override @@ -51,4 +63,79 @@ public class SpellbookEntityRenderer extends LivingEntityRenderer { + public AltarBeamFeature(FeatureRendererContext context) { + super(context); + } + + @Override + public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, SpellbookEntity entity, float limbPos, float limbSpeed, float tickDelta, float animationProgress, float yaw, float pitch) { + if (!entity.hasBeams()) { + return; + } + + matrices.peek(); + matrices.pop(); + matrices.push(); + + + Altar altar = entity.getAltar().get(); + Vec3d center = altar.origin().toCenterPos(); + + float x = (float)MathHelper.lerp(tickDelta, entity.prevX, entity.getX()); + float y = (float)MathHelper.lerp(tickDelta, entity.prevY, entity.getY()); + float z = (float)MathHelper.lerp(tickDelta, entity.prevZ, entity.getZ()); + Vec3d bookPos = new Vec3d(x, y, z); + Vec3d shift = bookPos.subtract(center); + + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(180)); + matrices.translate(shift.x, shift.y - 1, shift.z); + + for (BlockPos pillar : altar.pillars()) { + renderBeam(center.subtract(pillar.toCenterPos()), -tickDelta, -entity.age, matrices, vertices, light, 1, 0, 1); + } + + matrices.pop(); + } + + public static float getYOffset(float animationProgress) { + animationProgress = MathHelper.sin(animationProgress * 0.2F) * 0.5F + 0.5F; + return ((animationProgress * animationProgress + animationProgress) * 0.4F) - 1.4F; + } + } + + public static void renderBeam(Vec3d offset, float tickDelta, int age, MatrixStack matrices, VertexConsumerProvider buffers, int light, float r, float g, float b) { + final float horizontalDistance = (float)offset.horizontalLength(); + final float distance = (float)offset.length(); + matrices.push(); + matrices.multiply(RotationAxis.POSITIVE_Y.rotation((float)(-Math.atan2(offset.z, offset.x)) - 1.5707964f)); + matrices.multiply(RotationAxis.POSITIVE_X.rotation((float)(-Math.atan2(horizontalDistance, offset.y)) - 1.5707964f)); + VertexConsumer buffer = buffers.getBuffer(RenderLayer.getEntityTranslucent(ALTAR_BEAM_TEXTURE)); + final float minV = -(age + tickDelta) * 0.01f; + final float maxV = minV + (distance / 32F); + final int sides = 8; + final float diameter = 0.35F; + float segmentX = 0; + float segmentY = diameter; + float minU = 0; + MatrixStack.Entry entry = matrices.peek(); + Matrix4f positionMat = entry.getPositionMatrix(); + Matrix3f normalMat = entry.getNormalMatrix(); + + for (int i = 1; i <= sides; i++) { + float o = MathHelper.sin(i * MathHelper.TAU / sides) * diameter; + float p = MathHelper.cos(i * MathHelper.TAU / sides) * diameter; + float maxU = i / (float)sides; + buffer.vertex(positionMat, segmentX * 0.2F, segmentY * 0.2F, 0).color(0, 0, 0, 255).texture(minU, minV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next(); + buffer.vertex(positionMat, segmentX, segmentY, distance).color(r, g, b, 1).texture(minU, maxV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next(); + buffer.vertex(positionMat, o, p, distance).color(r, g, b, 1).texture(maxU, maxV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next(); + buffer.vertex(positionMat, o * 0.2F, p * 0.2F, 0).color(0, 0, 0, 255).texture(maxU, minV).overlay(OverlayTexture.DEFAULT_UV).light(light).normal(normalMat, 0, -1, 0).next(); + segmentX = o; + segmentY = p; + minU = maxU; + } + matrices.pop(); + } } \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java index eb97f159..a09abfc8 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java @@ -2,9 +2,12 @@ package com.minelittlepony.unicopia.entity.mob; import java.util.Optional; +import org.jetbrains.annotations.Nullable; + import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.UTags; +import com.minelittlepony.unicopia.ability.magic.spell.crafting.AltarRecipeMatch; import com.minelittlepony.unicopia.container.SpellbookScreenHandler; import com.minelittlepony.unicopia.container.SpellbookState; import com.minelittlepony.unicopia.entity.MagicImmune; @@ -19,6 +22,7 @@ import net.fabricmc.fabric.api.util.TriState; import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData; @@ -30,6 +34,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.PacketByteBuf; import net.minecraft.particle.ParticleTypes; +import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.screen.*; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; @@ -38,13 +43,18 @@ import net.minecraft.sound.SoundCategory; import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameRules; import net.minecraft.world.World; +import net.minecraft.world.World.ExplosionSourceType; public class SpellbookEntity extends MobEntity implements MagicImmune { private static final TrackedData LOCKED = DataTracker.registerData(SpellbookEntity.class, TrackedDataHandlerRegistry.BYTE); private static final TrackedData ALTERED = DataTracker.registerData(SpellbookEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final byte ALTAR_BEAMS_START = 61; + private static final byte ALTAR_BEAMS_END = 62; private static final int TICKS_TO_SLEEP = 600; @@ -55,6 +65,12 @@ public class SpellbookEntity extends MobEntity implements MagicImmune { private Optional altar = Optional.empty(); + private boolean hasBeams; + private int beamsActive; + + @Nullable + private AltarRecipeMatch activeRecipe; + public SpellbookEntity(EntityType type, World world) { super(type, world); setPersistent(); @@ -104,6 +120,14 @@ public class SpellbookEntity extends MobEntity implements MagicImmune { this.altar = Optional.of(altar); } + public Optional getAltar() { + return altar; + } + + public boolean hasBeams() { + return hasBeams && altar.isPresent(); + } + public boolean isAltered() { return dataTracker.get(ALTERED); } @@ -205,44 +229,83 @@ public class SpellbookEntity extends MobEntity implements MagicImmune { return false; } - altar.pillars().forEach(pillar -> { - Vec3d center = pillar.toCenterPos().add( - random.nextTriangular(0.5, 0.2), - random.nextTriangular(0.5, 0.2), - random.nextTriangular(0.5, 0.2) - ); + tickAltarCrafting(altar); - ((ServerWorld)getWorld()).spawnParticles( - ParticleTypes.SOUL_FIRE_FLAME, - center.x - 0.5, center.y + 0.5, center.z - 0.5, - 0, - 0.5, 0.5, 0.5, 0); - - if (random.nextInt(12) != 0) { - return; - } - - Vec3d vel = center.subtract(this.altar.get().origin().toCenterPos()).normalize(); - - ((ServerWorld)getWorld()).spawnParticles( - ParticleTypes.SOUL_FIRE_FLAME, - center.x - 0.5, center.y + 0.5, center.z - 0.5, - 0, - vel.x, vel.y, vel.z, -0.2); - - if (random.nextInt(2000) == 0) { - if (getWorld().getBlockState(pillar).isOf(Blocks.CRYING_OBSIDIAN)) { - pillar = pillar.down(); - } - getWorld().setBlockState(pillar, Blocks.CRYING_OBSIDIAN.getDefaultState()); - } - }); + Vec3d origin = altar.origin().toCenterPos(); + altar.pillars().forEach(pillar -> tickAltarPillar(origin, pillar)); return true; }); } } + public void setBeamTicks(int ticks) { + getWorld().sendEntityStatus(this, ticks > 0 ? ALTAR_BEAMS_START : ALTAR_BEAMS_END); + beamsActive = ticks; + } + + private void tickAltarCrafting(Altar altar) { + if (activeRecipe == null || activeRecipe.isRemoved()) { + activeRecipe = AltarRecipeMatch.of(getWorld().getEntitiesByClass(ItemEntity.class, Box.of(altar.origin().toCenterPos(), 2, 2, 2), EntityPredicates.VALID_ENTITY)); + + if (activeRecipe != null) { + setBeamTicks(5); + } + } + + if (beamsActive <= 0) { + return; + } + + if (--beamsActive > 0) { + playSound(USounds.Vanilla.ENTITY_GUARDIAN_ATTACK, 1.5F, 0.5F); + return; + } + + //setBeamTicks(0); + + if (activeRecipe == null) { + return; + } + + activeRecipe.craft(); + activeRecipe = null; + getWorld().createExplosion(this, altar.origin().getX(), altar.origin().getY(), altar.origin().getZ(), 0, ExplosionSourceType.NONE); + } + + private void tickAltarPillar(Vec3d origin, BlockPos pillar) { + Vec3d center = pillar.toCenterPos().add( + random.nextTriangular(0.5, 0.2), + random.nextTriangular(0.5, 0.2), + random.nextTriangular(0.5, 0.2) + ); + + ((ServerWorld)getWorld()).spawnParticles( + ParticleTypes.SOUL_FIRE_FLAME, + center.x - 0.5, center.y + 0.5, center.z - 0.5, + 0, + 0.5, 0.5, 0.5, 0); + + if (random.nextInt(12) != 0) { + return; + } + + Vec3d vel = center.subtract(origin).normalize(); + + ((ServerWorld)getWorld()).spawnParticles( + ParticleTypes.SOUL_FIRE_FLAME, + center.x - 0.5, center.y + 0.5, center.z - 0.5, + 0, + vel.x, vel.y, vel.z, -0.2); + + if (random.nextInt(2000) == 0) { + if (getWorld().getBlockState(pillar).isOf(Blocks.CRYING_OBSIDIAN)) { + pillar = pillar.down(); + } + getWorld().setBlockState(pillar, Blocks.CRYING_OBSIDIAN.getDefaultState()); + } + } + private boolean shouldBeSleeping() { return MeteorlogicalUtil.getSkyAngle(getWorld()) > 1 && activeTicks <= 0; } @@ -337,4 +400,19 @@ public class SpellbookEntity extends MobEntity implements MagicImmune { }); Altar.SERIALIZER.writeOptional("altar", compound, altar); } + + @Override + public void handleStatus(byte status) { + switch (status) { + case ALTAR_BEAMS_START: + altar = Altar.locateAltar(getWorld(), getBlockPos()); + hasBeams = altar.isPresent(); + break; + case ALTAR_BEAMS_END: + altar = Optional.empty(); + hasBeams = false; + default: + super.handleStatus(status); + } + } }