From 34bdd7f135bb54fcdaf841a31ba30a889b61f571 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 5 Apr 2024 23:32:14 +0100 Subject: [PATCH 01/32] Set rock stew's max count to 1 --- src/main/java/com/minelittlepony/unicopia/item/UItems.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index 5e69e0d6..b9495246 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -111,7 +111,7 @@ public interface UItems { Item TOM = register("tom", new BluntWeaponItem(new Item.Settings(), ImmutableMultimap.of( EntityAttributes.GENERIC_KNOCKBACK_RESISTANCE, new EntityAttributeModifier(BluntWeaponItem.KNOCKBACK_MODIFIER_ID, "Weapon modifier", 0.9, EntityAttributeModifier.Operation.ADDITION) )), ItemGroups.NATURAL); - Item ROCK_STEW = register("rock_stew", new StewItem(new Item.Settings().food(FoodComponents.MUSHROOM_STEW).recipeRemainder(Items.BOWL)), ItemGroups.FOOD_AND_DRINK); + Item ROCK_STEW = register("rock_stew", new StewItem(new Item.Settings().food(FoodComponents.MUSHROOM_STEW).maxCount(1).recipeRemainder(Items.BOWL)), ItemGroups.FOOD_AND_DRINK); Item ROCK_CANDY = register("rock_candy", new Item(new Item.Settings().food(UFoodComponents.CANDY).maxCount(16)), ItemGroups.FOOD_AND_DRINK); Item SALT_CUBE = register("salt_cube", new Item(new Item.Settings().food(UFoodComponents.SALT_CUBE)), ItemGroups.FOOD_AND_DRINK); From 7140ce6d0ee2a6fb7da86f5a8a2a74fcb32354a1 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 5 Apr 2024 23:41:32 +0100 Subject: [PATCH 02/32] Return the empty container when eating stacked food with containers --- .../unicopia/item/ConsumableItem.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java b/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java index dba7218d..747115e1 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java @@ -4,8 +4,10 @@ import java.util.Optional; import net.minecraft.advancement.criterion.Criteria; import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUsage; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.stat.Stats; import net.minecraft.util.UseAction; @@ -29,7 +31,16 @@ public class ConsumableItem extends Item { serverPlayerEntity.incrementStat(Stats.USED.getOrCreateStat(this)); } - return stack.isEmpty() ? Optional.ofNullable(getRecipeRemainder()).map(Item::getDefaultStack).orElse(ItemStack.EMPTY) : stack; + if (stack.isEmpty()) { + return stack.isEmpty() ? Optional.ofNullable(getRecipeRemainder()).map(Item::getDefaultStack).orElse(ItemStack.EMPTY) : stack; + } + + if (user instanceof PlayerEntity player) { + return Optional.ofNullable(getRecipeRemainder()).map(Item::getDefaultStack).map(remainder -> { + return ItemUsage.exchangeStack(stack, player, remainder); + }).orElse(stack); + } + return stack; } @Override From ca2be7ae8504ec2f281eee10385f9f50e44c5249 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 5 Apr 2024 23:41:51 +0100 Subject: [PATCH 03/32] Set the max count for fried axolotl to 1 (same as other bucket foods) --- src/main/java/com/minelittlepony/unicopia/item/UItems.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java index b9495246..9ec070b3 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java +++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java @@ -235,7 +235,7 @@ public interface UItems { Item COOKED_TROPICAL_FISH = register("cooked_tropical_fish", new Item(new Item.Settings().food(FoodComponents.COOKED_COD)), ItemGroups.FOOD_AND_DRINK); Item COOKED_PUFFERFISH = register("cooked_pufferfish", new Item(new Item.Settings().food(FoodComponents.COOKED_COD)), ItemGroups.FOOD_AND_DRINK); - Item FRIED_AXOLOTL = register("fried_axolotl", new ConsumableItem(new Item.Settings().food(FoodComponents.COOKED_CHICKEN).recipeRemainder(Items.BUCKET), UseAction.EAT), ItemGroups.FOOD_AND_DRINK); + Item FRIED_AXOLOTL = register("fried_axolotl", new ConsumableItem(new Item.Settings().food(FoodComponents.COOKED_CHICKEN).maxCount(1).recipeRemainder(Items.BUCKET), UseAction.EAT), ItemGroups.FOOD_AND_DRINK); Item GREEN_FRIED_EGG = register("green_fried_egg", new Item(new Item.Settings().food(UFoodComponents.FRIED_EGG)), ItemGroups.FOOD_AND_DRINK); Item CARAPACE = register("carapace", new Item(new Item.Settings()), ItemGroups.INGREDIENTS); From ea12ca7f24c635a48955f2d647c25bf5348db829 Mon Sep 17 00:00:00 2001 From: LingVarr <104311317+LingVarr@users.noreply.github.com> Date: Sun, 7 Apr 2024 01:10:21 +1100 Subject: [PATCH 04/32] Update ru_ru.json (#318) --- .../resources/assets/unicopia/lang/ru_ru.json | 183 +++++++++++++----- 1 file changed, 132 insertions(+), 51 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/ru_ru.json b/src/main/resources/assets/unicopia/lang/ru_ru.json index 00fc3c1d..e341a1d4 100644 --- a/src/main/resources/assets/unicopia/lang/ru_ru.json +++ b/src/main/resources/assets/unicopia/lang/ru_ru.json @@ -74,6 +74,14 @@ "item.unicopia.cooked_zap_apple": "Печёное зап-яблоко", "item.unicopia.zap_apple": "Зап-яблоко", "item.unicopia.zap_bulb": "Недозрелое зап-яблоко", + "item.unicopia.rotten_cod": "Гнилая треска", + "item.unicopia.rotten_salmon": "Гнилой лосось", + "item.unicopia.rotten_tropical_fish": "Гнилая тропическая рыба", + "item.unicopia.rotten_pufferfish": "Гнилой иглобрюх", + "item.unicopia.cooked_tropical_fish": "Приготовленная тропическая рыба", + "item.unicopia.cooked_pufferfish": "Приготовленный иглобрюх", + "item.unicopia.fried_axolotl": "Жареный аксолотль", + "item.unicopia.green_fried_egg": "Жареное зелёное яйцо", "item.unicopia.love_bottle": "Бутылочка любви", "item.unicopia.love_bucket": "Ведро любви", @@ -81,11 +89,16 @@ "item.unicopia.plunder_vine": "Чёрная лоза", "item.unicopia.empty_jar": "Стеклянная банка", + "block.unicopia.jar": "Стеклянная банка", "item.unicopia.filled_jar": "%s в банке", "item.unicopia.rain_cloud_jar": "Дождь в банке", + "item.unicopia.cloud_jar": "Дождь в банке", "item.unicopia.storm_cloud_jar": "Буря в банке", + "block.unicopia.storm_jar": "Буря в банке", "item.unicopia.lightning_jar": "Молния в банке", + "block.unicopia.lightning_jar": "Молния в банке", "item.unicopia.zap_apple_jam_jar": "Джем из зап-яблока", + "block.unicopia.zap_jar": "Банка зап-яблочного джема", "item.unicopia.toast": "Тост", "item.unicopia.burned_toast": "Подгоревший тост", @@ -145,7 +158,13 @@ "item.unicopia.crispy_hay_fries": "Хрустящий картофель фри", "item.unicopia.horse_shoe_fries": "Подкова из картофеля фри", "item.unicopia.wheat_worms": "Пшеничные черви", + "item.unicopia.baited_fishing_rod": "Удочка с приманкой", "item.unicopia.muffin": "Маффин", + "item.unicopia.scone": "Булочка", + "item.unicopia.oatmeal_cookie": "Овсяное печенье", + "item.unicopia.chocolate_oatmeal_cookie": "Шоколадное овсяное печенье", + "item.unicopia.pinecone_cookie": "Печенье из шишек", + "item.unicopia.bowl_of_nuts": "Миска с орехами", "item.unicopia.pegasus_amulet": "Крылья Икара", "item.unicopia.pegasus_amulet.lore": "Дарует временный полёт тому, кто носит его", @@ -197,6 +216,7 @@ "item.unicopia.music_disc_funk.desc": "Фанк, просто фанк", "item.unicopia.cloud_lump": "Облачный ком", + "item.unicopia.white_bed_sheets": "Белая простынь", "item.unicopia.light_gray_bed_sheets": "Светло-серая простынь", "item.unicopia.gray_bed_sheets": "Серая простынь", "item.unicopia.black_bed_sheets": "Черная простынь", @@ -279,6 +299,7 @@ "block.unicopia.golden_oak_leaves": "Листья золотого дуба", "block.unicopia.golden_oak_log": "Бревно золотого дуба", "block.unicopia.mango": "Манго", + "block.unicopia.worm_block": "Блок червей", "block.unicopia.mango_leaves": "Листья мангового дерева", "block.unicopia.mango_sapling": "Саженец мангового дерева", "block.unicopia.potted_mango_sapling": "Саженец манго в горшке", @@ -353,7 +374,8 @@ "block.unicopia.oats_crown": "Овёс", "entity.unicopia.butterfly": "Бабочка", - "entity.unicopia.twittermite": "Твиттермиты", + "entity.unicopia.twittermite": "Твиттермит", + "entity.unicopia.specter": "Призрак", "entity.unicopia.cast_spell": "Заклинание", "entity.unicopia.cast_spell.by": "Заклинание, наложенное %s", "entity.unicopia.spellbook": "Книга заклинаний", @@ -605,42 +627,43 @@ "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.nuts_and_seeds": "Орехи и семена", - "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.fruit": "Фрукты", - "tag.unicopia.food_types.baked_goods": "Выпечка", - "tag.unicopia.food_types.misc": "Прочее", - "tag.unicopia.food_types.fruits_and_vegetables": "Фрукты и овощи", - "tag.unicopia.food_types.drinks": "Напитки", - "tag.minecraft.leaves": "Листья", - - "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": "Токсичное", + "food_group.unicopia.meat.rotten": "Гниющее мясо", + "food_group.unicopia.meat.raw": "Свежее мясо", + "food_group.unicopia.meat.cooked": "Готовое мясо", + "food_group.unicopia.fish.rotten": "Гнилая рыба", + "food_group.unicopia.fish.raw": "Свежая рыба", + "food_group.unicopia.fish.cooked": "Готовая рыба", + "food_group.unicopia.insect.rotten": "Гнилые жуки и насекомые", + "food_group.unicopia.insect.raw": "Жуки и насекомые", + "food_group.unicopia.insect.cooked": "Приготовленные жуки и насекомые", + "food_group.unicopia.nuts_and_seeds": "Орехи и семена", + "food_group.unicopia.love": "Любовь", + "food_group.unicopia.rocks": "Камни", + "food_group.unicopia.pinecone": "Орехи и семена", + "food_group.unicopia.bat_ponys_delight": "Лакомства бэтпони", + "food_group.unicopia.sea_vegetable.cooked": "Подготовленные ракушки и кораллы", + "food_group.unicopia.sea_vegetable.raw": "Ракушки и кораллы", + "food_group.unicopia.shells": "Морские ракушки", + "food_group.unicopia.special_shells": "Компаньоны", + "food_group.unicopia.candy": "Конфеты", + "food_group.unicopia.desserts": "Десерты", + "food_group.unicopia.fruit": "Фрукты", + "food_group.unicopia.baked_goods": "Выпечка", + "food_group.unicopia.misc": "Прочее", + "food_group.unicopia.fruits_and_vegetables": "Фрукты и овощи", + "food_group.unicopia.drinks": "Напитки", + "food_group.unicopia.foraging.edible_filling": "Крупная растительная масса", + "food_group.unicopia.foraging.edible": "Растительная масса", + "food_group.unicopia.foraging.nauseating": "Тошнотворное", + "food_group.unicopia.foraging.prickly": "Колючее", + "food_group.unicopia.foraging.risky": "Небезопасное", + "food_group.unicopia.foraging.strengthening": "Повышающее силу", + "food_group.unicopia.foraging.severely_prickly": "Очень колючее", + "food_group.unicopia.foraging.severely_nauseating": "Отвратительное", + "food_group.unicopia.foraging.radioactive": "Светящееся", + "food_group.unicopia.foraging.dangerous": "Опасное", + "food_group.unicopia.foraging.blinding": "Токсичное", + "food_group.unicopia.foraging.leafy_greens": "Листовая зелень", "toxicity.safe.name": "Безопасное", "toxicity.mild.name": "Слаботоксичное", @@ -1332,6 +1355,8 @@ "enchantment.unicopia.heart_bound.desc": "Заставляет предмет оставаться с вами после смерти", "enchantment.unicopia.consumption": "Потребление", "enchantment.unicopia.consumption.desc": "Преобразует предметы, добытые с помощью инструмента, в опыт", + "enchantment.unicopia.feather_touch": "Касание пера", + "enchantment.unicopia.feather_touch.desc": "Позволяет ломать и размещать облачные блоки при держании", "commands.race.success.self": "Изменена раса на %1$s.", "commands.race.success": "%1$s изменил расу на %2$s.", @@ -1380,6 +1405,7 @@ "commands.gravity.set": "Ваша гравитация была обновлена до %f", "commands.gravity.set.self": "Установить собственную гравитацию на %f", "commands.gravity.set.other": "Установить гравитацию %s на %f", + "commands.gravity.set.multiple": "Обновлено %s сущностей", "unicopia.options.title": "Опции Unicopia", "unicopia.options.ignore_mine_lp": "Игнорировать Mine Little Pony", @@ -1592,13 +1618,23 @@ "advancements.unicopia.praise_the_sun.title": "Хвала Солнцу!", "advancements.unicopia.praise_the_sun.description": "Испытайте безудержную славу Селестии", "advancements.unicopia.cool_potato.title": "Крутая картошка", - "advancements.unicopia.cool_potato.description": "Защитите глаза от солнца", + "advancements.unicopia.cool_potato.description": "Защитите свои глаза от солнца", + "advancements.unicopia.take_a_note.title": "Записывай, Спайк", + "advancements.unicopia.take_a_note.description": "Получите свиток дыхания дракона", + "advancements.unicopia.dear_princess.title": "Дорогая принцесса...", + "advancements.unicopia.dear_princess.description": "Отправьте письмо с помощью свитка дыхания дракона", + "advancements.unicopia.i_await_your_reply.title": "Я жду твоего ответа", + "advancements.unicopia.i_await_your_reply.description": "Используйте свиток дыхания дракона, чтобы отправить кому-нибудь свиток дыхания дракона", "advancements.unicopia.baked_bads.title": "Запечённые хлебцы", "advancements.unicopia.baked_bads.description": "Испеките вкусный маффин", "advancements.unicopia.mid_flight_interruption.title": "Побеспокоили в середине полёта", "advancements.unicopia.mid_flight_interruption.description": "Получите удар молнии во время полёта в грозу", - "advancements.unicopia.lightning_bug.title": "Ошибка молнии", - "advancements.unicopia.lightning_bug.description": "Привлеките 10 ударов молний", + "advancements.unicopia.lightning_bug.title": "Баг молнии", + "advancements.unicopia.lightning_bug.description": "Привлеките 10 ударов молнии за чейнджлинга", + "advancements.unicopia.wonder_bolt.title": "Вондерболт", + "advancements.unicopia.wonder_bolt.description": "Привлеките 10 ударов молнии", + "advancements.unicopia.bait.title": "Это приманка?", + "advancements.unicopia.bait.description": "Насадите несколько червей на крючок", "advancements.unicopia.jar.title": "О, ничего себе. Что это?", "advancements.unicopia.jar.description": "Найдите пустую банку", "advancements.unicopia.gotcha.title": "Попался!", @@ -1606,11 +1642,17 @@ "advancements.unicopia.trick_apple.title": "Яблоко раздора", "advancements.unicopia.trick_apple.description": "Найдите своё первое зап-яблоко", "advancements.unicopia.feed_trick_apple.title": "Вот, попробуй это", - "advancements.unicopia.feed_trick_apple.description": "Скорми зап-яблоко мобу", + "advancements.unicopia.feed_trick_apple.description": "Скормите зап-яблоко мобу", "advancements.unicopia.eat_trick_apple.title": "Хрустящий", "advancements.unicopia.eat_trick_apple.description": "Кусните зап-яблоко", "advancements.unicopia.eat_pinecone.title": "Отчаяние", "advancements.unicopia.eat_pinecone.description": "Съешьте шишку", + "advancements.unicopia.tastes_like_chicken.title": "На вкус как курица", + "advancements.unicopia.tastes_like_chicken.description": "Зажарьте и съешьте аксолотля", + "advancements.unicopia.what_the_hay.title": "Какого сена?", + "advancements.unicopia.what_the_hay.description": "Съешьте целый блок сена", + "advancements.unicopia.oats_so_easy.title": "Овёс - это просто", + "advancements.unicopia.oats_so_easy.description": "Вырастите немного овса", "advancements.unicopia.imported_oats.title": "Столь же вкусные, сколь и дорогие", "advancements.unicopia.imported_oats.description": "Отправьте или получите шикарный импортный овёс", @@ -1640,11 +1682,15 @@ "advancements.unicopia.sweet_apple_acres.description": "Получите по одному яблоку каждого сорта", "advancements.unicopia.brew_cider.title": "Лучшее от Эпплджек", "advancements.unicopia.brew_cider.description": "Сварить сидр", + "advancements.unicopia.basket_case.title": "Дело о корзине", + "advancements.unicopia.basket_case.description": "Сплетите корзину", + "advancements.unicopia.aeronaut.title": "Аэронавт", + "advancements.unicopia.aeronaut.description": "Оснастите свою корзину фонарём и воздушным шаром", "advancements.unicopia.travelling_in_style.title": "Путешествие со стилем", "advancements.unicopia.travelling_in_style.description": "Прокатитесь на воздушном шаре", - "advancements.unicopia.night_route.title": "Дети Ночи", - "advancements.unicopia.night_route.description": "Пойдите по пути ночи", + "advancements.unicopia.bat_route.title": "Дети Ночи", + "advancements.unicopia.bat_route.description": "Пойдите по пути ночи", "advancements.unicopia.screech_twenty_mobs.title": "Ужас с неба", "advancements.unicopia.screech_twenty_mobs.description": "Обрушьте ужас по меньшей мере на 20 мобов одновременно", "advancements.unicopia.screech_self.title": "Божечки!", @@ -1657,10 +1703,18 @@ "advancements.unicopia.blasphemy.title": "Кощунство!", "advancements.unicopia.blasphemy.description": "Ударьте Селестию по башке. Упс!", - "advancements.unicopia.earth_route.title": "Путь Пони", + "advancements.unicopia.earth_route.title": "Очаг Земли", "advancements.unicopia.earth_route.description": "Вступить в клан Яблока", "advancements.unicopia.sticks_and_stones.title": "Палки и камни", "advancements.unicopia.sticks_and_stones.description": "Убейте моба, бросая в него камни", + "advancements.unicopia.blacksmith.title": "Кузнец", + "advancements.unicopia.blacksmith.description": "Сделайте подкову", + "advancements.unicopia.change_of_shoes.title": "Переобулся", + "advancements.unicopia.change_of_shoes.description": "Сделайте железную подкову", + "advancements.unicopia.fashionably_expensive.title": "Модно и дорого", + "advancements.unicopia.fashionably_expensive.description": "Обновитесь до золотых подков", + "advancements.unicopia.overkill.title": "Оверкилл", + "advancements.unicopia.overkill.description": "Создайте незеритовую подкову", "advancements.unicopia.dead_ringer.title": "Звон смерти", "advancements.unicopia.dead_ringer.description": "Убейте моба подковой", "advancements.unicopia.born_on_a_rock_farm.title": "Рождённый на Ферме Камней", @@ -1668,14 +1722,32 @@ "advancements.unicopia.thats_unusual.title": "Это необычно", "advancements.unicopia.thats_unusual.description": "Но что оно делает?", - "advancements.unicopia.sky_route.title": "Путь Пегаса", - "advancements.unicopia.sky_route.description": "Присоединяйтесь к пегасам Клаудсдейла", + "advancements.unicopia.pegasus_route.title": "Путь Пегаса", + "advancements.unicopia.pegasus_route.description": "Присоединитесь к пегасам Клаудсдейла", "advancements.unicopia.molting_season_1.title": "Сезон линьки", - "advancements.unicopia.molting_season_1.description": "Сбросьте перо во время полёта", + "advancements.unicopia.molting_season_1.description": "Сбросьте своё первое перо во время полёта", "advancements.unicopia.molting_season_2.title": "Сезон линьки 2", - "advancements.unicopia.molting_season_2.description": "Сбросьте 5 перьев во время полёта", + "advancements.unicopia.molting_season_2.description": "Сбросьте своё второе перо во время полёта", "advancements.unicopia.molting_season_3.title": "Сезон линьки 3", - "advancements.unicopia.molting_season_3.description": "Сбросьте 15 перьев во время полёта", + "advancements.unicopia.molting_season_3.description": "Сбросьте своё четвёртое перо во время полёта", + "advancements.unicopia.molting_season_4.title": "Сезон линьки 4", + "advancements.unicopia.molting_season_4.description": "Сбросьте своё восьмое перо во время полёта", + "advancements.unicopia.molting_season_5.title": "Сезон линьки 5", + "advancements.unicopia.molting_season_5.description": "Сбросьте своё шестнадцатое перо во время полёта", + "advancements.unicopia.molting_season_6.title": "Сезон линьки 6", + "advancements.unicopia.molting_season_6.description": "Сбросьте своё тридцать второе перо во время полёта", + "advancements.unicopia.molting_season_7.title": "Сезон линьки 7", + "advancements.unicopia.molting_season_7.description": "Сбросьте своё шестьдесят четвёртое перо во время полёта", + "advancements.unicopia.molting_season_8.title": "Сезон линьки 8", + "advancements.unicopia.molting_season_8.description": "Сбросьте своё сто двадцать восьмое перо во время полёта", + "advancements.unicopia.molting_season_9.title": "Сезон линьки 9", + "advancements.unicopia.molting_season_9.description": "Сбросьте своё двести пятьдесят шестое перо во время полёта", + "advancements.unicopia.molting_season_10.title": "Сезон линьки 10", + "advancements.unicopia.molting_season_10.description": "Сбросьте своё пятьсот двенадцатое перо во время полёта", + "advancements.unicopia.molting_season_11.title": "Сезон линьки 11", + "advancements.unicopia.molting_season_11.description": "Сбросьте своё тысяча двадцать четвёртое перо во время полёта", + "advancements.unicopia.dedicated_flier.title": "Всё в порядке, теперь ты можешь остановиться", + "advancements.unicopia.dedicated_flier.description": "Сбросьте своё две тысячи сорок восьмое перо во время полёта", "advancements.unicopia.rainbow_crash.title": "Дискорд тебя побери, Рэйнбоу", "advancements.unicopia.rainbow_crash.description": "Устройте войну против злой нации стеклянных окон", "advancements.unicopia.second_wind.title": "Второе дыхание", @@ -1683,11 +1755,13 @@ "advancements.unicopia.deter_phantom.title": "Что летает вокруг", "advancements.unicopia.deter_phantom.description": "Поднимитесь и дайте этим фантомам попробовать их собственное лекарство", - "advancements.unicopia.magical_route.title": "Рог единорога", - "advancements.unicopia.magical_route.description": "Окунитесь в мир блеска и радуги", + "advancements.unicopia.unicorn_route.title": "Рог единорога", + "advancements.unicopia.unicorn_route.description": "Окунитесь в мир блеска и радуги", "advancements.unicopia.books.title": "Книги!", "advancements.unicopia.books.description": "Это МОЯ книга заклинаний, и я собираюсь её ПРОЧИТАТЬ!", + "advancements.unicopia.books_books_books.title": "Книги! Книги! Книги!", + "advancements.unicopia.books_books_books.description": "Заполните инвентарь книгами", "advancements.unicopia.tempted.title": "Заманчиво...", "advancements.unicopia.tempted.description": "Наденьте амулет аликорна", "advancements.unicopia.hello_darkness_my_old_friend.title": "Здравствуй, Тьма...", @@ -1710,6 +1784,13 @@ "advancements.unicopia.love_is_power.title": "Любовь - это сила", "advancements.unicopia.love_is_power.description": "Изгоните короля Сомбра с помощью кристального сердца", + "advancements.unicopia.hippogriff_route.title": "Всплеск Сиквестрии", + "advancements.unicopia.hippogriff_route.description": "Присоединитесь к гнезду гиппогрифов", + "advancements.unicopia.shoo_be_doo.title": "Шу-би-ду!", + "advancements.unicopia.shoo_be_doo.description": "Используйте жемчужное ожерелье, чтобы превратиться в морское существо", + "advancements.unicopia.shoo_be_done.title": "Шу-би-всё!", + "advancements.unicopia.shoo_be_done.description": "Используйте жемчужное ожерелье, чтобы стать снова собой", + "unicopia.toast.discoveries.title": "Новые открытия!", "unicopia.toast.discoveries.description": "Проверьте свою книгу заклинаний" } From b81e03a489c282327d75e1b0f1fe0a2c0cc0c89f 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 Apr 2024 22:10:46 +0800 Subject: [PATCH 05/32] update zh_cn.json and HTP_CN (#319) --- HOW_TO_PLAY_CN.md | 2 +- .../resources/assets/unicopia/lang/zh_cn.json | 53 ++++++++++++++----- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/HOW_TO_PLAY_CN.md b/HOW_TO_PLAY_CN.md index 0d839eea..93ae652b 100644 --- a/HOW_TO_PLAY_CN.md +++ b/HOW_TO_PLAY_CN.md @@ -124,6 +124,6 @@ 这些状态是按周期循环的,如果您发现的果树不是您想要的状态,可以在它旁边等待几天直到结果,但请不要在魔虹苹果成熟前收获它们,否则它们会把您电得酥脆! 如果您设法获取到了魔虹苹果的木头和叶子,那它们也可以充当威慑滋事者的完美屏障。 -### 马芬 +### 玛芬 香软可口,猪猪最爱。 \ No newline at end of file diff --git a/src/main/resources/assets/unicopia/lang/zh_cn.json b/src/main/resources/assets/unicopia/lang/zh_cn.json index 9da5f8b3..d8a3fe99 100644 --- a/src/main/resources/assets/unicopia/lang/zh_cn.json +++ b/src/main/resources/assets/unicopia/lang/zh_cn.json @@ -197,6 +197,7 @@ "item.unicopia.music_disc_funk.desc": "funk, just funk", "item.unicopia.cloud_lump": "云团", + "item.unicopia.white_bed_sheets": "白色被单", "item.unicopia.light_gray_bed_sheets": "淡灰色被单", "item.unicopia.gray_bed_sheets": "灰色被单", "item.unicopia.black_bed_sheets": "黑色被单", @@ -228,7 +229,7 @@ "block.unicopia.spectral_fire": "节律火", "block.unicopia.bananas": "香蕉", "block.unicopia.zapling": "魔虹苹果树苗", - "block.unicopia.potted_zapling": "盆中 魔虹苹果树苗", + "block.unicopia.potted_zapling": "魔虹苹果树苗盆栽", "block.unicopia.zap_log": "魔虹苹果木原木", "block.unicopia.zap_wood": "魔虹苹果木", "block.unicopia.stripped_zap_log": "去皮魔虹苹果木原木", @@ -252,7 +253,7 @@ "block.unicopia.zap_apple": "魔虹苹果", "block.unicopia.zap_bulb": "没熟的魔虹苹果", "block.unicopia.palm_sapling": "棕榈树苗", - "block.unicopia.potted_palm_sapling": "盆中 棕榈树苗", + "block.unicopia.potted_palm_sapling": "棕榈树苗盆栽", "block.unicopia.palm_log": "棕榈木原木", "block.unicopia.palm_wood": "棕榈木", "block.unicopia.palm_planks": "棕榈木板", @@ -275,13 +276,13 @@ "block.unicopia.gold_root": "黄金根", "block.unicopia.golden_oak_sprout": "金橡树嫩芽", "block.unicopia.golden_oak_sapling": "金橡树树苗", - "block.unicopia.potted_golden_oak_sapling": "盆中 金橡树树苗", + "block.unicopia.potted_golden_oak_sapling": "金橡树树苗盆栽", "block.unicopia.golden_oak_leaves": "金橡树树叶", "block.unicopia.golden_oak_log": "金橡树原木", "block.unicopia.mango": "芒果", "block.unicopia.mango_leaves": "芒果树叶", "block.unicopia.mango_sapling": "芒果树苗", - "block.unicopia.potted_mango_sapling": "盆中 芒果树苗", + "block.unicopia.potted_mango_sapling": "芒果树苗盆栽", "block.unicopia.pineapple": "菠萝树", "block.unicopia.clam_shell": "蛤蜊壳", @@ -290,15 +291,15 @@ "block.unicopia.green_apple_leaves": "史密斯婆婆苹果树树叶", "block.unicopia.green_apple_sapling": "史密斯婆婆苹果树树苗", - "block.unicopia.potted_green_apple_sapling": "盆中 史密斯婆婆苹果树树苗", + "block.unicopia.potted_green_apple_sapling": "史密斯婆婆苹果树树苗盆栽", "block.unicopia.green_apple_sprout": "史密斯婆婆苹果嫩芽", "block.unicopia.sweet_apple_leaves": "甜苹果树树叶", "block.unicopia.sweet_apple_sapling": "甜苹果树树苗", - "block.unicopia.potted_sweet_apple_sapling": "盆中 甜苹果树树苗", + "block.unicopia.potted_sweet_apple_sapling": "甜苹果树树苗盆栽", "block.unicopia.sweet_apple_sprout": "甜苹果树嫩芽", "block.unicopia.sour_apple_leaves": "酸苹果树树叶", "block.unicopia.sour_apple_sapling": "酸苹果树树苗", - "block.unicopia.potted_sour_apple_sapling": "盆中 酸苹果树树苗", + "block.unicopia.potted_sour_apple_sapling": "酸苹果树树苗盆栽", "block.unicopia.sour_apple_sprout": "酸苹果树嫩芽", "block.unicopia.surface_chitin": "几丁质表面", @@ -612,6 +613,7 @@ "tag.unicopia.food_types.cooked_fish": "烤好的鱼", "tag.unicopia.food_types.raw_insect": "虫类", "tag.unicopia.food_types.cooked_insect": "烤虫类", + "tag.unicopia.food_types.nuts_and_seeds": "坚果和种子", "tag.unicopia.food_types.love": "爱", "tag.unicopia.food_types.rocks": "石块", "tag.unicopia.food_types.pinecone": "坚果和种子", @@ -1547,6 +1549,7 @@ "unicopia.subtitle.pegasus.molt": "天马:脱羽", "unicopia.subtitle.unicorn.teleport": "魔法:啵", "unicopia.subtitle.player.wololo": "Wololo!", + "unicopia.subtitle.corrupt": "魔法:堕落", "unicopia.subtitle.entity.player.whistle": "玩家吹口哨", "unicopia.subtitle.entity.player.kick": "玩家:踢腿", "unicopia.subtitle.magic_aura": "魔法:嗡嗡", @@ -1591,12 +1594,18 @@ "advancements.unicopia.praise_the_sun.description": "感受太阳公主的无上熔光", "advancements.unicopia.cool_potato.title": "酷毙了", "advancements.unicopia.cool_potato.description": "保护你的眼睛不受阳光侵袭", + "advancements.unicopia.take_a_note.title": "穗龙,把这个记下来", + "advancements.unicopia.take_a_note.description": "获得龙息卷轴", + "advancements.unicopia.dear_princess.title": "亲爱的公主殿下……", + "advancements.unicopia.dear_princess.description": "用龙息卷轴送出一份信", "advancements.unicopia.baked_bads.title": "烤砸了", - "advancements.unicopia.baked_bads.description": "烤制美味马芬", + "advancements.unicopia.baked_bads.description": "烤制美味玛芬", "advancements.unicopia.mid_flight_interruption.title": "风雷混合双打", "advancements.unicopia.mid_flight_interruption.description": "在风暴中飞行时被雷劈到", "advancements.unicopia.lightning_bug.title": "引火虫", - "advancements.unicopia.lightning_bug.description": "引得十次雷劈", + "advancements.unicopia.lightning_bug.description": "作为幻形灵,引得十次雷劈", + "advancements.unicopia.wonder_bolt.title": "闪电天马", + "advancements.unicopia.wonder_bolt.description": "引得十次雷劈", "advancements.unicopia.jar.title": "哇哦,这是什么?", "advancements.unicopia.jar.description": "找到一个空罐子", "advancements.unicopia.gotcha.title": "抓到你了!", @@ -1669,15 +1678,33 @@ "advancements.unicopia.sky_route.title": "飞彳亍", "advancements.unicopia.sky_route.description": "入驻云中城", "advancements.unicopia.molting_season_1.title": "换羽季节I", - "advancements.unicopia.molting_season_1.description": "飞行时掉下一根羽毛", + "advancements.unicopia.molting_season_1.description": "飞行时掉下第1根羽毛", "advancements.unicopia.molting_season_2.title": "换羽季节II", - "advancements.unicopia.molting_season_2.description": "飞行时掉下五根羽毛", + "advancements.unicopia.molting_season_2.description": "飞行时掉下第2根根羽毛", "advancements.unicopia.molting_season_3.title": "换羽季节III", - "advancements.unicopia.molting_season_3.description": "飞行时掉下十五根羽毛", + "advancements.unicopia.molting_season_3.description": "飞行时掉下第4根羽毛", + "advancements.unicopia.molting_season_4.title": "换羽季节IV", + "advancements.unicopia.molting_season_4.description": "飞行时掉下第8根羽毛", + "advancements.unicopia.molting_season_5.title": "换羽季节V", + "advancements.unicopia.molting_season_5.description": "飞行时掉下第16根羽毛", + "advancements.unicopia.molting_season_6.title": "换羽季节VI", + "advancements.unicopia.molting_season_6.description": "飞行时掉下第32根羽毛", + "advancements.unicopia.molting_season_7.title": "换羽季节VII", + "advancements.unicopia.molting_season_7.description": "飞行时掉下第64根羽毛", + "advancements.unicopia.molting_season_8.title": "换羽季节VIII", + "advancements.unicopia.molting_season_8.description": "飞行时掉下第128根羽毛", + "advancements.unicopia.molting_season_9.title": "换羽季节IX", + "advancements.unicopia.molting_season_9.description": "飞行时掉下第256根羽毛", + "advancements.unicopia.molting_season_10.title": "换羽季节X", + "advancements.unicopia.molting_season_10.description": "飞行时掉下第512根羽毛", + "advancements.unicopia.molting_season_11.title": "换羽季节XI", + "advancements.unicopia.molting_season_11.description": "飞行时掉下第1024根羽毛", + "advancements.unicopia.dedicated_flier.title": "谢谢,够了", + "advancements.unicopia.dedicated_flier.description": "飞行时掉下第2048根羽毛", "advancements.unicopia.rainbow_crash.title": "去你的彩虹", "advancements.unicopia.rainbow_crash.description": "向邪恶的玻璃国宣战", "advancements.unicopia.second_wind.title": "来劲了", - "advancements.unicopia.second_wind.description": "竭力飞行", + "advancements.unicopia.second_wind.description": "难受啊马飞", "advancements.unicopia.deter_phantom.title": "神仙斗法", "advancements.unicopia.deter_phantom.description": "马飞飞,让那些修仙的幻翼永远安眠吧", From 835834a46881231af8489723a47d76cdeaaf832c Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 6 Apr 2024 16:08:02 +0100 Subject: [PATCH 06/32] Update dependencies --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index e821a843..7018784c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,8 +22,8 @@ org.gradle.daemon=false # Dependencies fabwork_version=1.2.0 modmenu_version=7.0.0-beta.2 - minelp_version=4.10.6+1.20.1 - kirin_version=1.15.5-beta.1+1.20.1 + minelp_version=4.11.7+1.20.1 + kirin_version=1.15.6+1.20.1 reach_attributes_version=2.3.4 trinkets_version=3.7.1 terraformer_api_version=7.0.0-beta.1 From 12bc0b697315ce9850e28be65aa23fd1e84c09cd Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 7 Apr 2024 20:27:15 +0100 Subject: [PATCH 07/32] Rewrite the dark vortex to cause less lag and adjust/apply a cap to its maximum size --- .../magic/spell/effect/DarkVortexSpell.java | 253 +++++++++++------- .../unicopia/client/gui/UHud.java | 4 +- .../render/spell/DarkVortexSpellRenderer.java | 52 ++-- .../spell/SpellEffectsRenderDispatcher.java | 6 + .../providers/tag/UBlockTagProvider.java | 6 +- .../unicopia/entity/player/PlayerPhysics.java | 2 +- .../unicopia/server/world/UGameRules.java | 13 +- src/main/resources/unicopia.aw | 3 - 8 files changed, 205 insertions(+), 134 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 ea5f8462..c1d79205 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 @@ -1,8 +1,12 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect; +import java.util.Optional; + import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.UTags; +import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.Affine; import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; @@ -12,6 +16,7 @@ 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.entity.mob.CastSpellEntity; import com.minelittlepony.unicopia.particle.FollowingParticleEffect; import com.minelittlepony.unicopia.particle.LightningBoltParticleEffect; import com.minelittlepony.unicopia.particle.ParticleUtils; @@ -19,10 +24,12 @@ import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.projectile.MagicBeamEntity; import com.minelittlepony.unicopia.projectile.MagicProjectileEntity; import com.minelittlepony.unicopia.projectile.ProjectileDelegate; +import com.minelittlepony.unicopia.server.world.UGameRules; +import com.minelittlepony.unicopia.util.Lerp; import com.minelittlepony.unicopia.util.shape.Sphere; +import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; -import net.minecraft.entity.FallingBlockEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.PersistentProjectileEntity; import net.minecraft.item.Item; @@ -39,7 +46,7 @@ import net.minecraft.world.World.ExplosionSourceType; /** * More powerful version of the vortex spell which creates a black hole. */ -public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelegate.BlockHitListener { +public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate.BlockHitListener { public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder() .with(Trait.CHAOS, 5) .with(Trait.KNOWLEDGE, 1) @@ -49,18 +56,34 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega private float accumulatedMass = 0; + private final TargetSelecter targetSelecter = new TargetSelecter(this).setFilter(this::isValidTarget).setTargetowner(true).setTargetAllies(true); + + private final Lerp radius = new Lerp(0); + + private int prevTicksDying; + private int ticksDying; + protected DarkVortexSpell(CustomisedSpellType type) { super(type); - targetSelecter.setTargetowner(true).setTargetAllies(true); + } + // 1. force decreases with distance: distance scale 1 -> 0 + // 2. max force (at dist 0) is taken from accumulated mass + // 3. force reaches 0 at distance of drawDropOffRange + + private double getMass() { + return 0.1F + accumulatedMass / 10F; } - @Override - public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) { - if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) { - BlockPos pos = hit.getBlockPos(); - projectile.getWorld().createExplosion(projectile, pos.getX(), pos.getY(), pos.getZ(), 3, ExplosionSourceType.NONE); - toPlaceable().tick(source, Situation.BODY); - } + public double getEventHorizonRadius() { + return radius.getValue(); + } + + public double getDrawDropOffRange() { + return getEventHorizonRadius() * 20; + } + + private double getAttractiveForce(Caster source, Entity target) { + return AttractionUtils.getAttractiveForce(getMass(), getOrigin(source), target); } @Override @@ -78,23 +101,96 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega if (situation == Situation.BODY) { return true; } + double mass = getMass() * 0.1; + double logarithm = 1 - (1D / (1 + (mass * mass))); + radius.update((float)Math.max(0.1, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L); if (source.asEntity().age % 20 == 0) { source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1); } - if (!source.isClient() && source.asWorld().random.nextInt(300) == 0) { + double eventHorizon = getEventHorizonRadius(); + + if (source.isClient()) { + if (eventHorizon > 0.3) { + double range = eventHorizon * 2; + Vec3d origin = getOrigin(source); + source.spawnParticles(origin, new Sphere(false, range), 50, p -> { + source.addParticle( + new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F) + .withChild(source.asWorld().isAir(BlockPos.ofFloored(p)) ? ParticleTypes.SMOKE : ParticleTypes.CAMPFIRE_SIGNAL_SMOKE), + p, + Vec3d.ZERO + ); + }); + } + } else if (source.asWorld().random.nextInt(300) == 0) { ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, getOrigin(source), Vec3d.ZERO); } - super.tick(source, situation); + if (!source.isClient()) { + if (eventHorizon > 2) { + Vec3d origin = getOrigin(source); + new Sphere(false, eventHorizon + 3).translate(origin).randomPoints(10, source.asWorld().random).forEach(i -> { + BlockPos pos = BlockPos.ofFloored(i); + if (!source.asWorld().isAir(pos)) { + new Sphere(false, 3).translate(i).getBlockPositions().forEach(p -> { + affectBlock(source, p, origin); + }); + ParticleUtils.spawnParticle(source.asWorld(), new LightningBoltParticleEffect(true, 10, 6, 3, Optional.of(i)), getOrigin(source), Vec3d.ZERO); + } + }); + } + } + + Vec3d origin = getOrigin(source); + for (Entity insideEntity : source.findAllEntitiesInRange(eventHorizon * 0.5F).toList()) { + insideEntity.setVelocity(Vec3d.ZERO); + Living.updateVelocity(insideEntity); + + if (insideEntity instanceof CastSpellEntity s && getType().isOn(insideEntity)) { + setDead(); + s.getSpellSlot().clear(); + source.asWorld().createExplosion(source.asEntity(), source.getOrigin().getX(), source.getOrigin().getY(), source.getOrigin().getZ(), 12, ExplosionSourceType.NONE); + source.asWorld().createExplosion(source.asEntity(), insideEntity.getX(), insideEntity.getY(), insideEntity.getZ(), 12, ExplosionSourceType.NONE); + return false; + } + } + targetSelecter.getEntities(source, getDrawDropOffRange()).forEach(i -> { + try { + affectEntity(source, i, i.getPos().distanceTo(origin)); + } catch (Throwable e) { + Unicopia.LOGGER.error("Error updating radial effect", e); + } + }); + + if (!source.subtractEnergyCost(-accumulatedMass)) { + setDead(); + source.asWorld().createExplosion(source.asEntity(), source.getOrigin().getX(), source.getOrigin().getY(), source.getOrigin().getZ(), 3, ExplosionSourceType.NONE); + } + return true; } + @Override - protected void consumeManage(Caster source, long costMultiplier, float knowledge) { - if (!source.subtractEnergyCost(-accumulatedMass)) { - setDead(); + public void tickDying(Caster source) { + accumulatedMass /= 2D; + double mass = getMass() * 0.1; + double logarithm = 1 - (1D / (1 + (mass * mass))); + radius.update((float)Math.max(0.1, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L); + prevTicksDying = ticksDying; + if (ticksDying++ > 25) { + super.tickDying(source); + } + } + + @Override + public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) { + if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) { + BlockPos pos = hit.getBlockPos(); + projectile.getWorld().createExplosion(projectile, pos.getX(), pos.getY(), pos.getZ(), 12, ExplosionSourceType.NONE); + toPlaceable().tick(source, Situation.BODY); } } @@ -103,102 +199,49 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega return accumulatedMass < 4; } - @Override - protected boolean isValidTarget(Caster source, Entity entity) { + private boolean isValidTarget(Caster source, Entity entity) { return EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.test(entity) && getAttractiveForce(source, entity) > 0; } - @Override - public void generateParticles(Caster source) { - super.generateParticles(source); + public Vec3d getOrigin(Caster source) { + return source.asEntity().getPos().add(0, getYOffset(), 0); + } - if (getEventHorizonRadius() > 0.3) { - double range = getDrawDropOffRange(source); - Vec3d origin = getOrigin(source); - 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 - ); + public double getYOffset() { + return 3 - radius.getValue() * 0.5; + } + + private boolean canAffect(Caster source, BlockPos pos) { + return source.canModifyAt(pos) + && source.asWorld().getBlockState(pos).getHardness(source.asWorld(), pos) >= 0 + && !source.asWorld().getBlockState(pos).isIn(UTags.Blocks.CATAPULT_IMMUNE); + } + + private void affectBlock(Caster source, BlockPos pos, Vec3d origin) { + if (!canAffect(source, pos)) { + if (source.asWorld().getBlockState(pos).isOf(Blocks.BEDROCK)) { + source.asWorld().setBlockState(pos, Blocks.BARRIER.getDefaultState()); + } + return; + } + if (source.getOrigin().isWithinDistance(pos, getEventHorizonRadius())) { + source.asWorld().breakBlock(pos, false); + if (!source.asWorld().getFluidState(pos).isEmpty()) { + source.asWorld().setBlockState(pos, Blocks.AIR.getDefaultState()); + } + } else { + CatapultSpell.createBlockEntity(source.asWorld(), pos, e -> { + e.addVelocity(0, 0.1, 0); + if (e instanceof PlayerEntity) { + affectEntity(source, e, e.getPos().distanceTo(getOrigin(source))); } }); } } - @Override - public double getDrawDropOffRange(Caster source) { - return getEventHorizonRadius() * 20; - } - - @Override - protected Vec3d getOrigin(Caster source) { - return source.getOriginVector().add(0, getEventHorizonRadius() / 2D, 0); - } - - @Override - protected long applyEntities(Caster source) { - if (!source.isClient()) { - - double radius = getEventHorizonRadius(); - - if (radius > 2) { - Vec3d origin = getOrigin(source); - new Sphere(false, radius).translate(origin).getBlockPositions().forEach(i -> { - if (!canAffect(source, i)) { - return; - } - if (source.getOrigin().isWithinDistance(i, getEventHorizonRadius() / 2)) { - source.asWorld().breakBlock(i, false); - } else { - CatapultSpell.createBlockEntity(source.asWorld(), i, e -> { - applyRadialEffect(source, e, e.getPos().distanceTo(origin), radius); - }); - } - }); - } - } - - return super.applyEntities(source); - } - - protected boolean canAffect(Caster source, BlockPos pos) { - return source.canModifyAt(pos) - && source.asWorld().getFluidState(pos).isEmpty() - && source.asWorld().getBlockState(pos).getHardness(source.asWorld(), pos) >= 0; - } - - // 1. force decreases with distance: distance scale 1 -> 0 - // 2. max force (at dist 0) is taken from accumulated mass - // 3. force reaches 0 at distance of drawDropOffRange - - public double getEventHorizonRadius() { - return Math.sqrt(Math.max(0.001, getMass() / 3F)); - } - - private double getAttractiveForce(Caster source, Entity target) { - return AttractionUtils.getAttractiveForce(getMass(), getOrigin(source), target); - } - - private double getMass() { - return 0.1F + accumulatedMass / 10F; - } - - @Override - protected void applyRadialEffect(Caster source, Entity target, double distance, double radius) { - - if (target instanceof FallingBlockEntity && source.isClient()) { - return; - } - + private void affectEntity(Caster source, Entity target, double distance) { if (distance <= getEventHorizonRadius() + 0.5) { - target.setVelocity(target.getVelocity().multiply(distance / (2 * radius))); - if (distance < 1) { - target.setVelocity(target.getVelocity().multiply(distance)); - - } + target.setVelocity(target.getVelocity().multiply(distance < 1 ? distance : distance / (2 * getEventHorizonRadius()))); Living.updateVelocity(target); @Nullable @@ -227,14 +270,18 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega target.damage(source.damageOf(UDamageTypes.GAVITY_WELL_RECOIL, source), Integer.MAX_VALUE); if (!(target instanceof PlayerEntity)) { target.discard(); - source.asWorld().playSound(null, source.getOrigin(), USounds.ENCHANTMENT_CONSUMPTION_CONSUME, SoundCategory.AMBIENT, 2, 0.02F); + source.asWorld().playSound(null, target.getBlockPos(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.002F); } 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); + + if (target instanceof PlayerEntity && distance < getEventHorizonRadius() + 5) { + source.asWorld().playSound(null, target.getBlockPos(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F); + } + } else { double force = getAttractiveForce(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 ac25ce7d..11a5bf02 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java @@ -198,7 +198,7 @@ public class UHud { float vortexDistortion = DarkVortexSpellRenderer.getCameraDistortion(); - if (vortexDistortion > 20) { + if (vortexDistortion > 25) { context.fill(RenderLayers.getEndPortal(), 0, 0, scaledWidth, scaledHeight, 0); context.getMatrices().push(); context.getMatrices().translate(scaledWidth / 2, scaledHeight / 2, 0); @@ -206,7 +206,7 @@ public class UHud { context.getMatrices().pop(); return; } else if (vortexDistortion > 0) { - context.fill(0, 0, scaledWidth, scaledHeight, (int)((vortexDistortion / 20F) * 255) << 24); + context.fill(0, 0, scaledWidth, scaledHeight, (int)((Math.min(20, vortexDistortion) / 20F) * 255) << 24); } boolean hasEffect = client.player.hasStatusEffect(UEffects.SUN_BLINDNESS); 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 aec3d2d8..2134f8c3 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 @@ -8,11 +8,11 @@ import com.minelittlepony.unicopia.client.render.RenderLayers; 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.Camera; 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; @@ -36,65 +36,79 @@ public class DarkVortexSpellRenderer extends 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) { - super.render(matrices, vertices, spell, caster, light, limbAngle, limbDistance, tickDelta, animationProgress, headYaw, headPitch); - - Entity cameraEntity = MinecraftClient.getInstance().getCameraEntity(); + Camera camera = MinecraftClient.getInstance().gameRenderer.getCamera(); float radius = (float)spell.getEventHorizonRadius(); - float absDistance = (float)cameraEntity.getEyePos().distanceTo(caster.getOriginVector().add(0, 2, 0)); + + float absDistance = (float)camera.getPos().distanceTo(spell.getOrigin(caster)); matrices.push(); - matrices.translate(0, 2 + radius, 0); + matrices.translate(0, spell.getYOffset(), 0); - SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, Math.min(radius * 0.6F, absDistance * 0.1F), 0, 0, 0, 1); + float visualRadius = Math.min(radius * 0.8F, absDistance - 1F); + + SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getSolid()), light, 1, visualRadius, 0, 0, 0, 1); + SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getMagicColored()), light, 1, visualRadius + 0.05F, 0, 0, 0, 0.9F); + SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getMagicColored()), light, 1, visualRadius + 0.1F, 0, 0, 0, 0.9F); + SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getMagicColored()), light, 1, visualRadius + 0.15F, 0, 0, 0, 0.9F); 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.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90 + camera.getYaw())); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-camera.getPitch())); matrices.scale(0.7F, 1, 1); - float distance = 1F / MathHelper.clamp((absDistance / (radius * 4)), 0.0000001F, 1); + float distance = 1F / MathHelper.clamp(absDistance / (radius + 7), 0.0000001F, 1); distance *= distance; if (absDistance < radius * 4) { cameraDistortion += distance; } + SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getEndPortal()), light, 1, radius * 0.5F, 0, 0, 0, 0); + + matrices.push(); matrices.scale(distance, distance, distance); 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); if (radius > 0.3F && absDistance > radius) { radius *= Math.min(2, 3 + radius); - matrices.scale(radius, radius, radius); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 168)); + float processionSpeed = animationProgress * 0.02F; + float maxProcessionAngle = 15; + + float range = (float)spell.getDrawDropOffRange() / 8F; + + matrices.scale(range, range, range); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90 + MathHelper.cos(processionSpeed) * maxProcessionAngle)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(MathHelper.sin(processionSpeed) * maxProcessionAngle)); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 18)); 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.scale(0.9F, 0.9F, 0.9F); 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)); + matrices.scale(0.9F, 0.9F, 0.9F); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(33)); 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/SpellEffectsRenderDispatcher.java b/src/main/java/com/minelittlepony/unicopia/client/render/spell/SpellEffectsRenderDispatcher.java index 72d0db23..c87430da 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 @@ -14,6 +14,9 @@ import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation; import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.Living; +import com.minelittlepony.unicopia.entity.mob.CastSpellEntity; +import com.minelittlepony.unicopia.entity.player.Pony; + import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer.TextLayerType; @@ -91,6 +94,9 @@ public class SpellEffectsRenderDispatcher implements SynchronousResourceReloader if (client.getEntityRenderDispatcher().shouldRenderHitboxes() && !client.hasReducedDebugInfo() && !(caster.asEntity() == client.cameraEntity && client.options.getPerspective() == Perspective.FIRST_PERSON)) { + if (!(caster instanceof Pony || caster instanceof CastSpellEntity)) { + return; + } renderHotspot(matrices, vertices, caster, animationProgress); renderSpellDebugInfo(matrices, vertices, caster, light); } diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java index a576582d..12ddedd2 100644 --- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java +++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java @@ -44,7 +44,11 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider { UBlocks.GOLDEN_OAK_SPROUT }; - getOrCreateTagBuilder(UTags.Blocks.CATAPULT_IMMUNE).add(Blocks.BEDROCK).forceAddTag(BlockTags.DOORS).forceAddTag(BlockTags.TRAPDOORS); + getOrCreateTagBuilder(UTags.Blocks.CATAPULT_IMMUNE).add( + Blocks.STRUCTURE_VOID, Blocks.STRUCTURE_BLOCK, + Blocks.COMMAND_BLOCK, Blocks.CHAIN_COMMAND_BLOCK, Blocks.REPEATING_COMMAND_BLOCK, + Blocks.LIGHT, Blocks.JIGSAW, Blocks.BARRIER, Blocks.BEDROCK + ).forceAddTag(BlockTags.DOORS).forceAddTag(BlockTags.TRAPDOORS); getOrCreateTagBuilder(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON).forceAddTag(BlockTags.ANIMALS_SPAWNABLE_ON).forceAddTag(BlockTags.LEAVES).forceAddTag(BlockTags.FLOWERS).forceAddTag(BlockTags.FLOWER_POTS); getOrCreateTagBuilder(UTags.Blocks.JARS).add(UBlocks.JAR, UBlocks.CLOUD_JAR, UBlocks.STORM_JAR, UBlocks.LIGHTNING_JAR, UBlocks.ZAP_JAR); getOrCreateTagBuilder(BlockTags.CROPS).add(crops); 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 fc53f9b0..4465c2aa 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -716,7 +716,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickab } private void applyTurbulance(MutableVector velocity) { - int globalEffectStrength = MathHelper.clamp(entity.getWorld().getGameRules().getInt(UGameRules.WEATHER_EFFECTS_STRENGTH), 0, 100); + int globalEffectStrength = entity.getWorld().getGameRules().getInt(UGameRules.WEATHER_EFFECTS_STRENGTH); float effectStrength = Math.min(1, (float)ticksInAir / MAX_TICKS_TO_WEATHER_EFFECTS) * (globalEffectStrength / 100F); Vec3d gust = WeatherConditions.getGustStrength(entity.getWorld(), entity.getBlockPos()) .multiply(globalEffectStrength / 100D) diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/UGameRules.java b/src/main/java/com/minelittlepony/unicopia/server/world/UGameRules.java index 93fc32bd..cdf4f06a 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/UGameRules.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/UGameRules.java @@ -1,15 +1,18 @@ package com.minelittlepony.unicopia.server.world; +import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory; +import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry; import net.minecraft.world.GameRules; import net.minecraft.world.GameRules.BooleanRule; import net.minecraft.world.GameRules.IntRule; public interface UGameRules { - GameRules.Key SWAP_TRIBE_ON_DEATH = GameRules.register("swapTribeOnDeath", GameRules.Category.SPAWNING, BooleanRule.create(false)); - GameRules.Key ANNOUNCE_TRIBE_JOINS = GameRules.register("announceTribeJoins", GameRules.Category.SPAWNING, BooleanRule.create(false)); - GameRules.Key DO_NOCTURNAL_BAT_PONIES = GameRules.register("doNocturnalBatPonies", GameRules.Category.PLAYER, BooleanRule.create(true)); - GameRules.Key WEATHER_EFFECTS_STRENGTH = GameRules.register("weatherEffectsStrength", GameRules.Category.MISC, IntRule.create(100)); - GameRules.Key DO_TIME_MAGIC = GameRules.register("doTimeMagic", GameRules.Category.PLAYER, BooleanRule.create(true)); + GameRules.Key SWAP_TRIBE_ON_DEATH = GameRuleRegistry.register("swapTribeOnDeath", GameRules.Category.SPAWNING, GameRuleFactory.createBooleanRule(false)); + GameRules.Key ANNOUNCE_TRIBE_JOINS = GameRuleRegistry.register("announceTribeJoins", GameRules.Category.SPAWNING, GameRuleFactory.createBooleanRule(false)); + GameRules.Key DO_NOCTURNAL_BAT_PONIES = GameRuleRegistry.register("doNocturnalBatPonies", GameRules.Category.PLAYER, GameRuleFactory.createBooleanRule(true)); + GameRules.Key WEATHER_EFFECTS_STRENGTH = GameRuleRegistry.register("weatherEffectsStrength", GameRules.Category.MISC, GameRuleFactory.createIntRule(100, 0, 100)); + GameRules.Key DO_TIME_MAGIC = GameRuleRegistry.register("doTimeMagic", GameRules.Category.PLAYER, GameRuleFactory.createBooleanRule(true)); + GameRules.Key MAX_DARK_VORTEX_SIZE = GameRuleRegistry.register("maxDarkVortexSize", GameRules.Category.PLAYER, GameRuleFactory.createIntRule(20, 1, 25)); static void bootstrap() { } } diff --git a/src/main/resources/unicopia.aw b/src/main/resources/unicopia.aw index 0b811613..40a5a615 100644 --- a/src/main/resources/unicopia.aw +++ b/src/main/resources/unicopia.aw @@ -6,9 +6,6 @@ accessible class net/minecraft/client/render/item/HeldItemRenderer$H accessible class net/minecraft/client/render/VertexConsumers$Union accessible class net/minecraft/client/gui/hud/InGameHud$HeartType -accessible method net/minecraft/world/GameRules register (Ljava/lang/String;Lnet/minecraft/world/GameRules$Category;Lnet/minecraft/world/GameRules$Type;)Lnet/minecraft/world/GameRules$Key; -accessible method net/minecraft/world/GameRules$BooleanRule create (Z)Lnet/minecraft/world/GameRules$Type; -accessible method net/minecraft/world/GameRules$IntRule create (I)Lnet/minecraft/world/GameRules$Type; accessible method net/minecraft/world/gen/foliage/FoliagePlacerType (Lcom/mojang/serialization/Codec;)V accessible field net/minecraft/entity/mob/CreeperEntity CHARGED Lnet/minecraft/entity/data/TrackedData; From e465a218396f459173998c01e236feb02704da12 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 8 Apr 2024 16:53:01 +0100 Subject: [PATCH 08/32] Minor fixes to Ether and some owned style changes --- .../com/minelittlepony/unicopia/Owned.java | 2 +- .../minelittlepony/unicopia/WeaklyOwned.java | 4 ++-- .../magic/spell/effect/NecromancySpell.java | 2 +- .../magic/spell/effect/PortalSpell.java | 22 +++++++++---------- .../unicopia/entity/EntityReference.java | 2 +- .../unicopia/server/world/Ether.java | 14 +++++++++++- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Owned.java b/src/main/java/com/minelittlepony/unicopia/Owned.java index 47c5403c..41c55d02 100644 --- a/src/main/java/com/minelittlepony/unicopia/Owned.java +++ b/src/main/java/com/minelittlepony/unicopia/Owned.java @@ -53,7 +53,7 @@ public interface Owned { } default boolean hasCommonOwner(Owned sibling) { - return getMasterId().isPresent() && getMasterId().equals(sibling.getMasterId()); + return getMasterId().equals(sibling.getMasterId()); } interface Mutable { diff --git a/src/main/java/com/minelittlepony/unicopia/WeaklyOwned.java b/src/main/java/com/minelittlepony/unicopia/WeaklyOwned.java index 514c22fd..504191bf 100644 --- a/src/main/java/com/minelittlepony/unicopia/WeaklyOwned.java +++ b/src/main/java/com/minelittlepony/unicopia/WeaklyOwned.java @@ -42,8 +42,8 @@ public interface WeaklyOwned extends Owned, WorldConvertabl @Override @SuppressWarnings("unchecked") default void setMaster(Owned sibling) { - if (sibling instanceof WeaklyOwned) { - getMasterReference().copyFrom(((WeaklyOwned)sibling).getMasterReference()); + if (sibling instanceof WeaklyOwned w) { + getMasterReference().copyFrom(w.getMasterReference()); } else { setMaster(sibling.getMaster()); } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/NecromancySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/NecromancySpell.java index 1e4243a6..dbf79bab 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/NecromancySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/NecromancySpell.java @@ -197,7 +197,7 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti } Equine.of(minion).filter(eq -> eq instanceof Creature).ifPresent(eq -> { - ((Creature)eq).setMaster(source.getMaster()); + ((Creature)eq).setMaster(source); if (source.asWorld().random.nextFloat() < source.getCorruption().getScaled(1)) { ((Creature)eq).setDiscorded(true); } 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 82a66ad9..fea666b7 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 @@ -57,7 +57,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme } public boolean isLinked() { - return teleportationTarget.getTarget().isPresent(); + return teleportationTarget.isSet(); } public Optional> getTarget() { @@ -85,7 +85,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme } @SuppressWarnings("unchecked") - private Optional> getTarget(Caster source) { + private Optional> getDestination(Caster source) { return getTarget().map(target -> Ether.get(source.asWorld()).get((SpellType)getType(), target, targetPortalId)); } @@ -98,13 +98,12 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme @Override public boolean tick(Caster source, Situation situation) { if (situation == Situation.GROUND) { - if (source.isClient()) { source.spawnParticles(particleArea, 5, pos -> { source.addParticle(ParticleTypes.ELECTRIC_SPARK, pos, Vec3d.ZERO); }); } else { - getTarget().ifPresent(target -> { + teleportationTarget.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); @@ -113,16 +112,17 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme } }); - getTarget(source).ifPresentOrElse( + getDestination(source).ifPresentOrElse( entry -> tickWithTargetLink(source, entry), () -> findLink(source) ); } - var entry = Ether.get(source.asWorld()).getOrCreate(this, source); + Ether ether = Ether.get(source.asWorld()); + var entry = ether.getOrCreate(this, source); entry.pitch = pitch; entry.yaw = yaw; - Ether.get(source.asWorld()).markDirty(); + ether.markDirty(); } return !isDead(); @@ -185,8 +185,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme } Ether.get(source.asWorld()).anyMatch(getType(), entry -> { - if (entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet()) { - entry.setTaken(true); + if (!entry.entity.referenceEquals(source.asEntity()) && entry.claim()) { teleportationTarget.copyFrom(entry.entity); targetPortalId = entry.getSpellId(); setDirty(); @@ -216,9 +215,8 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme @Override protected void onDestroyed(Caster caster) { - Ether ether = Ether.get(caster.asWorld()); - ether.remove(getType(), caster); - getTarget(caster).ifPresent(e -> e.setTaken(false)); + Ether.get(caster.asWorld()).remove(getType(), caster); + getDestination(caster).ifPresent(Ether.Entry::release); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntityReference.java b/src/main/java/com/minelittlepony/unicopia/entity/EntityReference.java index ad6a4d4c..c401c1ce 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/EntityReference.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntityReference.java @@ -22,7 +22,7 @@ import net.minecraft.world.World; /** * An indirect reference to an entity by its unique id. - * Used to store the 'owner' reference for certain objects that allows them to\ + * Used to store the 'owner' reference for certain objects that allows them to * remember who they belong to even when the entity has been unloaded. * * Will also remember the position and certain attributes of the owner. 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 8d2e309c..d60b7607 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/Ether.java @@ -211,7 +211,7 @@ public class Ether extends PersistentState { } public boolean isAvailable() { - return !isDead() && !taken; + return !isDead() && !taken && entity.isSet(); } public void setTaken(boolean taken) { @@ -219,6 +219,18 @@ public class Ether extends PersistentState { markDirty(); } + public void release() { + setTaken(false); + } + + public boolean claim() { + if (isAvailable()) { + setTaken(true); + return true; + } + return false; + } + @Nullable public T getSpell() { if (removed) { From d6d69849558a1b6f396989fe8744d13cbf1bd458 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 8 Apr 2024 16:55:10 +0100 Subject: [PATCH 09/32] Fix some offset issues with the dark vortex spell --- .../magic/spell/effect/DarkVortexSpell.java | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 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 c1d79205..be09a09c 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 @@ -101,6 +101,8 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate if (situation == Situation.BODY) { return true; } + + Vec3d origin = getOrigin(source); double mass = getMass() * 0.1; double logarithm = 1 - (1D / (1 + (mass * mass))); radius.update((float)Math.max(0.1, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L); @@ -114,7 +116,6 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate if (source.isClient()) { if (eventHorizon > 0.3) { double range = eventHorizon * 2; - Vec3d origin = getOrigin(source); source.spawnParticles(origin, new Sphere(false, range), 50, p -> { source.addParticle( new FollowingParticleEffect(UParticles.HEALTH_DRAIN, origin, 0.4F) @@ -125,12 +126,11 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate }); } } else if (source.asWorld().random.nextInt(300) == 0) { - ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, getOrigin(source), Vec3d.ZERO); + ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, origin, Vec3d.ZERO); } if (!source.isClient()) { if (eventHorizon > 2) { - Vec3d origin = getOrigin(source); new Sphere(false, eventHorizon + 3).translate(origin).randomPoints(10, source.asWorld().random).forEach(i -> { BlockPos pos = BlockPos.ofFloored(i); if (!source.asWorld().isAir(pos)) { @@ -143,7 +143,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate } } - Vec3d origin = getOrigin(source); + for (Entity insideEntity : source.findAllEntitiesInRange(eventHorizon * 0.5F).toList()) { insideEntity.setVelocity(Vec3d.ZERO); Living.updateVelocity(insideEntity); @@ -151,22 +151,22 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate if (insideEntity instanceof CastSpellEntity s && getType().isOn(insideEntity)) { setDead(); s.getSpellSlot().clear(); - source.asWorld().createExplosion(source.asEntity(), source.getOrigin().getX(), source.getOrigin().getY(), source.getOrigin().getZ(), 12, ExplosionSourceType.NONE); + source.asWorld().createExplosion(source.asEntity(), origin.x, origin.y, origin.z, 12, ExplosionSourceType.NONE); source.asWorld().createExplosion(source.asEntity(), insideEntity.getX(), insideEntity.getY(), insideEntity.getZ(), 12, ExplosionSourceType.NONE); return false; } } targetSelecter.getEntities(source, getDrawDropOffRange()).forEach(i -> { try { - affectEntity(source, i, i.getPos().distanceTo(origin)); + affectEntity(source, i, origin); } catch (Throwable e) { Unicopia.LOGGER.error("Error updating radial effect", e); } }); - if (!source.subtractEnergyCost(-accumulatedMass)) { + if (!source.subtractEnergyCost(accumulatedMass * 0.001)) { setDead(); - source.asWorld().createExplosion(source.asEntity(), source.getOrigin().getX(), source.getOrigin().getY(), source.getOrigin().getZ(), 3, ExplosionSourceType.NONE); + source.asWorld().createExplosion(source.asEntity(), origin.x, origin.y, origin.z, 3, ExplosionSourceType.NONE); } return true; @@ -188,8 +188,8 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate @Override public void onImpact(MagicProjectileEntity projectile, BlockHitResult hit) { if (!projectile.isClient() && projectile instanceof MagicBeamEntity source) { - BlockPos pos = hit.getBlockPos(); - projectile.getWorld().createExplosion(projectile, pos.getX(), pos.getY(), pos.getZ(), 12, ExplosionSourceType.NONE); + Vec3d pos = hit.getPos(); + projectile.getWorld().createExplosion(projectile, pos.x, pos.y, pos.z, 12, ExplosionSourceType.NONE); toPlaceable().tick(source, Situation.BODY); } } @@ -208,7 +208,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate } public double getYOffset() { - return 3 - radius.getValue() * 0.5; + return 2; } private boolean canAffect(Caster source, BlockPos pos) { @@ -224,24 +224,29 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate } return; } - if (source.getOrigin().isWithinDistance(pos, getEventHorizonRadius())) { + if (pos.isWithinDistance(origin, getEventHorizonRadius())) { source.asWorld().breakBlock(pos, false); - if (!source.asWorld().getFluidState(pos).isEmpty()) { - source.asWorld().setBlockState(pos, Blocks.AIR.getDefaultState()); - } + updateStatePostRemoval(source, pos); } else { CatapultSpell.createBlockEntity(source.asWorld(), pos, e -> { + updateStatePostRemoval(source, pos); e.addVelocity(0, 0.1, 0); - if (e instanceof PlayerEntity) { - affectEntity(source, e, e.getPos().distanceTo(getOrigin(source))); - } }); } } - private void affectEntity(Caster source, Entity target, double distance) { - if (distance <= getEventHorizonRadius() + 0.5) { - target.setVelocity(target.getVelocity().multiply(distance < 1 ? distance : distance / (2 * getEventHorizonRadius()))); + private void updateStatePostRemoval(Caster source, BlockPos pos) { + if (!source.asWorld().getFluidState(pos).isEmpty()) { + source.asWorld().setBlockState(pos, Blocks.AIR.getDefaultState()); + } + } + + private void affectEntity(Caster source, Entity target, Vec3d origin) { + double distance = target.getPos().distanceTo(origin); + double eventHorizonRadius = getEventHorizonRadius(); + + if (distance <= eventHorizonRadius + 0.5) { + target.setVelocity(target.getVelocity().multiply(distance < 1 ? distance : distance / (2 * eventHorizonRadius))); Living.updateVelocity(target); @Nullable @@ -278,16 +283,14 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate source.subtractEnergyCost(-massOfTarget * 10); - if (target instanceof PlayerEntity && distance < getEventHorizonRadius() + 5) { + if (target instanceof PlayerEntity && distance < eventHorizonRadius + 5) { source.asWorld().playSound(null, target.getBlockPos(), USounds.AMBIENT_DARK_VORTEX_MOOD, SoundCategory.AMBIENT, 2, 0.02F); } } else { double force = getAttractiveForce(source, target); - AttractionUtils.applyForce(getOrigin(source), target, -force, 0, true); - - source.subtractEnergyCost(-2); + AttractionUtils.applyForce(origin, target, -force, 0, true); } } From b84f1f046bfaf775a16ea1a9ece730923a1f85c5 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 8 Apr 2024 17:48:42 +0100 Subject: [PATCH 10/32] Add damage type for the black hole to bypass armor --- .../data/minecraft/tags/damage_type/bypasses_armor.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/data/minecraft/tags/damage_type/bypasses_armor.json b/src/main/resources/data/minecraft/tags/damage_type/bypasses_armor.json index 9f715f8f..d2722662 100644 --- a/src/main/resources/data/minecraft/tags/damage_type/bypasses_armor.json +++ b/src/main/resources/data/minecraft/tags/damage_type/bypasses_armor.json @@ -2,6 +2,7 @@ "replace": false, "values": [ "unicopia:magical_exhaustion", + "unicopia:gravity_well_recoil", "unicopia:alicorn_amulet", "unicopia:zap", "unicopia:kick", From 978b5e9854f9667759d166a40f98b5379c2aae6d Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 8 Apr 2024 17:49:44 +0100 Subject: [PATCH 11/32] Fix decay animation and improve rendering a little --- .../ability/magic/spell/PlaceableSpell.java | 2 +- .../magic/spell/effect/DarkVortexSpell.java | 4 +- .../render/spell/DarkVortexSpellRenderer.java | 55 +++++++++---------- 3 files changed, 28 insertions(+), 33 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 51c26ea8..9f179443 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 @@ -98,7 +98,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS @Override public boolean isDead() { - return dead && deathTicks <= 0; + return dead && deathTicks <= 0 && super.isDead(); } @Override 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 be09a09c..9ca99fc5 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 @@ -164,7 +164,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate } }); - if (!source.subtractEnergyCost(accumulatedMass * 0.001)) { + if (!source.subtractEnergyCost(0.01)) { setDead(); source.asWorld().createExplosion(source.asEntity(), origin.x, origin.y, origin.z, 3, ExplosionSourceType.NONE); } @@ -175,7 +175,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate @Override public void tickDying(Caster source) { - accumulatedMass /= 2D; + accumulatedMass *= 0.8F; double mass = getMass() * 0.1; double logarithm = 1 - (1D / (1 + (mass * mass))); radius.update((float)Math.max(0.1, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L); 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 2134f8c3..f8a84c1a 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 @@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.DarkVortexSpell; import com.minelittlepony.unicopia.client.render.RenderLayers; 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.Camera; import net.minecraft.client.render.RenderLayer; @@ -16,6 +17,7 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RotationAxis; +import net.minecraft.util.math.Vec3d; public class DarkVortexSpellRenderer extends SpellRenderer { @@ -38,12 +40,15 @@ public class DarkVortexSpellRenderer extends SpellRenderer { 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) { Camera camera = MinecraftClient.getInstance().gameRenderer.getCamera(); + Vec3d ray = camera.getPos().subtract(spell.getOrigin(caster)); + float radius = (float)spell.getEventHorizonRadius(); - float absDistance = (float)camera.getPos().distanceTo(spell.getOrigin(caster)); + float absDistance = (float)ray.length(); matrices.push(); matrices.translate(0, spell.getYOffset(), 0); + matrices.multiply(RotationAxis.NEGATIVE_Y.rotationDegrees(-caster.asEntity().getYaw())); float visualRadius = Math.min(radius * 0.8F, absDistance - 1F); @@ -53,11 +58,6 @@ public class DarkVortexSpellRenderer extends SpellRenderer { SphereModel.SPHERE.render(matrices, vertices.getBuffer(RenderLayers.getMagicColored()), light, 1, visualRadius + 0.15F, 0, 0, 0, 0.9F); matrices.push(); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(90)); - matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90 + camera.getYaw())); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-camera.getPitch())); - - matrices.scale(0.7F, 1, 1); float distance = 1F / MathHelper.clamp(absDistance / (radius + 7), 0.0000001F, 1); distance *= distance; @@ -67,46 +67,41 @@ public class DarkVortexSpellRenderer extends SpellRenderer { SphereModel.DISK.render(matrices, vertices.getBuffer(RenderLayers.getEndPortal()), light, 1, radius * 0.5F, 0, 0, 0, 0); - matrices.push(); - matrices.scale(distance, distance, distance); - - if (absDistance > radius) { - matrices.push(); - matrices.translate(0, -0.1F, 0); - for (int i = 0; i < 10; i++) { - 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(); - - - if (radius > 0.3F && absDistance > radius) { + double g = Math.sqrt(ray.x * ray.x + ray.z * ray.z); + float pitch = MathHelper.wrapDegrees((float)(-(MathHelper.atan2(ray.y, g) * 180.0F / (float)Math.PI))); + float yaw = MathHelper.wrapDegrees((float)(MathHelper.atan2(ray.z, ray.x) * 180.0F / (float)Math.PI) - 90.0F); + + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(caster.asEntity().getYaw())); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-yaw)); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-pitch)); + radius *= Math.min(2, 3 + radius); float processionSpeed = animationProgress * 0.02F; float maxProcessionAngle = 15; + float cosProcession = MathHelper.cos(processionSpeed); + float sinProcession = MathHelper.sin(processionSpeed); + float range = (float)spell.getDrawDropOffRange() / 8F; matrices.scale(range, range, range); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90 + MathHelper.cos(processionSpeed) * maxProcessionAngle)); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(MathHelper.sin(processionSpeed) * maxProcessionAngle)); + + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(cosProcession * maxProcessionAngle)); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(sinProcession * maxProcessionAngle)); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(animationProgress * 18)); + VertexConsumer buffer = vertices.getBuffer(RenderLayer.getEntityTranslucent(ACCRETION_DISK_TEXTURE)); PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); - - - - matrices.scale(0.9F, 0.9F, 0.9F); + float secondaryScale = 0.9F + cosProcession * 0.3F; + matrices.translate(0, 0, 0.0001F); + matrices.scale(secondaryScale, secondaryScale, secondaryScale); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(33)); - PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); - + matrices.translate(0, 0, 0.0001F); matrices.scale(0.9F, 0.9F, 0.9F); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(33)); PlaneModel.INSTANCE.render(matrices, buffer, light, 0, 1, 1, 1, 1, 1); From 281e2ba26ddff9a78ffc246fcd93fb1d4b3a6727 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 8 Apr 2024 19:18:56 +0100 Subject: [PATCH 12/32] Add unique gem designs for different spells --- .../ability/magic/spell/effect/SpellType.java | 24 ++++++++++ .../unicopia/client/URenderers.java | 3 +- .../datagen/providers/ModelOverrides.java | 7 +++ .../datagen/providers/UModelProvider.java | 5 ++- .../unicopia/item/GemstoneItem.java | 42 ++++++++++++++---- .../resources/assets/unicopia/lang/en_us.json | 2 +- .../unicopia/textures/item/gemstone_arrow.png | Bin 0 -> 8736 bytes .../unicopia/textures/item/gemstone_brush.png | Bin 0 -> 3034 bytes .../unicopia/textures/item/gemstone_cross.png | Bin 0 -> 2935 bytes .../unicopia/textures/item/gemstone_donut.png | Bin 0 -> 2876 bytes .../unicopia/textures/item/gemstone_flame.png | Bin 0 -> 2965 bytes .../unicopia/textures/item/gemstone_ice.png | Bin 0 -> 2992 bytes .../textures/item/gemstone_lambda.png | Bin 0 -> 6327 bytes .../unicopia/textures/item/gemstone_ring.png | Bin 0 -> 2911 bytes .../textures/item/gemstone_rocket.png | Bin 0 -> 2942 bytes .../{gemstone_pure.png => gemstone_round.png} | Bin .../textures/item/gemstone_shield.png | Bin 0 -> 2992 bytes .../unicopia/textures/item/gemstone_skull.png | Bin 0 -> 2944 bytes .../textures/item/gemstone_splint.png | Bin 0 -> 2866 bytes .../unicopia/textures/item/gemstone_star.png | Bin 0 -> 8917 bytes .../textures/item/gemstone_triangle.png | Bin 0 -> 8728 bytes .../textures/item/gemstone_vortex.png | Bin 0 -> 3007 bytes .../unicopia/textures/item/gemstone_wave.png | Bin 0 -> 2920 bytes 23 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_arrow.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_brush.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_cross.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_donut.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_flame.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_ice.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_lambda.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_ring.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_rocket.png rename src/main/resources/assets/unicopia/textures/item/{gemstone_pure.png => gemstone_round.png} (100%) create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_shield.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_skull.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_splint.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_star.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_triangle.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_vortex.png create mode 100644 src/main/resources/assets/unicopia/textures/item/gemstone_wave.png diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java index 90bbed43..2179eab1 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java @@ -20,6 +20,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.Spell; import com.minelittlepony.unicopia.ability.magic.spell.ThrowableSpell; import com.minelittlepony.unicopia.ability.magic.spell.TimeControlAbilitySpell; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; +import com.minelittlepony.unicopia.item.GemstoneItem; import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.util.RegistryUtils; import com.mojang.brigadier.context.CommandContext; @@ -133,6 +134,29 @@ public final class SpellType implements Affine, SpellPredicate< return affinity; } + public GemstoneItem.Shape getGemShape() { + if (this == NECROMANCY) return GemstoneItem.Shape.SKULL; + if (this == DARK_VORTEX) return GemstoneItem.Shape.VORTEX; + if (this == FROST || this == CHILLING_BREATH) return GemstoneItem.Shape.TRIANGLE; + if (this == SHIELD || this == ARCANE_PROTECTION) return GemstoneItem.Shape.SHIELD; + if (this == FLAME || this == SCORCH || this == INFERNAL || this == FIRE_BOLT) return GemstoneItem.Shape.FLAME; + if (this == MIMIC) return GemstoneItem.Shape.ARROW; + if (this == DISPEL_EVIL) return GemstoneItem.Shape.CROSS; + if (this == REVEALING) return GemstoneItem.Shape.CROSS; + if (this == SIPHONING) return GemstoneItem.Shape.LAMBDA; + if (this == VORTEX) return GemstoneItem.Shape.VORTEX; + if (this == FEATHER_FALL) return GemstoneItem.Shape.LAMBDA; + if (this == DISPLACEMENT) return GemstoneItem.Shape.BRUSH; + if (this == TRANSFORMATION) return GemstoneItem.Shape.BRUSH; + if (this == BUBBLE) return GemstoneItem.Shape.DONUT; + if (this == MIND_SWAP) return GemstoneItem.Shape.WAVE; + if (this == HYDROPHOBIC || this == CATAPULT) return GemstoneItem.Shape.ROCKET; + if (this == LIGHT) return GemstoneItem.Shape.STAR; + if (this == PORTAL) return GemstoneItem.Shape.RING; + if (this == AWKWARD) return GemstoneItem.Shape.ICE; + return GemstoneItem.Shape.ROUND; + } + public SpellTraits getTraits() { return traits; } diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 747465b5..704f90cb 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -122,7 +122,8 @@ public interface URenderers { register(URenderers::renderBedItem, UItems.CLOTH_BED, UItems.CLOUD_BED); register(URenderers::renderChestItem, UBlocks.CLOUD_CHEST.asItem()); 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.GEMSTONE, new Identifier("affinity"), (stack, world, entity, seed) -> EnchantableItem.getSpellKey(stack).getAffinity().getAlignment()); + ModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("shape"), (stack, world, entity, seed) -> EnchantableItem.getSpellKey(stack).getGemShape().getId()); ModelPredicateProviderRegistry.register(UItems.ROCK_CANDY, new Identifier("count"), (stack, world, entity, seed) -> stack.getCount() / (float)stack.getMaxCount()); ModelPredicateProviderRegistry.register(UItems.BUTTERFLY, new Identifier("variant"), (stack, world, entity, seed) -> (float)ButterflyItem.getVariant(stack).ordinal() / ButterflyEntity.Variant.VALUES.length); ModelPredicateProviderRegistry.register(Unicopia.id("zap_cycle"), new ClampedModelPredicateProvider() { diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/ModelOverrides.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/ModelOverrides.java index d5ff1b05..c0744cbe 100644 --- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/ModelOverrides.java +++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/ModelOverrides.java @@ -28,6 +28,13 @@ public final class ModelOverrides { this.model = model; } + public ModelOverrides addUniform(String key, Iterable values, Function idFunc, Function childModelSupplier) { + for (T t : values) { + addOverride(childModelSupplier.apply(t), key, idFunc.apply(t)); + } + return this; + } + public ModelOverrides addUniform(String key, int from, int to, Identifier model) { float step = 1F / to; for (int index = from; index <= to; index++) { diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java index cc317eb5..6861d8ed 100644 --- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java +++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java @@ -1,12 +1,14 @@ package com.minelittlepony.unicopia.datagen.providers; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.CompletableFuture; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.datagen.DataCollector; import com.minelittlepony.unicopia.item.BedsheetsItem; +import com.minelittlepony.unicopia.item.GemstoneItem; import com.minelittlepony.unicopia.item.UItems; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider; @@ -137,8 +139,7 @@ public class UModelProvider extends FabricModelProvider { // gemstone ModelOverrides.of(ItemModels.GENERATED) - .addOverride(ModelIds.getItemSubModelId(UItems.GEMSTONE, "_pure"), "affinity", 0) - .addOverride(ModelIds.getItemSubModelId(UItems.GEMSTONE, "_corrupted"), "affinity", 1) + .addUniform("shape", List.of(GemstoneItem.Shape.values()), GemstoneItem.Shape::getId, shape -> ModelIds.getItemSubModelId(UItems.GEMSTONE, "_" + shape.name().toLowerCase(Locale.ROOT))) .upload(UItems.GEMSTONE, itemModelGenerator); // fishing rod diff --git a/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java b/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java index dcc35a05..8cb17b77 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/GemstoneItem.java @@ -1,12 +1,10 @@ package com.minelittlepony.unicopia.item; -import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.function.Predicate; - import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; @@ -96,12 +94,13 @@ public class GemstoneItem extends Item implements MultiItem, EnchantableItem { @Override public List getDefaultStacks() { - return Arrays.stream(Affinity.VALUES) - .flatMap(i -> SpellType.byAffinity(i).stream() - .filter(type -> type.isObtainable()) - .map(type -> EnchantableItem.enchant(getDefaultStack(), type, i)) - ) - .toList(); + return SpellType.REGISTRY.stream() + .filter(SpellType::isObtainable) + .sorted( + Comparator., GemstoneItem.Shape>comparing(SpellType::getGemShape).thenComparing(Comparator.comparing(SpellType::getAffinity)) + ) + .map(type -> EnchantableItem.enchant(getDefaultStack(), type)) + .toList(); } @Override @@ -121,4 +120,29 @@ public class GemstoneItem extends Item implements MultiItem, EnchantableItem { return super.getName(); } + public enum Shape { + ARROW, + BRUSH, + CROSS, + DONUT, + FLAME, + ICE, + LAMBDA, + RING, + ROCKET, + ROUND, + SHIELD, + SKULL, + SPLINT, + STAR, + TRIANGLE, + VORTEX, + WAVE; + + public static final int LENGTH = values().length; + + public float getId() { + return ordinal() / (float)LENGTH; + } + } } diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index df8a8f2e..736626b1 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -544,7 +544,7 @@ "spell.unicopia.shield.lore": "Casts a protective shield around the user", "spell.unicopia.bubble": "Bubble", "spell.unicopia.bubble.lore": "Traps any creature it hits in a soap bubble", - "spell.unicopia.arcane_protection": "Arcane Protections", + "spell.unicopia.arcane_protection": "Arcane Protection", "spell.unicopia.arcane_protection.lore": "Creates a protective shroud over an area in which no other spells can be cast", "spell.unicopia.vortex": "Arcane Attraction", "spell.unicopia.vortex.lore": "Creates a magnetic force that pulls in other targets", diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_arrow.png b/src/main/resources/assets/unicopia/textures/item/gemstone_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e51a73ae1d570e933728b9962db8537c66030a0d GIT binary patch literal 8736 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_brush.png b/src/main/resources/assets/unicopia/textures/item/gemstone_brush.png new file mode 100644 index 0000000000000000000000000000000000000000..fb7ebb21e10dc44b35b524ab7425557c1ca95d49 GIT binary patch literal 3034 zcmV<03nlc4P)EX>4Tx07!|QmUmQC*A|D*y?1({%`g-xL+`x}AiX!K(nMjH8DJ;_4l^{dA)*2i zMMMM@L4qO%jD{kyB8r88V8I@cAfUux6j4!mGqP56<>kGXm){>}eQTe+_dRFteb%}F zki7l5ymVL!fHa~vAmcQ z7uoQ$&mudEnVrUCi&%W-40ak@%snFBnkD3j81WZzQ5KhzE#g}u)=U+qaYg)A9Gk{r zW&(gBiR}UoD@nwrA|~;}Lfk~W6aXA4@hgu1iUph;f%sBx=^43vZeo&vuFKM+o7vhj z=-!;{RE|Jk6vSkuF!^k{TY6dsla~v?;+;QBMqFFEsL0l4w$|20=Ei1U73#lk{!NK{ zyGXBsKlcox^?kAZm0x;20E}5tZFYRI#qR~6V>1Bq_rKUQ4+0=5>RbE3SNEZb=OsxX z$gndp$O~ z2}Gii1cZ;QLyD0~q#kKOx{zMvCNhFdBkxcc6a_^`8KLY^-l*j$7HTzW9jX*njXHvA zNA;j?qDE0Os847zS_y4{wnO`%BhiWIY;+O265WVyLtjGQMvtT4U@#aOMh9bq@y0}9 zk}+#ArI`JgR?K_yPPlex4vr&>=Vw!U)NPjf5&f z3*i#sA>kE~NK_}<5`&3c;s#Leh59VbXchJ<=;OnXFBA zCP$M6>atgt3H=1Y2UgM2$qd#E`@bNxY<%q>JP#$vnwQ$&-=;lG9Rn zDQzh?DW=pqsT!$MQo~ZS(iCYk=|Jf;=~C&V(pRM?Ww0{ZG9EH)nL?REG8bjWC@3{{8fLrtcZP`{)0Q)gslWG!XGWpiX}WY5Ts&=8t7&4-psE2EvD z-J!jgQfv(`8kfN|tp+n)3B1%zTF<3EM z@qpqb#pxx~CH6~LONy7ASaM$pR?=4rQCg#PNU2Y0R#`>aOF2V%ukuCZX%(7^vr4i` zh00l#DOHN9qbgUmLiL>LGrBC@g`P^UqW92e)Rfe`)r4wwYW-^S>N@Jn)eF>H)gNgP zG#DBQ8WkGd8Z(-zngN>mn$4Q`weVUDtt72ITD@9x+B(`1+FP_cv?q1sb$oR4beeS@ z>XLPxbXV)v>)z7C=rQzC^!DrB(1-P{^po^!^al)J18W1W!G425L$sl-Ayeeqo|%5^b{6q}Sw=sg-G}X@ltlGZ`~qvjVd&v)|42%~|F( z=C>@!7M>RCEjle;S{hh#EDu=TwW3%BSZ%TDw)$voW6ig2v7WNgw28CXXEV&8GJ+VT zj4QTiTUXolwx@01*;(5O>`vJIW^ZJlVt>?ra;eTz&eDdZV-D&LOouv$5l6aXoZ~^q z5hpb#rc=Gs6K4%)wsWKNgo~a_vdb}-7p|tReAhPDIX64EwQlF#5qB^5V)uRz8IR>2 z)gF&M)jbnEn>}Z|ti0BEo%cq2`+4v59`;f8Vfi%q%=p^)uJ!HlBl(5;Rr@{h*Z1f9 zcLl%!z5%-e9xl^b##`1A2m*ZqcLhEQ(g|7}^kXn4I4HO#_-Tk)NPb9fC?zyD^l0dt zFxRlMum{U^mkXD7hf9XXgg1rHMYu zc#Ks{QOuo{IxBNlUR|ZQDs|PFSjkvs?8!KETtwW_xDU)gW<7H@-Y0%v{0z&DwTJbb z?aZ!VPjMVL<(!EGhlKKk$wY_5U5QgkPDzzX(_A-hHTPw*cXDm=TuNZd;gp5ch}70J zTv}Y(DV_{3h1Zj=lAe=3m|>7nlrgf}ZuRcfGkiaOVz}3Y2Bx^Z`;1P{p|fi z2b>SI)GF7O)V@E+J$SdytFFCXyT0-e=1|t5rw!o^z27pvZE93(ENT3Bn0I*ONXU_% zCYz?Fqe@51n&D<)^VG4JV>iBY|E{yesHLuz)>?8L92Xvc_I=#J{_+2=_${t8_!le8-Jehe15v28 zmBOpTuPtA9&j!stev|fQey;ef!rLS781H)DN4%ey&;Ee@Q1wyoW7j9YPY)N;78d>m z1DNyt6gNdX00009a7bBm000id000id0mpBsWB>pF2XskIMF;2v2mlB$==TT20000P zbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbO7)eAyR5;7clD&(&Jq^vymJI3|x`QMo5z?-Tv9>tkMK*1R;gkC?{GNNF+pZ#bHpBLrbTeeKY%JcHtwj zisR}X4xrOExLmIv1gki%qUEyHq|+w8TrQ29(ln*bL_LcShXbzb0`NVV0I=Eb0Z5Vr z5y79&nR%W9=-muvo~IUJSWPVpEyD45r0mtljK^a{>w31a!9AiVsF>gRkW%Q9@+24J__F&qy08jb#s&{pCtb6OM> zMZtEvZIsx#FL7#Fh{#{tS`!$f0%MeDU5j1EdD!5ggTNT&Iu0Vz$a)h8#;BKHc@c)y cJKSgB7ygil>7vR7VE_OC07*qoM6N<$g5f3A>;M1& literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_cross.png b/src/main/resources/assets/unicopia/textures/item/gemstone_cross.png new file mode 100644 index 0000000000000000000000000000000000000000..c8670f004e30d8aa18c3cbd1d1bd9980fcf1c899 GIT binary patch literal 2935 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_donut.png b/src/main/resources/assets/unicopia/textures/item/gemstone_donut.png new file mode 100644 index 0000000000000000000000000000000000000000..4a47ebf59d700d9da21d7529176372d4b93d7201 GIT binary patch literal 2876 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 B7)JmA literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_ring.png b/src/main/resources/assets/unicopia/textures/item/gemstone_ring.png new file mode 100644 index 0000000000000000000000000000000000000000..e6e4aebde20ddf827802c8a2ae62e786e3f01726 GIT binary patch literal 2911 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_pure.png b/src/main/resources/assets/unicopia/textures/item/gemstone_round.png similarity index 100% rename from src/main/resources/assets/unicopia/textures/item/gemstone_pure.png rename to src/main/resources/assets/unicopia/textures/item/gemstone_round.png diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_shield.png b/src/main/resources/assets/unicopia/textures/item/gemstone_shield.png new file mode 100644 index 0000000000000000000000000000000000000000..2395fc6f51a89f91276914f668f3d41a253bf675 GIT binary patch literal 2992 zcmV;h3s3ZkP)EX>4Tx07!|QmUmQC*A|D*y?1({%`g-xL+`x}AiX!K(nMjH8DJ;_4l^{dA)*2i zMMMM@L4qO%jD{kyB8r88V8I@cAfUux6j4!mGqP56<>kGXm){>}eQTe+_dRFteb%}F zki7l5ymVL!fHa~vAmcQ z7uoQ$&mudEnVrUCi&%W-40ak@%snFBnkD3j81WZzQ5KhzE#g}u)=U+qaYg)A9Gk{r zW&(gBiR}UoD@nwrA|~;}Lfk~W6aXA4@hgu1iUph;f%sBx=^43vZeo&vuFKM+o7vhj z=-!;{RE|Jk6vSkuF!^k{TY6dsla~v?;+;QBMqFFEsL0l4w$|20=Ei1U73#lk{!NK{ zyGXBsKlcox^?kAZm0x;20E}5tZFYRI#qR~6V>1Bq_rKUQ4+0=5>RbE3SNEZb=OsxX z$gndp$O~ z2}Gii1cZ;QLyD0~q#kKOx{zMvCNhFdBkxcc6a_^`8KLY^-l*j$7HTzW9jX*njXHvA zNA;j?qDE0Os847zS_y4{wnO`%BhiWIY;+O265WVyLtjGQMvtT4U@#aOMh9bq@y0}9 zk}+#ArI`JgR?K_yPPlex4vr&>=Vw!U)NPjf5&f z3*i#sA>kE~NK_}<5`&3c;s#Leh59VbXchJ<=;OnXFBA zCP$M6>atgt3H=1Y2UgM2$qd#E`@bNxY<%q>JP#$vnwQ$&-=;lG9Rn zDQzh?DW=pqsT!$MQo~ZS(iCYk=|Jf;=~C&V(pRM?Ww0{ZG9EH)nL?REG8bjWC@3{{8fLrtcZP`{)0Q)gslWG!XGWpiX}WY5Ts&=8t7&4-psE2EvD z-J!jgQfv(`8kfN|tp+n)3B1%zTF<3EM z@qpqb#pxx~CH6~LONy7ASaM$pR?=4rQCg#PNU2Y0R#`>aOF2V%ukuCZX%(7^vr4i` zh00l#DOHN9qbgUmLiL>LGrBC@g`P^UqW92e)Rfe`)r4wwYW-^S>N@Jn)eF>H)gNgP zG#DBQ8WkGd8Z(-zngN>mn$4Q`weVUDtt72ITD@9x+B(`1+FP_cv?q1sb$oR4beeS@ z>XLPxbXV)v>)z7C=rQzC^!DrB(1-P{^po^!^al)J18W1W!G425L$sl-Ayeeqo|%5^b{6q}Sw=sg-G}X@ltlGZ`~qvjVd&v)|42%~|F( z=C>@!7M>RCEjle;S{hh#EDu=TwW3%BSZ%TDw)$voW6ig2v7WNgw28CXXEV&8GJ+VT zj4QTiTUXolwx@01*;(5O>`vJIW^ZJlVt>?ra;eTz&eDdZV-D&LOouv$5l6aXoZ~^q z5hpb#rc=Gs6K4%)wsWKNgo~a_vdb}-7p|tReAhPDIX64EwQlF#5qB^5V)uRz8IR>2 z)gF&M)jbnEn>}Z|ti0BEo%cq2`+4v59`;f8Vfi%q%=p^)uJ!HlBl(5;Rr@{h*Z1f9 zcLl%!z5%-e9xl^b##`1A2m*ZqcLhEQ(g|7}^kXn4I4HO#_-Tk)NPb9fC?zyD^l0dt zFxRlMum{U^mkXD7hf9XXgg1rHMYu zc#Ks{QOuo{IxBNlUR|ZQDs|PFSjkvs?8!KETtwW_xDU)gW<7H@-Y0%v{0z&DwTJbb z?aZ!VPjMVL<(!EGhlKKk$wY_5U5QgkPDzzX(_A-hHTPw*cXDm=TuNZd;gp5ch}70J zTv}Y(DV_{3h1Zj=lAe=3m|>7nlrgf}ZuRcfGkiaOVz}3Y2Bx^Z`;1P{p|fi z2b>SI)GF7O)V@E+J$SdytFFCXyT0-e=1|t5rw!o^z27pvZE93(ENT3Bn0I*ONXU_% zCYz?Fqe@51n&D<)^VG4JV>iBY|E{yesHLuz)>?8L92Xvc_I=#J{_+2=_${t8_!le8-Jehe15v28 zmBOpTuPtA9&j!stev|fQey;ef!rLS781H)DN4%ey&;Ee@Q1wyoW7j9YPY)N;78d>m z1DNyt6gNdX00009a7bBm000id000id0mpBsWB>pF2XskIMF;2v2mlZgzl`2_0000P zbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbN?ny*JR5;7cld)=qP!L6r z*Mn>Fbq*j(f2({Dbh3rAj`7f696cs7{_tx(==`F z?PX2u`<{6i7{`$y2(Z>Z2e8(1@H|}CEX>4Tx07!|QmUmQC*A|D*y?1({%`g-xL+`x}AiX!K(nMjH8DJ;_4l^{dA)*2i zMMMM@L4qO%jD{kyB8r88V8I@cAfUux6j4!mGqP56<>kGXm){>}eQTe+_dRFteb%}F zki7l5ymVL!fHa~vAmcQ z7uoQ$&mudEnVrUCi&%W-40ak@%snFBnkD3j81WZzQ5KhzE#g}u)=U+qaYg)A9Gk{r zW&(gBiR}UoD@nwrA|~;}Lfk~W6aXA4@hgu1iUph;f%sBx=^43vZeo&vuFKM+o7vhj z=-!;{RE|Jk6vSkuF!^k{TY6dsla~v?;+;QBMqFFEsL0l4w$|20=Ei1U73#lk{!NK{ zyGXBsKlcox^?kAZm0x;20E}5tZFYRI#qR~6V>1Bq_rKUQ4+0=5>RbE3SNEZb=OsxX z$gndp$O~ z2}Gii1cZ;QLyD0~q#kKOx{zMvCNhFdBkxcc6a_^`8KLY^-l*j$7HTzW9jX*njXHvA zNA;j?qDE0Os847zS_y4{wnO`%BhiWIY;+O265WVyLtjGQMvtT4U@#aOMh9bq@y0}9 zk}+#ArI`JgR?K_yPPlex4vr&>=Vw!U)NPjf5&f z3*i#sA>kE~NK_}<5`&3c;s#Leh59VbXchJ<=;OnXFBA zCP$M6>atgt3H=1Y2UgM2$qd#E`@bNxY<%q>JP#$vnwQ$&-=;lG9Rn zDQzh?DW=pqsT!$MQo~ZS(iCYk=|Jf;=~C&V(pRM?Ww0{ZG9EH)nL?REG8bjWC@3{{8fLrtcZP`{)0Q)gslWG!XGWpiX}WY5Ts&=8t7&4-psE2EvD z-J!jgQfv(`8kfN|tp+n)3B1%zTF<3EM z@qpqb#pxx~CH6~LONy7ASaM$pR?=4rQCg#PNU2Y0R#`>aOF2V%ukuCZX%(7^vr4i` zh00l#DOHN9qbgUmLiL>LGrBC@g`P^UqW92e)Rfe`)r4wwYW-^S>N@Jn)eF>H)gNgP zG#DBQ8WkGd8Z(-zngN>mn$4Q`weVUDtt72ITD@9x+B(`1+FP_cv?q1sb$oR4beeS@ z>XLPxbXV)v>)z7C=rQzC^!DrB(1-P{^po^!^al)J18W1W!G425L$sl-Ayeeqo|%5^b{6q}Sw=sg-G}X@ltlGZ`~qvjVd&v)|42%~|F( z=C>@!7M>RCEjle;S{hh#EDu=TwW3%BSZ%TDw)$voW6ig2v7WNgw28CXXEV&8GJ+VT zj4QTiTUXolwx@01*;(5O>`vJIW^ZJlVt>?ra;eTz&eDdZV-D&LOouv$5l6aXoZ~^q z5hpb#rc=Gs6K4%)wsWKNgo~a_vdb}-7p|tReAhPDIX64EwQlF#5qB^5V)uRz8IR>2 z)gF&M)jbnEn>}Z|ti0BEo%cq2`+4v59`;f8Vfi%q%=p^)uJ!HlBl(5;Rr@{h*Z1f9 zcLl%!z5%-e9xl^b##`1A2m*ZqcLhEQ(g|7}^kXn4I4HO#_-Tk)NPb9fC?zyD^l0dt zFxRlMum{U^mkXD7hf9XXgg1rHMYu zc#Ks{QOuo{IxBNlUR|ZQDs|PFSjkvs?8!KETtwW_xDU)gW<7H@-Y0%v{0z&DwTJbb z?aZ!VPjMVL<(!EGhlKKk$wY_5U5QgkPDzzX(_A-hHTPw*cXDm=TuNZd;gp5ch}70J zTv}Y(DV_{3h1Zj=lAe=3m|>7nlrgf}ZuRcfGkiaOVz}3Y2Bx^Z`;1P{p|fi z2b>SI)GF7O)V@E+J$SdytFFCXyT0-e=1|t5rw!o^z27pvZE93(ENT3Bn0I*ONXU_% zCYz?Fqe@51n&D<)^VG4JV>iBY|E{yesHLuz)>?8L92Xvc_I=#J{_+2=_${t8_!le8-Jehe15v28 zmBOpTuPtA9&j!stev|fQey;ef!rLS781H)DN4%ey&;Ee@Q1wyoW7j9YPY)N;78d>m z1DNyt6gNdX00009a7bBm000id000id0mpBsWB>pF2XskIMF;2v2mlTd^2D$l0000P zbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbNzDYzuR5;7cQmYPxFc2J| zscldM1-hn&|NneMs6u&TzBbuhcD@Fi-G3J>DZQCM*u)nvR qxEbnbCSwfxzCY2w6j8eS|HT*56HDgUWoyL%0000EX>4Tx07!|QmUmQC*A|D*y?1({%`g-xL+`x}AiX!K(nMjH8DJ;_4l^{dA)*2i zMMMM@L4qO%jD{kyB8r88V8I@cAfUux6j4!mGqP56<>kGXm){>}eQTe+_dRFteb%}F zki7l5ymVL!fHa~vAmcQ z7uoQ$&mudEnVrUCi&%W-40ak@%snFBnkD3j81WZzQ5KhzE#g}u)=U+qaYg)A9Gk{r zW&(gBiR}UoD@nwrA|~;}Lfk~W6aXA4@hgu1iUph;f%sBx=^43vZeo&vuFKM+o7vhj z=-!;{RE|Jk6vSkuF!^k{TY6dsla~v?;+;QBMqFFEsL0l4w$|20=Ei1U73#lk{!NK{ zyGXBsKlcox^?kAZm0x;20E}5tZFYRI#qR~6V>1Bq_rKUQ4+0=5>RbE3SNEZb=OsxX z$gndp$O~ z2}Gii1cZ;QLyD0~q#kKOx{zMvCNhFdBkxcc6a_^`8KLY^-l*j$7HTzW9jX*njXHvA zNA;j?qDE0Os847zS_y4{wnO`%BhiWIY;+O265WVyLtjGQMvtT4U@#aOMh9bq@y0}9 zk}+#ArI`JgR?K_yPPlex4vr&>=Vw!U)NPjf5&f z3*i#sA>kE~NK_}<5`&3c;s#Leh59VbXchJ<=;OnXFBA zCP$M6>atgt3H=1Y2UgM2$qd#E`@bNxY<%q>JP#$vnwQ$&-=;lG9Rn zDQzh?DW=pqsT!$MQo~ZS(iCYk=|Jf;=~C&V(pRM?Ww0{ZG9EH)nL?REG8bjWC@3{{8fLrtcZP`{)0Q)gslWG!XGWpiX}WY5Ts&=8t7&4-psE2EvD z-J!jgQfv(`8kfN|tp+n)3B1%zTF<3EM z@qpqb#pxx~CH6~LONy7ASaM$pR?=4rQCg#PNU2Y0R#`>aOF2V%ukuCZX%(7^vr4i` zh00l#DOHN9qbgUmLiL>LGrBC@g`P^UqW92e)Rfe`)r4wwYW-^S>N@Jn)eF>H)gNgP zG#DBQ8WkGd8Z(-zngN>mn$4Q`weVUDtt72ITD@9x+B(`1+FP_cv?q1sb$oR4beeS@ z>XLPxbXV)v>)z7C=rQzC^!DrB(1-P{^po^!^al)J18W1W!G425L$sl-Ayeeqo|%5^b{6q}Sw=sg-G}X@ltlGZ`~qvjVd&v)|42%~|F( z=C>@!7M>RCEjle;S{hh#EDu=TwW3%BSZ%TDw)$voW6ig2v7WNgw28CXXEV&8GJ+VT zj4QTiTUXolwx@01*;(5O>`vJIW^ZJlVt>?ra;eTz&eDdZV-D&LOouv$5l6aXoZ~^q z5hpb#rc=Gs6K4%)wsWKNgo~a_vdb}-7p|tReAhPDIX64EwQlF#5qB^5V)uRz8IR>2 z)gF&M)jbnEn>}Z|ti0BEo%cq2`+4v59`;f8Vfi%q%=p^)uJ!HlBl(5;Rr@{h*Z1f9 zcLl%!z5%-e9xl^b##`1A2m*ZqcLhEQ(g|7}^kXn4I4HO#_-Tk)NPb9fC?zyD^l0dt zFxRlMum{U^mkXD7hf9XXgg1rHMYu zc#Ks{QOuo{IxBNlUR|ZQDs|PFSjkvs?8!KETtwW_xDU)gW<7H@-Y0%v{0z&DwTJbb z?aZ!VPjMVL<(!EGhlKKk$wY_5U5QgkPDzzX(_A-hHTPw*cXDm=TuNZd;gp5ch}70J zTv}Y(DV_{3h1Zj=lAe=3m|>7nlrgf}ZuRcfGkiaOVz}3Y2Bx^Z`;1P{p|fi z2b>SI)GF7O)V@E+J$SdytFFCXyT0-e=1|t5rw!o^z27pvZE93(ENT3Bn0I*ONXU_% zCYz?Fqe@51n&D<)^VG4JV>iBY|E{yesHLuz)>?8L92Xvc_I=#J{_+2=_${t8_!le8-Jehe15v28 zmBOpTuPtA9&j!stev|fQey;ef!rLS781H)DN4%ey&;Ee@Q1wyoW7j9YPY)N;78d>m z1DNyt6gNdX00009a7bBm000id000id0mpBsWB>pF2XskIMF;2v2mk^Dt;mj80000P zbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbNa7jc#R5;6HV4x+qcJADN zLTZ>OwS)U6%36oVLX#XYeuz?+CBO`7z7@JHjo=LL!Z06nWZvmr77 QQ~&?~07*qoM6N<$g5*1Us{jB1 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_star.png b/src/main/resources/assets/unicopia/textures/item/gemstone_star.png new file mode 100644 index 0000000000000000000000000000000000000000..35d634247a37b0e6b70f3952f90301d3b7ee4884 GIT binary patch literal 8917 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_triangle.png b/src/main/resources/assets/unicopia/textures/item/gemstone_triangle.png new file mode 100644 index 0000000000000000000000000000000000000000..e1cec73f71737f5a394b4f926317f329f6f48064 GIT binary patch literal 8728 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_vortex.png b/src/main/resources/assets/unicopia/textures/item/gemstone_vortex.png new file mode 100644 index 0000000000000000000000000000000000000000..6554e9a9cbe0acf0d8a757f5f442b9a6075b105e GIT binary patch literal 3007 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 Sj)*A$0000 Date: Mon, 8 Apr 2024 20:45:46 +0100 Subject: [PATCH 13/32] Move ability assignment to the races --- .../com/minelittlepony/unicopia/Affinity.java | 14 +- .../minelittlepony/unicopia/Availability.java | 15 +- .../minelittlepony/unicopia/FlightType.java | 14 +- .../com/minelittlepony/unicopia/Race.java | 241 +++++++++++++----- .../unicopia/ability/Ability.java | 4 +- .../ability/AbstractSpellCastingAbility.java | 7 - .../unicopia/ability/BatEeeeAbility.java | 6 - .../unicopia/ability/BatPonyHangAbility.java | 6 - .../unicopia/ability/CarryAbility.java | 6 - .../ability/ChangelingFeedAbility.java | 6 - .../ability/EarthPonyGrowAbility.java | 6 - .../ability/EarthPonyKickAbility.java | 5 - .../ability/EarthPonyStompAbility.java | 5 - .../unicopia/ability/FlyingDashAbility.java | 6 - .../unicopia/ability/HugAbility.java | 7 - .../unicopia/ability/KirinCastingAbility.java | 6 - .../unicopia/ability/KirinRageAbility.java | 6 - .../unicopia/ability/NirikBlastAbility.java | 6 - .../unicopia/ability/PeckAbility.java | 6 - .../ability/PegasusCaptureStormAbility.java | 6 - .../ability/PegasusRainboomAbility.java | 6 - .../unicopia/ability/ScreechAbility.java | 6 - .../ability/SeaponySonarPulseAbility.java | 6 - .../unicopia/ability/TimeChangeAbility.java | 5 - .../unicopia/ability/ToggleFlightAbility.java | 5 - .../ability/UnicornDispellAbility.java | 5 - .../ability/UnicornTeleportAbility.java | 6 - 27 files changed, 217 insertions(+), 200 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Affinity.java b/src/main/java/com/minelittlepony/unicopia/Affinity.java index 35285a76..fb34045a 100644 --- a/src/main/java/com/minelittlepony/unicopia/Affinity.java +++ b/src/main/java/com/minelittlepony/unicopia/Affinity.java @@ -1,12 +1,17 @@ package com.minelittlepony.unicopia; -import net.minecraft.util.Formatting; +import java.util.Locale; -public enum Affinity { +import net.minecraft.util.Formatting; +import net.minecraft.util.StringIdentifiable; + +public enum Affinity implements StringIdentifiable { GOOD(Formatting.BLUE, -1, 0), NEUTRAL(Formatting.LIGHT_PURPLE, 0, 0.5F), BAD(Formatting.RED, 1, 1); + public static final Codec CODEC = StringIdentifiable.createCodec(Affinity::values); + private final Formatting color; private final int corruption; @@ -20,6 +25,11 @@ public enum Affinity { this.alignment = alignment; } + @Override + public String asString() { + return name().toLowerCase(Locale.ROOT); + } + public Formatting getColor() { return color; } diff --git a/src/main/java/com/minelittlepony/unicopia/Availability.java b/src/main/java/com/minelittlepony/unicopia/Availability.java index f24497a3..a8df720a 100644 --- a/src/main/java/com/minelittlepony/unicopia/Availability.java +++ b/src/main/java/com/minelittlepony/unicopia/Availability.java @@ -1,10 +1,23 @@ package com.minelittlepony.unicopia; -public enum Availability { +import java.util.Locale; + +import net.minecraft.util.StringIdentifiable; + +public enum Availability implements StringIdentifiable { DEFAULT, COMMANDS, NONE; + public static final Codec CODEC = StringIdentifiable.createCodec(Availability::values); + + private final String name = name().toLowerCase(Locale.ROOT); + + @Override + public String asString() { + return name; + } + public boolean isSelectable() { return this == DEFAULT; } diff --git a/src/main/java/com/minelittlepony/unicopia/FlightType.java b/src/main/java/com/minelittlepony/unicopia/FlightType.java index f2ef92f0..9faca7f4 100644 --- a/src/main/java/com/minelittlepony/unicopia/FlightType.java +++ b/src/main/java/com/minelittlepony/unicopia/FlightType.java @@ -1,15 +1,27 @@ package com.minelittlepony.unicopia; +import java.util.Locale; + import net.minecraft.entity.player.PlayerEntity; import net.minecraft.sound.SoundEvent; +import net.minecraft.util.StringIdentifiable; -public enum FlightType { +public enum FlightType implements StringIdentifiable { UNSET, NONE, AVIAN, INSECTOID, ARTIFICIAL; + public static final Codec CODEC = StringIdentifiable.createCodec(FlightType::values); + + private final String name = name().toLowerCase(Locale.ROOT); + + @Override + public String asString() { + return name; + } + public boolean isGrounded() { return this == NONE; } diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index dd3bf4d9..9e06f047 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -1,20 +1,21 @@ package com.minelittlepony.unicopia; import java.util.*; +import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; -import org.spongepowered.include.com.google.common.base.Objects; - import com.google.common.base.Strings; -import com.google.common.base.Suppliers; +import com.minelittlepony.unicopia.ability.Abilities; +import com.minelittlepony.unicopia.ability.Ability; import com.minelittlepony.unicopia.ability.magic.Affine; import com.minelittlepony.unicopia.util.RegistryUtils; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.command.argument.RegistryKeyArgumentType; import net.minecraft.entity.player.PlayerEntity; @@ -22,52 +23,89 @@ import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; import net.minecraft.util.Identifier; +import net.minecraft.util.Util; import net.minecraft.registry.Registry; import net.minecraft.registry.RegistryKey; -public record Race (Supplier compositeSupplier, Availability availability, boolean canCast, FlightType flightType, boolean canUseEarth, boolean isNocturnal, boolean canHang) implements Affine { +public record Race ( + List> abilities, + Affinity affinity, + Availability availability, + FlightType flightType, + boolean canCast, + boolean hasIronGut, + boolean canUseEarth, + boolean isNocturnal, + boolean canHang, + boolean isFish, + boolean canInfluenceWeather, + boolean canInteractWithClouds + ) implements Affine { public static final String DEFAULT_ID = "unicopia:unset"; public static final Registry REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID); public static final Registry COMMAND_REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race/grantable"), DEFAULT_ID); public static final RegistryKey> REGISTRY_KEY = REGISTRY.getKey(); private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("race.unknown", id)); + private static final Function COMPOSITES = Util.memoize(race -> new Composite(race, null, null)); - public static Race register(String name, Availability availability, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) { - return register(Unicopia.id(name), availability, magic, flight, earth, nocturnal, canHang); - } - - public static Race register(Identifier id, Availability availability, boolean magic, FlightType flight, boolean earth, boolean nocturnal, boolean canHang) { - Race race = Registry.register(REGISTRY, id, new Race(Suppliers.memoize(() -> new Composite(REGISTRY.get(id), null, null)), availability, magic, flight, earth, nocturnal, canHang)); - if (availability.isGrantable()) { - Registry.register(COMMAND_REGISTRY, id, race); - } - return race; - } - - public static RegistryKeyArgumentType argument() { - return RegistryKeyArgumentType.registryKey(COMMAND_REGISTRY.getKey()); - } + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + Abilities.REGISTRY.getCodec().listOf().fieldOf("abilities").forGetter(Race::abilities), + Affinity.CODEC.fieldOf("affinity").forGetter(Race::affinity), + Availability.CODEC.fieldOf("availability").forGetter(Race::availability), + FlightType.CODEC.fieldOf("flight").forGetter(Race::flightType), + Codec.BOOL.fieldOf("magic").forGetter(Race::canCast), + Codec.BOOL.fieldOf("can_forage").forGetter(Race::hasIronGut), + Codec.BOOL.fieldOf("earth_pony_strength").forGetter(Race::canUseEarth), + Codec.BOOL.fieldOf("nocturnal").forGetter(Race::isNocturnal), + Codec.BOOL.fieldOf("hanging").forGetter(Race::canHang), + Codec.BOOL.fieldOf("aquatic").forGetter(Race::isFish), + Codec.BOOL.fieldOf("weather_magic").forGetter(Race::canInfluenceWeather), + Codec.BOOL.fieldOf("cloud_magic").forGetter(Race::canInteractWithClouds) + ).apply(i, Race::new)); /** * The default, unset race. * This is used if there are no other races. */ - public static final Race UNSET = register("unset", Availability.COMMANDS, false, FlightType.NONE, false, false, false); - public static final Race HUMAN = register("human", Availability.COMMANDS, false, FlightType.NONE, false, false, false); - public static final Race EARTH = register("earth", Availability.DEFAULT, false, FlightType.NONE, true, false, false); - public static final Race UNICORN = register("unicorn", Availability.DEFAULT, true, FlightType.NONE, false, false, false); - public static final Race PEGASUS = register("pegasus", Availability.DEFAULT, false, FlightType.AVIAN, false, false, false); - public static final Race BAT = register("bat", Availability.DEFAULT, false, FlightType.AVIAN, false, true, true); - public static final Race ALICORN = register("alicorn", Availability.COMMANDS, true, FlightType.AVIAN, true, false, false); - public static final Race CHANGELING = register("changeling", Availability.DEFAULT, false, FlightType.INSECTOID, false, false, true); - public static final Race KIRIN = register("kirin", Availability.DEFAULT, true, FlightType.NONE, false, false, false); - public static final Race HIPPOGRIFF = register("hippogriff", Availability.DEFAULT, false, FlightType.AVIAN, false, false, false); - public static final Race SEAPONY = register("seapony", Availability.NONE, false, FlightType.NONE, false, false, false); + public static final Race UNSET = register("unset", new Builder().availability(Availability.COMMANDS)); + public static final Race HUMAN = register("human", new Builder().availability(Availability.COMMANDS)); + public static final Race EARTH = register("earth", new Builder().foraging().earth() + .abilities(Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW) + ); + public static final Race UNICORN = register("unicorn", new Builder().foraging().magic() + .abilities(Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL) + ); + public static final Race PEGASUS = register("pegasus", new Builder().foraging().flight(FlightType.AVIAN).weatherMagic().cloudMagic() + .abilities(Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY) + ); + public static final Race BAT = register("bat", new Builder().foraging().flight(FlightType.AVIAN).canHang().cloudMagic() + .abilities(Abilities.TOGGLE_FLIGHT, Abilities.CARRY, Abilities.HANG, Abilities.EEEE) + ); + public static final Race ALICORN = register("alicorn", new Builder().foraging().availability(Availability.COMMANDS).flight(FlightType.AVIAN).earth().magic().weatherMagic().cloudMagic() + .abilities( + Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL, + Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY, + Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW, + Abilities.TIME + ) + ); + public static final Race CHANGELING = register("changeling", new Builder().foraging().affinity(Affinity.BAD).flight(FlightType.INSECTOID).canHang() + .abilities(Abilities.DISPELL, Abilities.TOGGLE_FLIGHT, Abilities.FEED, Abilities.DISGUISE, Abilities.CARRY) + ); + public static final Race KIRIN = register("kirin", new Builder().foraging().magic() + .abilities(Abilities.DISPELL, Abilities.RAGE, Abilities.NIRIK_BLAST, Abilities.KIRIN_CAST) + ); + public static final Race HIPPOGRIFF = register("hippogriff", new Builder().foraging().flight(FlightType.AVIAN).cloudMagic() + .abilities(Abilities.TOGGLE_FLIGHT, Abilities.SCREECH, Abilities.PECK, Abilities.DASH, Abilities.CARRY) + ); + public static final Race SEAPONY = register("seapony", new Builder().foraging().fish() + .abilities(Abilities.SONAR_PULSE) + ); public static void bootstrap() {} public Composite composite() { - return compositeSupplier.get(); + return COMPOSITES.apply(this); } public Composite composite(@Nullable Race pseudo, @Nullable Race potential) { @@ -76,11 +114,7 @@ public record Race (Supplier compositeSupplier, Availability availabi @Override public Affinity getAffinity() { - return this == CHANGELING ? Affinity.BAD : Affinity.NEUTRAL; - } - - public boolean hasIronGut() { - return !isHuman(); + return affinity; } public boolean isUnset() { @@ -91,12 +125,8 @@ public record Race (Supplier compositeSupplier, Availability availabi return !isHuman(); } - public boolean isFish() { - return this == SEAPONY; - } - public boolean isHuman() { - return this == UNSET || this == HUMAN; + return isUnset() || this == HUMAN; } public boolean isDayurnal() { @@ -107,18 +137,14 @@ public record Race (Supplier compositeSupplier, Availability availabi return !flightType().isGrounded(); } - public boolean canInteractWithClouds() { - return canFly() && this != CHANGELING; - } - - public boolean canInfluenceWeather() { - return canFly() && this != CHANGELING && this != BAT && this != HIPPOGRIFF; - } - public boolean hasPersistentWeatherMagic() { return canInfluenceWeather(); } + public boolean canUse(Ability ability) { + return abilities.contains(ability); + } + public Identifier getId() { return REGISTRY.getId(this); } @@ -132,13 +158,11 @@ public record Race (Supplier compositeSupplier, Availability availabi } public String getTranslationKey() { - Identifier id = getId(); - return String.format("%s.race.%s", id.getNamespace(), id.getPath().toLowerCase()); + return Util.createTranslationKey("race", getId()); } public Identifier getIcon() { - Identifier id = getId(); - return new Identifier(id.getNamespace(), "textures/gui/race/" + id.getPath() + ".png"); + return getId().withPath(p -> "textures/gui/race/" + p + ".png"); } public boolean isPermitted(@Nullable PlayerEntity sender) { @@ -161,16 +185,6 @@ public record Race (Supplier compositeSupplier, Availability availabi return isEquine() ? this : other; } - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object o) { - return o instanceof Race race && Objects.equal(race.getId(), getId()); - } - @Override public String toString() { return "Race{ " + getId().toString() + " }"; @@ -195,8 +209,20 @@ public record Race (Supplier compositeSupplier, Availability availabi return def; } - public static Race fromName(String name) { - return fromName(name, EARTH); + public static Race register(String name, Builder builder) { + return register(Unicopia.id(name), builder); + } + + public static Race register(Identifier id, Builder builder) { + Race race = Registry.register(REGISTRY, id, builder.build()); + if (race.availability().isGrantable()) { + Registry.register(COMMAND_REGISTRY, id, race); + } + return race; + } + + public static RegistryKeyArgumentType argument() { + return RegistryKeyArgumentType.registryKey(COMMAND_REGISTRY.getKey()); } public static Race fromArgument(CommandContext context, String name) throws CommandSyntaxException { @@ -233,6 +259,10 @@ public record Race (Supplier compositeSupplier, Availability availabi return any(Race::canCast); } + public boolean canUse(Ability ability) { + return any(r -> r.canUse(ability)); + } + public boolean canInteractWithClouds() { return any(Race::canInteractWithClouds); } @@ -252,6 +282,85 @@ public record Race (Supplier compositeSupplier, Availability availabi return physical().flightType().or(pseudo().flightType()); } } + + public static final class Builder { + private final List> abilities = new ArrayList<>(); + private Affinity affinity = Affinity.NEUTRAL; + private Availability availability = Availability.DEFAULT; + private boolean canCast; + private boolean hasIronGut; + private FlightType flightType = FlightType.NONE; + private boolean canUseEarth; + private boolean isNocturnal; + private boolean canHang; + private boolean isFish; + private boolean canInfluenceWeather; + private boolean canInteractWithClouds; + + public Builder abilities(Ability...abilities) { + this.abilities.addAll(List.of(abilities)); + return this; + } + + public Builder foraging() { + hasIronGut = true; + return this; + } + + public Builder affinity(Affinity affinity) { + this.affinity = affinity; + return this; + } + + public Builder availability(Availability availability) { + this.availability = availability; + return this; + } + + public Builder flight(FlightType flight) { + flightType = flight; + return this; + } + + public Builder magic() { + canCast = true; + return this; + } + + public Builder earth() { + canUseEarth = true; + return this; + } + + public Builder nocturnal() { + isNocturnal = true; + return this; + } + + public Builder canHang() { + canHang = true; + return this; + } + + public Builder fish() { + isFish = true; + return this; + } + + public Builder weatherMagic() { + canInfluenceWeather = true; + return this; + } + + public Builder cloudMagic() { + canInteractWithClouds = true; + return this; + } + + public Race build() { + return new Race(List.copyOf(abilities), affinity, availability, flightType, canCast, hasIronGut, canUseEarth, isNocturnal, canHang, isFish, canInfluenceWeather, canInteractWithClouds); + } + } } diff --git a/src/main/java/com/minelittlepony/unicopia/ability/Ability.java b/src/main/java/com/minelittlepony/unicopia/ability/Ability.java index 979d7f97..eaa96379 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/Ability.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/Ability.java @@ -68,7 +68,9 @@ public interface Ability { * Checks if the given race is permitted to use this ability * @param playerSpecies The player's species */ - boolean canUse(Race playerSpecies); + default boolean canUse(Race race) { + return race.canUse(this); + } /** * Called when an ability is about to be triggered. This event occurs on both the client and server so check {@code Pony#isClient} if you need to know which one you're on. diff --git a/src/main/java/com/minelittlepony/unicopia/ability/AbstractSpellCastingAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/AbstractSpellCastingAbility.java index 2ff33f62..abe2489e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/AbstractSpellCastingAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/AbstractSpellCastingAbility.java @@ -1,6 +1,5 @@ package com.minelittlepony.unicopia.ability; -import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType; import com.minelittlepony.unicopia.entity.player.Pony; @@ -11,17 +10,11 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.TypedActionResult; abstract class AbstractSpellCastingAbility implements Ability { - @Override public int getCooldownTime(Pony player) { return 0; } - @Override - public boolean canUse(Race race) { - return race.canCast() && race != Race.KIRIN; - } - @Override public Text getName(Pony player) { CustomisedSpellType spell = player.getCharms().getEquippedSpell(player.getCharms().getHand()); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java index 867f67ae..826f29a1 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/BatEeeeAbility.java @@ -1,7 +1,6 @@ package com.minelittlepony.unicopia.ability; import com.minelittlepony.unicopia.AwaitTickQueue; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; @@ -16,11 +15,6 @@ import net.minecraft.world.event.GameEvent; public class BatEeeeAbility extends ScreechAbility { public static final int SELF_SPOOK_PROBABILITY = 20000; - @Override - public boolean canUse(Race race) { - return race == Race.BAT; - } - @Override protected void playSounds(Pony player, Random rng, float strength) { int count = 1 + rng.nextInt(10) + (int)(strength * 10); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/BatPonyHangAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/BatPonyHangAbility.java index 9237ab34..723cc0f5 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/BatPonyHangAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/BatPonyHangAbility.java @@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.ability; import java.util.Optional; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Multi; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.util.TraceHelper; @@ -30,11 +29,6 @@ public class BatPonyHangAbility implements Ability { return 0; } - @Override - public boolean canUse(Race race) { - return race == Race.BAT; - } - @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/CarryAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/CarryAbility.java index e93d1c2a..844fd96f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/CarryAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/CarryAbility.java @@ -6,7 +6,6 @@ import java.util.UUID; import java.util.stream.StreamSupport; import com.minelittlepony.unicopia.EquinePredicates; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.player.Pony; @@ -37,11 +36,6 @@ public class CarryAbility implements Ability { return 0; } - @Override - public boolean canUse(Race race) { - return race.canFly(); - } - @Override public Optional prepare(Pony player) { return Hit.INSTANCE; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ChangelingFeedAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ChangelingFeedAbility.java index 7a4e1f76..974f212c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/ChangelingFeedAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/ChangelingFeedAbility.java @@ -7,7 +7,6 @@ import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.magic.spell.ChangelingFeedingSpell; @@ -48,11 +47,6 @@ public class ChangelingFeedAbility implements Ability { return !SpellType.FEED.isOn(player) && ChangelingFeedingSpell.canFeed(player) ? 15 : 80; } - @Override - public boolean canUse(Race race) { - return race == Race.CHANGELING; - } - @Nullable @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java index 0454681e..744ee4a5 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java @@ -3,7 +3,6 @@ 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.USounds; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Hit; @@ -48,11 +47,6 @@ public class EarthPonyGrowAbility implements Ability { return 50; } - @Override - public boolean canUse(Race race) { - return race.canUseEarth(); - } - @Override public Optional prepare(Pony player) { return TraceHelper.findBlock(player.asEntity(), 3, 1).map(Pos::new); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java index 0affd0cf..62dfda4a 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyKickAbility.java @@ -53,11 +53,6 @@ public class EarthPonyKickAbility implements Ability { return 50; } - @Override - public boolean canUse(Race race) { - return race.canUseEarth(); - } - @Override public Identifier getIcon(Pony player) { return getId().withPath(p -> "textures/gui/ability/" + p diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java index 285266c9..78314ba7 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java @@ -62,11 +62,6 @@ public class EarthPonyStompAbility implements Ability { return 50; } - @Override - public boolean canUse(Race race) { - return race.canUseEarth(); - } - @Override public Identifier getIcon(Pony player) { Identifier id = Abilities.REGISTRY.getId(this); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/FlyingDashAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/FlyingDashAbility.java index 0a9f5f6b..c9436ce9 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/FlyingDashAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/FlyingDashAbility.java @@ -4,7 +4,6 @@ import java.util.Optional; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.entity.player.Pony; @@ -23,11 +22,6 @@ public class FlyingDashAbility implements Ability { return 30; } - @Override - public boolean canUse(Race race) { - return race == Race.HIPPOGRIFF; - } - @Nullable @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/HugAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/HugAbility.java index 6457ccb9..caca424f 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/HugAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/HugAbility.java @@ -1,6 +1,5 @@ package com.minelittlepony.unicopia.ability; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.entity.Living; @@ -16,12 +15,6 @@ import net.minecraft.particle.ParticleTypes; * Ability to hug mobs. Not all of them are receptive to your advances though, so be careful! */ public class HugAbility extends CarryAbility { - - @Override - public boolean canUse(Race race) { - return race.canUseEarth(); - } - @Override public boolean apply(Pony pony, Hit data) { PlayerEntity player = pony.asEntity(); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/KirinCastingAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/KirinCastingAbility.java index 5b82e4cc..53b2e56c 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/KirinCastingAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/KirinCastingAbility.java @@ -1,16 +1,10 @@ package com.minelittlepony.unicopia.ability; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.particle.ParticleTypes; public class KirinCastingAbility extends UnicornCastingAbility { - @Override - public boolean canUse(Race race) { - return race == Race.KIRIN; - } - @Override public void coolDown(Pony player, AbilitySlot slot) { player.spawnParticles(ParticleTypes.FLAME, 5); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/KirinRageAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/KirinRageAbility.java index 92d0074c..dce16e46 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/KirinRageAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/KirinRageAbility.java @@ -4,7 +4,6 @@ import java.util.Optional; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; @@ -30,11 +29,6 @@ public class KirinRageAbility implements Ability { return 60; } - @Override - public boolean canUse(Race race) { - return race == Race.KIRIN; - } - @Nullable @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/NirikBlastAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/NirikBlastAbility.java index 3c516f32..65c6f353 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/NirikBlastAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/NirikBlastAbility.java @@ -5,7 +5,6 @@ import java.util.Optional; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquinePredicates; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation.Recipient; @@ -36,11 +35,6 @@ public class NirikBlastAbility implements Ability { return 3; } - @Override - public boolean canUse(Race race) { - return race == Race.KIRIN; - } - @Nullable @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/PeckAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/PeckAbility.java index 849dbb4c..db5b09fc 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/PeckAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/PeckAbility.java @@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability; import java.util.Optional; import com.minelittlepony.unicopia.EquinePredicates; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Numeric; @@ -54,11 +53,6 @@ public class PeckAbility implements Ability { return true; } - @Override - public boolean canUse(Race race) { - return race == Race.HIPPOGRIFF; - } - @Override public Optional prepare(Pony player) { return Hit.INSTANCE; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/PegasusCaptureStormAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/PegasusCaptureStormAbility.java index b02579df..6b59dfe4 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/PegasusCaptureStormAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/PegasusCaptureStormAbility.java @@ -4,7 +4,6 @@ import java.util.Optional; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.item.UItems; @@ -33,11 +32,6 @@ public class PegasusCaptureStormAbility implements Ability { return 6; } - @Override - public boolean canUse(Race race) { - return race.canInfluenceWeather(); - } - @Nullable @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java index 606cd65f..c9abd9b6 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/PegasusRainboomAbility.java @@ -4,7 +4,6 @@ import java.util.Optional; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; @@ -26,11 +25,6 @@ public class PegasusRainboomAbility implements Ability { return 60; } - @Override - public boolean canUse(Race race) { - return race.canInfluenceWeather(); - } - @Nullable @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java index 60319ba1..55b9c393 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java @@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability; import java.util.Optional; import com.minelittlepony.unicopia.EquinePredicates; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.ability.data.Numeric; @@ -49,11 +48,6 @@ public class ScreechAbility implements Ability { return true; } - @Override - public boolean canUse(Race race) { - return race == Race.HIPPOGRIFF; - } - @Override public Optional prepare(Pony player) { return player.getAbilities().getActiveStat() diff --git a/src/main/java/com/minelittlepony/unicopia/ability/SeaponySonarPulseAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/SeaponySonarPulseAbility.java index 95078776..f1be14f7 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/SeaponySonarPulseAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/SeaponySonarPulseAbility.java @@ -5,7 +5,6 @@ import java.util.Optional; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.AwaitTickQueue; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.UPOIs; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.data.Hit; @@ -38,11 +37,6 @@ public class SeaponySonarPulseAbility implements Ability { return 100; } - @Override - public boolean canUse(Race race) { - return race == Race.SEAPONY; - } - @Nullable @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java index a1e92a03..b06df709 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/TimeChangeAbility.java @@ -12,11 +12,6 @@ import com.minelittlepony.unicopia.server.world.UGameRules; public class TimeChangeAbility implements Ability { - @Override - public boolean canUse(Race race) { - return race == Race.ALICORN; - } - @Override public boolean canUse(Race.Composite race) { return canUse(race.physical()) || race.pseudo() == Race.UNICORN; diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java index 4f20d77d..caa3c701 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/ToggleFlightAbility.java @@ -24,11 +24,6 @@ public class ToggleFlightAbility implements Ability { return 0; } - @Override - public boolean canUse(Race race) { - return race.canFly(); - } - @Nullable @Override public Optional prepare(Pony player) { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java index d965f9da..7b0c35d6 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/UnicornDispellAbility.java @@ -33,11 +33,6 @@ public class UnicornDispellAbility implements Ability { return 0; } - @Override - public boolean canUse(Race race) { - return race.canCast() || race == Race.CHANGELING; - } - @Override public int getColor(Pony player) { return SpellType.PORTAL.getColor(); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/UnicornTeleportAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/UnicornTeleportAbility.java index 7f75d688..c5e3bbd4 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/UnicornTeleportAbility.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/UnicornTeleportAbility.java @@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.ability; import java.util.Optional; -import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Pos; @@ -60,11 +59,6 @@ public class UnicornTeleportAbility implements Ability { return (int)(50 - Math.min(45F, player.getLevel().get() * 0.75F)); } - @Override - public boolean canUse(Race race) { - return race.canCast() && race != Race.KIRIN; - } - @Override public double getCostEstimate(Pony player) { return prepare(player).map(pos -> pos.distanceTo(player) / 10).orElse(0D); From a8f0796ad70e8b3480ca09557104b6b2f20024b8 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 8 Apr 2024 22:13:08 +0100 Subject: [PATCH 14/32] Added sticky keys option. Fixes #322 --- .../com/minelittlepony/unicopia/Config.java | 3 + .../com/minelittlepony/unicopia/Race.java | 4 +- .../unicopia/client/KeyBindingsHandler.java | 34 +++++++++- .../unicopia/client/gui/SettingsScreen.java | 4 ++ .../unicopia/client/gui/Slot.java | 14 +++- .../unicopia/client/gui/UHud.java | 64 +++++++++---------- .../unicopia/entity/mob/ButterflyEntity.java | 3 +- .../unicopia/entity/mob/UEntities.java | 5 +- .../resources/assets/unicopia/lang/en_us.json | 48 +++++++------- 9 files changed, 117 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Config.java b/src/main/java/com/minelittlepony/unicopia/Config.java index 8ccae93c..af798840 100644 --- a/src/main/java/com/minelittlepony/unicopia/Config.java +++ b/src/main/java/com/minelittlepony/unicopia/Config.java @@ -34,6 +34,9 @@ public class Config extends com.minelittlepony.common.util.settings.Config { .addComment("If true Mine Little Pony will not be considered when determining the race to use") .addComment("The result will always be what is set by this config file."); + public final Setting toggleAbilityKeys = value("client", "toggleAbilityKeys", false) + .addComment("If true the ability keybinds will function as toggle keys rather than hold keys"); + public final Setting hudPage = value("client", "hudActivePage", 0) .addComment("The page of abilities currently visible in the HUD. You can change this in-game using the PG_UP and PG_DWN keys (configurable)"); diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index 9e06f047..c3ab492d 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -73,7 +73,7 @@ public record Race ( .abilities(Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW) ); public static final Race UNICORN = register("unicorn", new Builder().foraging().magic() - .abilities(Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL) + .abilities(Abilities.TELEPORT, Abilities.CAST, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL) ); public static final Race PEGASUS = register("pegasus", new Builder().foraging().flight(FlightType.AVIAN).weatherMagic().cloudMagic() .abilities(Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY) @@ -83,7 +83,7 @@ public record Race ( ); public static final Race ALICORN = register("alicorn", new Builder().foraging().availability(Availability.COMMANDS).flight(FlightType.AVIAN).earth().magic().weatherMagic().cloudMagic() .abilities( - Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL, + Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.CAST, Abilities.SHOOT, Abilities.DISPELL, Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY, Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW, Abilities.TIME diff --git a/src/main/java/com/minelittlepony/unicopia/client/KeyBindingsHandler.java b/src/main/java/com/minelittlepony/unicopia/client/KeyBindingsHandler.java index d8c52062..23494849 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/KeyBindingsHandler.java +++ b/src/main/java/com/minelittlepony/unicopia/client/KeyBindingsHandler.java @@ -18,7 +18,9 @@ import com.minelittlepony.unicopia.entity.player.Pony; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.MinecraftClient; import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.option.StickyKeyBinding; import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.client.util.InputUtil; import net.minecraft.text.Text; import net.minecraft.util.math.MathHelper; @@ -35,6 +37,10 @@ public class KeyBindingsHandler { private final Binding pageDown = register(GLFW.GLFW_KEY_PAGE_DOWN, "hud_page_dn"); private final Binding pageUp = register(GLFW.GLFW_KEY_PAGE_UP, "hud_page_up"); + private final Binding singleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_tap"); + private final Binding doubleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_double_tap"); + private final Binding tripleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_triple_tap"); + private final Set pressed = new HashSet<>(); public KeyBindingsHandler() { @@ -47,8 +53,12 @@ public class KeyBindingsHandler { return reverse.get(slot); } + public boolean isToggleMode() { + return Unicopia.getConfig().toggleAbilityKeys.get(); + } + public void addKeybind(int code, AbilitySlot slot) { - Binding binding = register(code, slot.name().toLowerCase()); + Binding binding = new Binding(KeyBindingHelper.registerKeyBinding(new StickyKeyBinding("key.unicopia." + slot.name().toLowerCase(), code, KEY_CATEGORY, this::isToggleMode))); reverse.put(slot, binding); keys.put(binding, slot); } @@ -104,6 +114,7 @@ public class KeyBindingsHandler { int page = Unicopia.getConfig().hudPage.get(); page += sigma; Unicopia.getConfig().hudPage.set(page); + Unicopia.getConfig().save(); client.getSoundManager().play(PositionedSoundInstance.master(USounds.Vanilla.UI_BUTTON_CLICK, 1.75F + (0.25F * sigma))); UHud.INSTANCE.setMessage(Text.translatable("gui.unicopia.page_num", page + 1, max + 1)); } @@ -141,6 +152,27 @@ public class KeyBindingsHandler { } public ActivationType getType() { + if (binding.isPressed() && binding instanceof StickyKeyBinding) { + if (singleTapModifier.binding.isPressed()) { + KeyBinding.untoggleStickyKeys(); + return ActivationType.TAP; + } + + if (doubleTapModifier.binding.isPressed()) { + KeyBinding.untoggleStickyKeys(); + return ActivationType.DOUBLE_TAP; + } + + if (tripleTapModifier.binding.isPressed()) { + KeyBinding.untoggleStickyKeys(); + return ActivationType.TRIPLE_TAP; + } + + if (isToggleMode()) { + return ActivationType.NONE; + } + } + long now = System.currentTimeMillis(); if (type != ActivationType.NONE && now > nextPhaseTime - 70) { ActivationType t = type; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/SettingsScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/SettingsScreen.java index 749d2b13..4d0d5452 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/SettingsScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/SettingsScreen.java @@ -68,6 +68,10 @@ public class SettingsScreen extends GameGui { }) .getStyle().setText("unicopia.options.ignore_mine_lp"); + content.addButton(new Toggle(LEFT, row += 20, config.toggleAbilityKeys.get())) + .onChange(config.toggleAbilityKeys) + .getStyle().setText("unicopia.options.toggle_ability_keys"); + mineLpStatus = content.addButton(new Label(LEFT, row += 10)).getStyle().setText(getMineLPStatus()); RegistryIndexer races = RegistryIndexer.of(Race.REGISTRY); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/Slot.java b/src/main/java/com/minelittlepony/unicopia/client/gui/Slot.java index 4117c7e1..434f5dda 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/Slot.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/Slot.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.client.gui; +import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.ability.AbilityDispatcher; import com.minelittlepony.unicopia.ability.AbilitySlot; import com.minelittlepony.unicopia.client.KeyBindingsHandler; @@ -82,6 +83,12 @@ class Slot { bSwap &= abilities.isFilled(bSlot); } + AbilityDispatcher.Stat stat = abilities.getStat(bSwap ? bSlot : aSlot); + + if (stat.getAbility(Unicopia.getConfig().hudPage.get()).isEmpty()) { + return; + } + RenderSystem.setShaderColor(1, 1, 1, 1); RenderSystem.enableBlend(); MatrixStack matrices = context.getMatrices(); @@ -91,7 +98,7 @@ class Slot { // background context.drawTexture(UHud.HUD_TEXTURE, 0, 0, backgroundU, backgroundV, size, size, 128, 128); - AbilityDispatcher.Stat stat = abilities.getStat(bSwap ? bSlot : aSlot); + int iconPosition = ((size - iconSize + slotPadding + 1) / 2); int sz = iconSize - slotPadding; @@ -122,6 +129,11 @@ class Slot { } void renderLabel(DrawContext context, AbilityDispatcher abilities, float tickDelta) { + + if (abilities.getStat(aSlot).getAbility(Unicopia.getConfig().hudPage.get()).isEmpty()) { + return; + } + Text label = KeyBindingsHandler.INSTANCE.getBinding(aSlot).getLabel(); MatrixStack matrices = context.getMatrices(); 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 11a5bf02..59b89037 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/UHud.java @@ -48,8 +48,8 @@ public class UHud { private final List slots = List.of( new ManaRingSlot(this, AbilitySlot.PRIMARY, AbilitySlot.PASSIVE, 0, 0), - new Slot(this, AbilitySlot.SECONDARY, AbilitySlot.SECONDARY, 26, -5), - new Slot(this, AbilitySlot.TERTIARY, AbilitySlot.TERTIARY, 36, 19) + new Slot(this, AbilitySlot.SECONDARY, AbilitySlot.SECONDARY, 30, -8), + new Slot(this, AbilitySlot.TERTIARY, AbilitySlot.TERTIARY, 40, 18) ); @Nullable @@ -68,10 +68,6 @@ public class UHud { private SpellType focusedType = SpellType.empty(); public void render(InGameHud hud, DrawContext context, float tickDelta) { - - // TODO: Check this when backporting! - // InGameHud#renderHotbar line 460 - // context.getMatrices().translate(0.0f, 0.0f, -90.0f); final int hotbarZ = -90; if (client.player == null) { @@ -96,7 +92,6 @@ public class UHud { font = client.textRenderer; xDirection = client.player.getMainArm() == Arm.LEFT ? -1 : 1; - matrices.push(); matrices.translate(scaledWidth / 2, scaledHeight / 2, 0); @@ -109,7 +104,7 @@ public class UHud { matrices.pop(); matrices.push(); - int hudX = ((scaledWidth - 50) / 2) + (104 * xDirection); + int hudX = ((scaledWidth - 50) / 2) + (109 * xDirection); int hudY = scaledHeight - 50; int hudZ = hotbarZ; @@ -139,33 +134,33 @@ public class UHud { slots.forEach(slot -> slot.renderBackground(context, abilities, swap, tickDelta)); - boolean canCast = Abilities.CAST.canUse(pony.getCompositeRace()) || Abilities.KIRIN_CAST.canUse(pony.getCompositeRace()); + + Ability ability = pony.getAbilities().getStat(AbilitySlot.PRIMARY) + .getAbility(Unicopia.getConfig().hudPage.get()) + .orElse(null); + boolean canCast = ability == Abilities.CAST || ability == Abilities.KIRIN_CAST || ability == Abilities.SHOOT; if (canCast) { - Ability ability = pony.getAbilities().getStat(AbilitySlot.PRIMARY) - .getAbility(Unicopia.getConfig().hudPage.get()) - .orElse(null); - - if (ability == Abilities.CAST || ability == Abilities.SHOOT) { - matrices.push(); - matrices.translate(PRIMARY_SLOT_SIZE / 2F, PRIMARY_SLOT_SIZE / 2F, 0); - boolean first = !pony.asEntity().isSneaking(); - TypedActionResult> inHand = pony.getCharms().getSpellInHand(false); - boolean replacing = inHand.getResult().isAccepted() && pony.getAbilities().getStat(AbilitySlot.PRIMARY).getActiveAbility().isEmpty(); - if (first != prevPointed || replacing != prevReplacing || inHand.getValue().type() != focusedType) { - focusedType = inHand.getValue().type(); - prevPointed = first; - prevReplacing = replacing; - setMessage(ability.getName(pony)); - } - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(first ? 37 : 63)); - matrices.translate(-23, 0, 0); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-26)); - matrices.scale(0.8F, 0.8F, 1); - int u = replacing ? 16 : 3; - context.drawTexture(HUD_TEXTURE, 0, 0, u, 120, 13, 7, 128, 128); - matrices.pop(); + matrices.push(); + matrices.translate(PRIMARY_SLOT_SIZE / 2F, PRIMARY_SLOT_SIZE / 2F, 0); + boolean first = !pony.asEntity().isSneaking(); + TypedActionResult> inHand = pony.getCharms().getSpellInHand(false); + boolean replacing = inHand.getResult().isAccepted() && pony.getAbilities().getStat(AbilitySlot.PRIMARY).getActiveAbility().isEmpty(); + if (first != prevPointed || replacing != prevReplacing || inHand.getValue().type() != focusedType) { + focusedType = inHand.getValue().type(); + prevPointed = first; + prevReplacing = replacing; + setMessage(ability.getName(pony)); } + int baseAngle = xDirection < 0 ? 100 : 0; + int secondAngleDif = xDirection * 30; + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(baseAngle + 37 + (first ? 0 : secondAngleDif))); + matrices.translate(-23, 0, 0); + matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-26)); + matrices.scale(0.8F, 0.8F, 1); + int u = replacing ? 16 : 3; + context.drawTexture(HUD_TEXTURE, 0, 0, u, 120, 13, 7, 128, 128); + matrices.pop(); } slots.forEach(slot -> slot.renderLabel(context, abilities, tickDelta)); @@ -173,8 +168,13 @@ public class UHud { matrices.pop(); if (canCast) { + matrices.push(); + if (xDirection < 0) { + hudX += PRIMARY_SLOT_SIZE / 2F - 8; + } SpellIconRenderer.renderSpell(context, pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 10 - xDirection * 13, hudY + 2, EQUIPPED_GEMSTONE_SCALE); SpellIconRenderer.renderSpell(context, pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 8 - xDirection * 2, hudY - 6, EQUIPPED_GEMSTONE_SCALE); + matrices.pop(); } RenderSystem.disableBlend(); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/ButterflyEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/ButterflyEntity.java index 6a0731b4..ceaf6dad 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/ButterflyEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/ButterflyEntity.java @@ -11,7 +11,6 @@ import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; -import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.item.ButterflyItem; import com.minelittlepony.unicopia.util.NbtSerialisable; @@ -74,7 +73,7 @@ public class ButterflyEntity extends AmbientEntity { } public static boolean canSpawn(EntityType type, WorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random) { - return world.getBlockState(pos.down()).isIn(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON); + return true;//world.getBlockState(pos.down()).isIn(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON); } @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 8d6ea782..76b0de8a 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UEntities.java @@ -26,7 +26,8 @@ import net.minecraft.world.Heightmap.Type; public interface UEntities { EntityType BUTTERFLY = register("butterfly", FabricEntityTypeBuilder.createMob().spawnGroup(SpawnGroup.AMBIENT).entityFactory(ButterflyEntity::new) - .spawnRestriction(Location.NO_RESTRICTIONS, Type.WORLD_SURFACE_WG, ButterflyEntity::canSpawn) + .spawnRestriction(Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, ButterflyEntity::canSpawn) + .spawnableFarFromPlayer() .dimensions(EntityDimensions.fixed(0.25F, 0.25F))); EntityType THROWN_ITEM = register("thrown_item", FabricEntityTypeBuilder.create(SpawnGroup.MISC, MagicProjectileEntity::new) .trackRangeBlocks(100) @@ -83,7 +84,7 @@ public interface UEntities { .trackRangeChunks(8) .dimensions(EntityDimensions.fixed(3, 2))); EntityType SPECTER = register("specter", FabricEntityTypeBuilder.createMob().spawnGroup(SpawnGroup.MONSTER).entityFactory(SpecterEntity::new) - .spawnRestriction(Location.ON_GROUND, Type.WORLD_SURFACE, HostileEntity::canSpawnInDark) + .spawnRestriction(Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnIgnoreLightLevel) .fireImmune() .spawnableFarFromPlayer() .dimensions(EntityDimensions.fixed(1, 2))); diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 736626b1..523ff40e 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -1329,6 +1329,9 @@ "key.unicopia.secondary": "Secondary Ability", "key.unicopia.tertiary": "Tertiary Ability", "key.unicopia.passive": "Passive Ability", + "key.unicopia.ability_modifier_tap": "VR Ability Modifier (1-TAP)", + "key.unicopia.ability_modifier_double_tap": "VR Ability Modifier (2-TAP)", + "key.unicopia.ability_modifier_triple_tap": "VR Ability Modifier (3-TAP)", "key.unicopia.hud_page_dn": "Hud Previous Page", "key.unicopia.hud_page_up": "Hud Next Page", @@ -1408,6 +1411,7 @@ "commands.gravity.set.multiple": "Updated %s entities", "unicopia.options.title": "Unicopia Options", + "unicopia.options.toggle_ability_keys": "Sticky Ability Keys", "unicopia.options.ignore_mine_lp": "Ignore Mine Little Pony", "unicopia.options.ignore_mine_lp.missing": "* Mine Little Pony is not installed", "unicopia.options.ignore_mine_lp.detected": "* Your detected race is %s", @@ -1427,28 +1431,28 @@ "command.unicopia.config.list": "[Config] Property (%s) contains (%s) entries: ", "command.unicopia.config.clear": "[Config] Cleared all values from property %s", - "unicopia.race.unset": "Unset", - "unicopia.race.unset.alt": "Unset", - "unicopia.race.human": "Human", - "unicopia.race.human.alt": "Humans", - "unicopia.race.earth": "Earth Pony", - "unicopia.race.earth.alt": "Earth Ponies", - "unicopia.race.unicorn": "Unicorn", - "unicopia.race.unicorn.alt": "Unicorns", - "unicopia.race.pegasus": "Pegasus", - "unicopia.race.pegasus.alt": "Pegasi", - "unicopia.race.alicorn": "Alicorn", - "unicopia.race.alicorn.alt": "Alicorns", - "unicopia.race.changeling": "Changeling", - "unicopia.race.changeling.alt": "Changelings", - "unicopia.race.bat": "Bat Pony", - "unicopia.race.bat.alt": "Bat Ponies", - "unicopia.race.kirin": "Kirin", - "unicopia.race.kirin.alt": "Kirins", - "unicopia.race.hippogriff": "Hippogriff", - "unicopia.race.hippogriff.alt": "Hippogriffs", - "unicopia.race.seapony": "Sea Pony", - "unicopia.race.seapony.alt": "Sea Ponies", + "race.unicopia.unset": "Unset", + "race.unicopia.unset.alt": "Unset", + "race.unicopia.human": "Human", + "race.unicopia.human.alt": "Humans", + "race.unicopia.earth": "Earth Pony", + "race.unicopia.earth.alt": "Earth Ponies", + "race.unicopia.unicorn": "Unicorn", + "race.unicopia.unicorn.alt": "Unicorns", + "race.unicopia.pegasus": "Pegasus", + "race.unicopia.pegasus.alt": "Pegasi", + "race.unicopia.alicorn": "Alicorn", + "race.unicopia.alicorn.alt": "Alicorns", + "race.unicopia.changeling": "Changeling", + "race.unicopia.changeling.alt": "Changelings", + "race.unicopia.bat": "Bat Pony", + "race.unicopia.bat.alt": "Bat Ponies", + "race.unicopia.kirin": "Kirin", + "race.unicopia.kirin.alt": "Kirins", + "race.unicopia.hippogriff": "Hippogriff", + "race.unicopia.hippogriff.alt": "Hippogriffs", + "race.unicopia.seapony": "Sea Pony", + "race.unicopia.seapony.alt": "Sea Ponies", "death.attack.unicopia.generic.and_also": "%1$s and %2$s", "death.attack.unicopia.generic.whilst_flying": "%1$s whilst flying", From 04f8827e283ed0e5218ce6e68caccf2538ae2dca Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 00:21:39 +0100 Subject: [PATCH 15/32] Disable the dust cloud --- .../unicopia/entity/player/PlayerPhysics.java | 7 +++---- 1 file changed, 3 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 4465c2aa..338ae0e4 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -40,7 +40,6 @@ 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; @@ -581,10 +580,10 @@ public class PlayerPhysics extends EntityPhysics implements Tickab entity.calculateDimensions(); if (entity.isOnGround() || !force) { - BlockState steppingState = pony.asEntity().getSteppingBlockState(); - if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) { + //BlockState steppingState = pony.asEntity().getSteppingBlockState(); + /*if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) { pony.addParticle(new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), pony.getOrigin().toCenterPos(), Vec3d.ZERO); - } else { + } 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 bd8c2f39fec163450d8100051795c9ee2b53cb98 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 00:50:37 +0100 Subject: [PATCH 16/32] Update vortex gem texture --- .../textures/item/gemstone_vortex.png | Bin 3007 -> 6505 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/unicopia/textures/item/gemstone_vortex.png b/src/main/resources/assets/unicopia/textures/item/gemstone_vortex.png index 6554e9a9cbe0acf0d8a757f5f442b9a6075b105e..c367d2171c3163059315bf2df367113cdc6d3ec6 100644 GIT binary patch literal 6505 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 delta 2939 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 From 51d67d52158641843989fb1d5684e63d5608abe1 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 00:50:58 +0100 Subject: [PATCH 17/32] Clean things up a little --- .../ability/magic/spell/effect/SpellType.java | 112 +++++++----------- 1 file changed, 41 insertions(+), 71 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java index 2179eab1..e963c0e7 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/SpellType.java @@ -1,10 +1,5 @@ package com.minelittlepony.unicopia.ability.magic.spell.effect; -import java.util.EnumMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.Affinity; @@ -39,48 +34,47 @@ import net.minecraft.server.command.ServerCommandSource; public final class SpellType implements Affine, SpellPredicate { public static final Identifier EMPTY_ID = Unicopia.id("none"); - public static final SpellType EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, SpellTraits.EMPTY, t -> null); + public static final SpellType EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, GemstoneItem.Shape.ROUND, SpellTraits.EMPTY, t -> null); public static final Registry> REGISTRY = RegistryUtils.createSimple(Unicopia.id("spells")); public static final RegistryKey>> REGISTRY_KEY = REGISTRY.getKey(); - public static final Map>> BY_AFFINITY = new EnumMap<>(Affinity.class); private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id)); - public static final SpellType PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, PlaceableSpell::new); - public static final SpellType THROWN_SPELL = register("thrown", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, ThrowableSpell::new); + public static final SpellType PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, GemstoneItem.Shape.DONUT, SpellTraits.EMPTY, PlaceableSpell::new); + public static final SpellType THROWN_SPELL = register("thrown", Affinity.NEUTRAL, 0, false, GemstoneItem.Shape.DONUT, SpellTraits.EMPTY, ThrowableSpell::new); - public static final SpellType CHANGELING_DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, SpellTraits.EMPTY, DispersableDisguiseSpell::new); - public static final SpellType FEED = register("feed", Affinity.BAD, 0xBDBDF9, false, SpellTraits.EMPTY, ChangelingFeedingSpell::new); - public static final SpellType RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, RainboomAbilitySpell::new); - public static final SpellType RAGE = register("rage", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, RageAbilitySpell::new); - public static final SpellType TIME_CONTROL = register("time_control", Affinity.GOOD, 0xBDBDF9, false, SpellTraits.EMPTY, TimeControlAbilitySpell::new); + public static final SpellType CHANGELING_DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, GemstoneItem.Shape.ARROW, SpellTraits.EMPTY, DispersableDisguiseSpell::new); + public static final SpellType FEED = register("feed", Affinity.BAD, 0xBDBDF9, false, GemstoneItem.Shape.ARROW, SpellTraits.EMPTY, ChangelingFeedingSpell::new); + public static final SpellType RAINBOOM = register("rainboom", Affinity.GOOD, 0xBDBDF9, false, GemstoneItem.Shape.ROCKET, SpellTraits.EMPTY, RainboomAbilitySpell::new); + public static final SpellType RAGE = register("rage", Affinity.GOOD, 0xBDBDF9, false, GemstoneItem.Shape.FLAME, SpellTraits.EMPTY, RageAbilitySpell::new); + public static final SpellType TIME_CONTROL = register("time_control", Affinity.GOOD, 0xBDBDF9, false, GemstoneItem.Shape.STAR, SpellTraits.EMPTY, TimeControlAbilitySpell::new); - public static final SpellType FROST = register("frost", Affinity.GOOD, 0xEABBFF, true, IceSpell.DEFAULT_TRAITS, IceSpell::new); - public static final SpellType CHILLING_BREATH = register("chilling_breath", Affinity.NEUTRAL, 0xFFEAFF, true, ChillingBreathSpell.DEFAULT_TRAITS, ChillingBreathSpell::new); - public static final SpellType SCORCH = register("scorch", Affinity.BAD, 0xF8EC1F, true, ScorchSpell.DEFAULT_TRAITS, ScorchSpell::new); - public static final SpellType FLAME = register("flame", Affinity.GOOD, 0xFFBB99, true, FireSpell.DEFAULT_TRAITS, FireSpell::new); - public static final SpellType INFERNAL = register("infernal", Affinity.BAD, 0xFFAA00, true, InfernoSpell.DEFAULT_TRAITS, InfernoSpell::new); - public static final SpellType SHIELD = register("shield", Affinity.NEUTRAL, 0x66CDAA, true, ShieldSpell.DEFAULT_TRAITS, ShieldSpell::new); - public static final SpellType ARCANE_PROTECTION = register("arcane_protection", Affinity.BAD, 0x99CDAA, true, AreaProtectionSpell.DEFAULT_TRAITS, AreaProtectionSpell::new); - public static final SpellType VORTEX = register("vortex", Affinity.NEUTRAL, 0xFFEA88, true, AttractiveSpell.DEFAULT_TRAITS, AttractiveSpell::new); - public static final SpellType DARK_VORTEX = register("dark_vortex", Affinity.BAD, 0xA33333, true, DarkVortexSpell.DEFAULT_TRAITS, DarkVortexSpell::new); - public static final SpellType NECROMANCY = register("necromancy", Affinity.BAD, 0xFA3A3A, true, SpellTraits.EMPTY, NecromancySpell::new); - public static final SpellType SIPHONING = register("siphoning", Affinity.NEUTRAL, 0xFFA3AA, true, SpellTraits.EMPTY, SiphoningSpell::new); - public static final SpellType REVEALING = register("reveal", Affinity.GOOD, 0xFFFFAF, true, SpellTraits.EMPTY, DisperseIllusionSpell::new); - public static final SpellType AWKWARD = register("awkward", Affinity.GOOD, 0x3A59FF, true, SpellTraits.EMPTY, AwkwardSpell::new); - public static final SpellType TRANSFORMATION = register("transformation", Affinity.GOOD, 0x19E48E, true, SpellTraits.EMPTY, TransformationSpell::new); - public static final SpellType FEATHER_FALL = register("feather_fall", Affinity.GOOD, 0x00EEFF, true, FeatherFallSpell.DEFAULT_TRAITS, FeatherFallSpell::new); - public static final SpellType CATAPULT = register("catapult", Affinity.GOOD, 0x22FF00, true, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new); - public static final SpellType FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0xFF8811, true, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new); - public static final SpellType LIGHT = register("light", Affinity.GOOD, 0xEEFFAA, true, LightSpell.DEFAULT_TRAITS, LightSpell::new); - public static final SpellType DISPLACEMENT = register("displacement", Affinity.NEUTRAL, 0x9900FF, true, PortalSpell.DEFAULT_TRAITS, DisplacementSpell::new); - public static final SpellType PORTAL = register("portal", Affinity.GOOD, 0x99FFFF, true, PortalSpell.DEFAULT_TRAITS, PortalSpell::new); - public static final SpellType MIMIC = register("mimic", Affinity.GOOD, 0xFFFF00, true, SpellTraits.EMPTY, MimicSpell::new); - public static final SpellType MIND_SWAP = register("mind_swap", Affinity.BAD, 0xF9FF99, true, SpellTraits.EMPTY, MindSwapSpell::new); - public static final SpellType HYDROPHOBIC = register("hydrophobic", Affinity.NEUTRAL, 0xF999FF, true, SpellTraits.EMPTY, s -> new HydrophobicSpell(s, FluidTags.WATER)); - public static final SpellType BUBBLE = register("bubble", Affinity.NEUTRAL, 0xF999FF, true, BubbleSpell.DEFAULT_TRAITS, BubbleSpell::new); - public static final SpellType DISPEL_EVIL = register("dispel_evil", Affinity.GOOD, 0x00FF00, true, DispellEvilSpell.DEFAULT_TRAITS, DispellEvilSpell::new); + public static final SpellType FROST = register("frost", Affinity.GOOD, 0xEABBFF, true, GemstoneItem.Shape.TRIANGLE, IceSpell.DEFAULT_TRAITS, IceSpell::new); + public static final SpellType CHILLING_BREATH = register("chilling_breath", Affinity.NEUTRAL, 0xFFEAFF, true, GemstoneItem.Shape.TRIANGLE, ChillingBreathSpell.DEFAULT_TRAITS, ChillingBreathSpell::new); + public static final SpellType SCORCH = register("scorch", Affinity.BAD, 0xF8EC1F, true, GemstoneItem.Shape.FLAME, ScorchSpell.DEFAULT_TRAITS, ScorchSpell::new); + public static final SpellType FLAME = register("flame", Affinity.GOOD, 0xFFBB99, true, GemstoneItem.Shape.FLAME, FireSpell.DEFAULT_TRAITS, FireSpell::new); + public static final SpellType INFERNAL = register("infernal", Affinity.BAD, 0xFFAA00, true, GemstoneItem.Shape.FLAME, InfernoSpell.DEFAULT_TRAITS, InfernoSpell::new); + public static final SpellType SHIELD = register("shield", Affinity.NEUTRAL, 0x66CDAA, true, GemstoneItem.Shape.SHIELD, ShieldSpell.DEFAULT_TRAITS, ShieldSpell::new); + public static final SpellType ARCANE_PROTECTION = register("arcane_protection", Affinity.BAD, 0x99CDAA, true, GemstoneItem.Shape.SHIELD, AreaProtectionSpell.DEFAULT_TRAITS, AreaProtectionSpell::new); + public static final SpellType VORTEX = register("vortex", Affinity.NEUTRAL, 0xFFEA88, true, GemstoneItem.Shape.VORTEX, AttractiveSpell.DEFAULT_TRAITS, AttractiveSpell::new); + public static final SpellType DARK_VORTEX = register("dark_vortex", Affinity.BAD, 0xA33333, true, GemstoneItem.Shape.VORTEX, DarkVortexSpell.DEFAULT_TRAITS, DarkVortexSpell::new); + public static final SpellType NECROMANCY = register("necromancy", Affinity.BAD, 0xFA3A3A, true, GemstoneItem.Shape.SKULL, SpellTraits.EMPTY, NecromancySpell::new); + public static final SpellType SIPHONING = register("siphoning", Affinity.NEUTRAL, 0xFFA3AA, true, GemstoneItem.Shape.LAMBDA, SpellTraits.EMPTY, SiphoningSpell::new); + public static final SpellType REVEALING = register("reveal", Affinity.GOOD, 0xFFFFAF, true, GemstoneItem.Shape.CROSS, SpellTraits.EMPTY, DisperseIllusionSpell::new); + public static final SpellType AWKWARD = register("awkward", Affinity.GOOD, 0x3A59FF, true, GemstoneItem.Shape.ICE, SpellTraits.EMPTY, AwkwardSpell::new); + public static final SpellType TRANSFORMATION = register("transformation", Affinity.GOOD, 0x19E48E, true, GemstoneItem.Shape.BRUSH, SpellTraits.EMPTY, TransformationSpell::new); + public static final SpellType FEATHER_FALL = register("feather_fall", Affinity.GOOD, 0x00EEFF, true, GemstoneItem.Shape.LAMBDA, FeatherFallSpell.DEFAULT_TRAITS, FeatherFallSpell::new); + public static final SpellType CATAPULT = register("catapult", Affinity.GOOD, 0x22FF00, true, GemstoneItem.Shape.ROCKET, CatapultSpell.DEFAULT_TRAITS, CatapultSpell::new); + public static final SpellType FIRE_BOLT = register("fire_bolt", Affinity.GOOD, 0xFF8811, true, GemstoneItem.Shape.FLAME, FireBoltSpell.DEFAULT_TRAITS, FireBoltSpell::new); + public static final SpellType LIGHT = register("light", Affinity.GOOD, 0xEEFFAA, true, GemstoneItem.Shape.STAR, LightSpell.DEFAULT_TRAITS, LightSpell::new); + public static final SpellType DISPLACEMENT = register("displacement", Affinity.NEUTRAL, 0x9900FF, true, GemstoneItem.Shape.BRUSH, PortalSpell.DEFAULT_TRAITS, DisplacementSpell::new); + public static final SpellType PORTAL = register("portal", Affinity.GOOD, 0x99FFFF, true, GemstoneItem.Shape.RING, PortalSpell.DEFAULT_TRAITS, PortalSpell::new); + public static final SpellType MIMIC = register("mimic", Affinity.GOOD, 0xFFFF00, true, GemstoneItem.Shape.ARROW, SpellTraits.EMPTY, MimicSpell::new); + public static final SpellType MIND_SWAP = register("mind_swap", Affinity.BAD, 0xF9FF99, true, GemstoneItem.Shape.WAVE, SpellTraits.EMPTY, MindSwapSpell::new); + public static final SpellType HYDROPHOBIC = register("hydrophobic", Affinity.NEUTRAL, 0xF999FF, true, GemstoneItem.Shape.ROCKET, SpellTraits.EMPTY, s -> new HydrophobicSpell(s, FluidTags.WATER)); + public static final SpellType BUBBLE = register("bubble", Affinity.NEUTRAL, 0xF999FF, true, GemstoneItem.Shape.DONUT, BubbleSpell.DEFAULT_TRAITS, BubbleSpell::new); + public static final SpellType DISPEL_EVIL = register("dispel_evil", Affinity.GOOD, 0x00FF00, true, GemstoneItem.Shape.CROSS, DispellEvilSpell.DEFAULT_TRAITS, DispellEvilSpell::new); public static void bootstrap() {} @@ -88,6 +82,7 @@ public final class SpellType implements Affine, SpellPredicate< private final Affinity affinity; private final int color; private final boolean obtainable; + private final GemstoneItem.Shape shape; private final Factory factory; @@ -99,11 +94,12 @@ public final class SpellType implements Affine, SpellPredicate< private final ItemStack defaultStack; - private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory factory) { + private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, GemstoneItem.Shape shape, SpellTraits traits, Factory factory) { this.id = id; this.affinity = affinity; this.color = color; this.obtainable = obtainable; + this.shape = shape; this.factory = factory; this.traits = traits; traited = new CustomisedSpellType<>(this, traits); @@ -135,26 +131,7 @@ public final class SpellType implements Affine, SpellPredicate< } public GemstoneItem.Shape getGemShape() { - if (this == NECROMANCY) return GemstoneItem.Shape.SKULL; - if (this == DARK_VORTEX) return GemstoneItem.Shape.VORTEX; - if (this == FROST || this == CHILLING_BREATH) return GemstoneItem.Shape.TRIANGLE; - if (this == SHIELD || this == ARCANE_PROTECTION) return GemstoneItem.Shape.SHIELD; - if (this == FLAME || this == SCORCH || this == INFERNAL || this == FIRE_BOLT) return GemstoneItem.Shape.FLAME; - if (this == MIMIC) return GemstoneItem.Shape.ARROW; - if (this == DISPEL_EVIL) return GemstoneItem.Shape.CROSS; - if (this == REVEALING) return GemstoneItem.Shape.CROSS; - if (this == SIPHONING) return GemstoneItem.Shape.LAMBDA; - if (this == VORTEX) return GemstoneItem.Shape.VORTEX; - if (this == FEATHER_FALL) return GemstoneItem.Shape.LAMBDA; - if (this == DISPLACEMENT) return GemstoneItem.Shape.BRUSH; - if (this == TRANSFORMATION) return GemstoneItem.Shape.BRUSH; - if (this == BUBBLE) return GemstoneItem.Shape.DONUT; - if (this == MIND_SWAP) return GemstoneItem.Shape.WAVE; - if (this == HYDROPHOBIC || this == CATAPULT) return GemstoneItem.Shape.ROCKET; - if (this == LIGHT) return GemstoneItem.Shape.STAR; - if (this == PORTAL) return GemstoneItem.Shape.RING; - if (this == AWKWARD) return GemstoneItem.Shape.ICE; - return GemstoneItem.Shape.ROUND; + return shape; } public SpellTraits getTraits() { @@ -202,15 +179,12 @@ public final class SpellType implements Affine, SpellPredicate< return "SpellType[" + getTranslationKey() + "]"; } - public static SpellType register(String name, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory factory) { - return register(Unicopia.id(name), affinity, color, obtainable, traits, factory); + public static SpellType register(String name, Affinity affinity, int color, boolean obtainable, GemstoneItem.Shape shape, SpellTraits traits, Factory factory) { + return register(Unicopia.id(name), affinity, color, obtainable, shape, traits, factory); } - public static SpellType register(Identifier id, Affinity affinity, int color, boolean obtainable, SpellTraits traits, Factory factory) { - SpellType type = new SpellType<>(id, affinity, color, obtainable, traits, factory); - byAffinity(affinity).add(type); - Registry.register(REGISTRY, id, type); - return type; + public static SpellType register(Identifier id, Affinity affinity, int color, boolean obtainable, GemstoneItem.Shape shape, SpellTraits traits, Factory factory) { + return Registry.register(REGISTRY, id, new SpellType<>(id, affinity, color, obtainable, shape, traits, factory)); } @SuppressWarnings("unchecked") @@ -227,10 +201,6 @@ public final class SpellType implements Affine, SpellPredicate< return (SpellType)REGISTRY.getOrEmpty(id).orElse(EMPTY_KEY); } - public static Set> byAffinity(Affinity affinity) { - return BY_AFFINITY.computeIfAbsent(affinity, a -> new LinkedHashSet<>()); - } - public static SpellType fromArgument(CommandContext context, String name) throws CommandSyntaxException { Identifier id = context.getArgument(name, RegistryKey.class).getValue(); return REGISTRY.getOrEmpty(id).orElseThrow(() -> UNKNOWN_SPELL_TYPE_EXCEPTION.create(id)); From d5e4b69aecb565e2ed73f59b0b0781ee6f1736e3 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 11:29:14 +0100 Subject: [PATCH 18/32] Cancel velocity when changing dimensions. #329 --- .../unicopia/entity/player/PlayerPhysics.java | 19 +++++++++++++++---- 1 file changed, 15 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 338ae0e4..1432887d 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -1,7 +1,10 @@ package com.minelittlepony.unicopia.entity.player; +import java.util.Optional; import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + import com.minelittlepony.unicopia.*; import com.minelittlepony.unicopia.ability.Abilities; import com.minelittlepony.unicopia.ability.magic.SpellPredicate; @@ -81,7 +84,9 @@ public class PlayerPhysics extends EntityPhysics implements Tickab private int wallHitCooldown; - private Vec3d lastPos = Vec3d.ZERO; + @Nullable + private DimensionType lastDimension; + private Optional lastPos = Optional.empty(); private Vec3d lastVel = Vec3d.ZERO; private final PlayerDimensions dimensions; @@ -247,8 +252,14 @@ public class PlayerPhysics extends EntityPhysics implements Tickab ticksToGlide--; } - lastVel = entity.getPos().subtract(lastPos); - lastPos = entity.getPos(); + DimensionType dimension = entity.getWorld().getDimension(); + if (dimension != lastDimension) { + lastDimension = dimension; + lastPos = Optional.empty(); + } + + lastVel = lastPos.map(entity.getPos()::subtract).orElse(Vec3d.ZERO); + lastPos = Optional.of(entity.getPos()); final MutableVector velocity = new MutableVector(entity.getVelocity()); @@ -546,7 +557,7 @@ public class PlayerPhysics extends EntityPhysics implements Tickab private void checkAvianTakeoffConditions(MutableVector velocity) { double horMotion = getHorizontalMotion(); - double motion = entity.getPos().subtract(lastPos).lengthSquared(); + double motion = lastVel.lengthSquared(); boolean takeOffCondition = (horMotion > 0.05 || motion > 0.05) From 12748f5c6eb0f6ab512b4305dfa6c3e26af3d7e5 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 11:37:48 +0100 Subject: [PATCH 19/32] Fixed animations not being cancelled correctly when their timer ends. Fixes #325 --- .../com/minelittlepony/unicopia/entity/player/Pony.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 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 fad4a95d..f973bc04 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -535,13 +535,15 @@ public class Pony extends Living implements Copyable, Update return; } - if (animationDuration > 0 && --animationDuration <= 0) { + if (animationDuration <= 0 || --animationDuration <= 0) { if (animation.renderBothArms() && acrobatics.distanceClimbed > 0) { return; } - setAnimation(AnimationInstance.NONE); + if (!getAnimation().isOf(Animation.NONE)) { + setAnimation(AnimationInstance.NONE); + } } } From 1cab1bd16a3c365dd79fe2cfc717b2d45a7f8f2f Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 11:44:42 +0100 Subject: [PATCH 20/32] Fixed bat ponies having both bat and pegasus wings --- .../unicopia/client/minelittlepony/BodyPartGear.java | 2 +- .../unicopia/client/render/WingsFeatureRenderer.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BodyPartGear.java b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BodyPartGear.java index 76f267f5..f3eadd4b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BodyPartGear.java +++ b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BodyPartGear.java @@ -47,7 +47,7 @@ class BodyPartGear & MsonModel & IModel> public static final Predicate UNICORN_HORN_PREDICATE = MINE_LP_HAS_NO_HORN.and(AmuletSelectors.ALICORN_AMULET.or(EquinePredicates.raceMatches(com.minelittlepony.unicopia.Race::canCast))); public static final Identifier UNICORN_HORN = Unicopia.id("textures/models/horn/unicorn.png"); - public static final Predicate PEGA_WINGS_PREDICATE = MINE_LP_HAS_NO_WINGS.and(AmuletSelectors.PEGASUS_AMULET.or(EquinePredicates.raceMatches(race -> race.flightType() == FlightType.AVIAN))); + public static final Predicate PEGA_WINGS_PREDICATE = MINE_LP_HAS_NO_WINGS.and(AmuletSelectors.PEGASUS_AMULET.or(EquinePredicates.raceMatches(race -> race != com.minelittlepony.unicopia.Race.BAT && race.flightType() == FlightType.AVIAN))); public static final Identifier PEGASUS_WINGS = Unicopia.id("textures/models/wings/pegasus_pony.png"); public static BodyPartGear pegasusWings() { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java index 10c8ecc3..d37171db 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java @@ -1,6 +1,7 @@ package com.minelittlepony.unicopia.client.render; import com.minelittlepony.unicopia.FlightType; +import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.player.Pony; @@ -51,7 +52,9 @@ public class WingsFeatureRenderer implements AccessoryFe } protected boolean canRender(E entity) { - return entity instanceof PlayerEntity && Pony.of((PlayerEntity)entity).getObservedSpecies().flightType() == FlightType.AVIAN; + return entity instanceof PlayerEntity player + && Pony.of(player).getObservedSpecies().flightType() == FlightType.AVIAN + && Pony.of(player).getObservedSpecies() != Race.BAT; } protected Identifier getTexture(E entity) { From b8e2db12f0e006de1cfb8834b4516525953d5378 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 11:48:36 +0100 Subject: [PATCH 21/32] Fixed bat ponies having both bat wings and icarus wings when wearing the wings of icarus --- .../unicopia/client/render/BatWingsFeatureRenderer.java | 5 ++++- .../unicopia/client/render/WingsFeatureRenderer.java | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/BatWingsFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/BatWingsFeatureRenderer.java index 32cb9aed..59af0f4f 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/BatWingsFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/BatWingsFeatureRenderer.java @@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client.render; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.AmuletSelectors; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.client.model.Dilation; @@ -56,7 +57,9 @@ public class BatWingsFeatureRenderer extends WingsFeatur @Override protected boolean canRender(E entity) { - return entity instanceof PlayerEntity && Pony.of((PlayerEntity)entity).getObservedSpecies() == Race.BAT; + return entity instanceof PlayerEntity + && Pony.of((PlayerEntity)entity).getObservedSpecies() == Race.BAT + && !AmuletSelectors.PEGASUS_AMULET.test(entity); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java index d37171db..1a418971 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/WingsFeatureRenderer.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.client.render; import com.minelittlepony.unicopia.FlightType; import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.AmuletSelectors; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.client.model.Dilation; @@ -54,7 +55,8 @@ public class WingsFeatureRenderer implements AccessoryFe protected boolean canRender(E entity) { return entity instanceof PlayerEntity player && Pony.of(player).getObservedSpecies().flightType() == FlightType.AVIAN - && Pony.of(player).getObservedSpecies() != Race.BAT; + && Pony.of(player).getObservedSpecies() != Race.BAT + && !AmuletSelectors.PEGASUS_AMULET.test(entity); } protected Identifier getTexture(E entity) { From 59d20f9e0fb89fbc8fdba5bb92d031ca22eb0cbe Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 11:50:50 +0100 Subject: [PATCH 22/32] Fix error message when trying to set an unknown race --- src/main/java/com/minelittlepony/unicopia/Race.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index c3ab492d..f8cdf292 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -45,7 +45,7 @@ public record Race ( public static final Registry REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race"), DEFAULT_ID); public static final Registry COMMAND_REGISTRY = RegistryUtils.createDefaulted(Unicopia.id("race/grantable"), DEFAULT_ID); public static final RegistryKey> REGISTRY_KEY = REGISTRY.getKey(); - private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("race.unknown", id)); + private static final DynamicCommandExceptionType UNKNOWN_RACE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("commands.race.fail", id)); private static final Function COMPOSITES = Util.memoize(race -> new Composite(race, null, null)); public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( From ca935ce224b73fdbb15a18d842ff057cc41aaa93 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 11:56:20 +0100 Subject: [PATCH 23/32] Fixed error when setting race without specifying a namespace --- src/main/java/com/minelittlepony/unicopia/Race.java | 6 +++++- .../minelittlepony/unicopia/command/UCommandSuggestion.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index f8cdf292..93131646 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -227,7 +227,11 @@ public record Race ( public static Race fromArgument(CommandContext context, String name) throws CommandSyntaxException { Identifier id = context.getArgument(name, RegistryKey.class).getValue(); - return REGISTRY.getOrEmpty(id).orElseThrow(() -> UNKNOWN_RACE_EXCEPTION.create(id)); + final Identifier idf = id; + if (id.getNamespace() == Identifier.DEFAULT_NAMESPACE && !REGISTRY.containsId(id)) { + id = Unicopia.id(id.getPath()); + } + return REGISTRY.getOrEmpty(id).orElseThrow(() -> UNKNOWN_RACE_EXCEPTION.create(idf)); } public static Set allPermitted(PlayerEntity player) { diff --git a/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java b/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java index 39d8f858..d6e46350 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java +++ b/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java @@ -43,7 +43,7 @@ public class UCommandSuggestion { } public static void forEachMatching(Iterable candidates, String input, Function idFunc, Consumer consumer, String defaultNamespace) { - final boolean hasNamespaceDelimiter = input.indexOf(58) > -1; + final boolean hasNamespaceDelimiter = input.indexOf(':') > -1; for (T object : candidates) { final Identifier id = idFunc.apply(object); if (hasNamespaceDelimiter) { From 4a94c63e9bcaa3dcee69b84a3fa3ae52cb89e2ff Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 12:34:30 +0100 Subject: [PATCH 24/32] Fixed seapony race appearing in the selection screen --- src/main/java/com/minelittlepony/unicopia/Race.java | 4 ++-- .../minelittlepony/unicopia/command/UCommandSuggestion.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index 93131646..9c02ecdb 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -98,7 +98,7 @@ public record Race ( public static final Race HIPPOGRIFF = register("hippogriff", new Builder().foraging().flight(FlightType.AVIAN).cloudMagic() .abilities(Abilities.TOGGLE_FLIGHT, Abilities.SCREECH, Abilities.PECK, Abilities.DASH, Abilities.CARRY) ); - public static final Race SEAPONY = register("seapony", new Builder().foraging().fish() + public static final Race SEAPONY = register("seapony", new Builder().availability(Availability.COMMANDS).foraging().fish() .abilities(Abilities.SONAR_PULSE) ); @@ -229,7 +229,7 @@ public record Race ( Identifier id = context.getArgument(name, RegistryKey.class).getValue(); final Identifier idf = id; if (id.getNamespace() == Identifier.DEFAULT_NAMESPACE && !REGISTRY.containsId(id)) { - id = Unicopia.id(id.getPath()); + id = new Identifier(REGISTRY_KEY.getValue().getNamespace(), id.getPath()); } return REGISTRY.getOrEmpty(id).orElseThrow(() -> UNKNOWN_RACE_EXCEPTION.create(idf)); } diff --git a/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java b/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java index d6e46350..e8ab6b1c 100644 --- a/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java +++ b/src/main/java/com/minelittlepony/unicopia/command/UCommandSuggestion.java @@ -21,7 +21,7 @@ import net.minecraft.util.Identifier; public class UCommandSuggestion { public static final SuggestionProvider ALL_RACE_SUGGESTIONS = suggestFromRegistry(Race.REGISTRY_KEY); - public static final SuggestionProvider ALLOWED_RACE_SUGGESTIONS = suggestFromRegistry(Race.REGISTRY_KEY, (context, race) -> race.isPermitted(context.getSource().getPlayer())); + public static final SuggestionProvider ALLOWED_RACE_SUGGESTIONS = suggestFromRegistry(Race.REGISTRY_KEY, (context, race) -> race.availability().isGrantable() && race.isPermitted(context.getSource().getPlayer())); public static SuggestionProvider suggestFromRegistry(RegistryKey> registryKey, @Nullable BiPredicate, T> filter) { return (context, builder) -> { From 7e680549c7634f1305150a7395f66d089b90aa3b Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 12:54:27 +0100 Subject: [PATCH 25/32] Adjust starting size and decay rate of the black hole and add particles+item drops when decaying --- .../magic/spell/effect/DarkVortexSpell.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 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 9ca99fc5..e46941c0 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 @@ -28,11 +28,13 @@ import com.minelittlepony.unicopia.server.world.UGameRules; import com.minelittlepony.unicopia.util.Lerp; import com.minelittlepony.unicopia.util.shape.Sphere; +import net.minecraft.block.Block; import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.PersistentProjectileEntity; import net.minecraft.item.Item; +import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleTypes; import net.minecraft.predicate.entity.EntityPredicates; @@ -60,9 +62,6 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate private final Lerp radius = new Lerp(0); - private int prevTicksDying; - private int ticksDying; - protected DarkVortexSpell(CustomisedSpellType type) { super(type); } @@ -105,7 +104,7 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate Vec3d origin = getOrigin(source); double mass = getMass() * 0.1; double logarithm = 1 - (1D / (1 + (mass * mass))); - radius.update((float)Math.max(0.1, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L); + radius.update((float)Math.max(0.01, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L); if (source.asEntity().age % 20 == 0) { source.asWorld().playSound(null, source.getOrigin(), USounds.AMBIENT_DARK_VORTEX_ADDITIONS, SoundCategory.AMBIENT, 1, 1); @@ -125,11 +124,11 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate ); }); } - } else if (source.asWorld().random.nextInt(300) == 0) { - ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, origin, Vec3d.ZERO); - } - if (!source.isClient()) { + if (source.asWorld().random.nextInt(300) == 0) { + ParticleUtils.spawnParticle(source.asWorld(), LightningBoltParticleEffect.DEFAULT, origin, Vec3d.ZERO); + } + } else { if (eventHorizon > 2) { new Sphere(false, eventHorizon + 3).translate(origin).randomPoints(10, source.asWorld().random).forEach(i -> { BlockPos pos = BlockPos.ofFloored(i); @@ -175,14 +174,21 @@ public class DarkVortexSpell extends AbstractSpell implements ProjectileDelegate @Override public void tickDying(Caster source) { - accumulatedMass *= 0.8F; + accumulatedMass -= 0.8F; double mass = getMass() * 0.1; double logarithm = 1 - (1D / (1 + (mass * mass))); radius.update((float)Math.max(0.1, logarithm * source.asWorld().getGameRules().getInt(UGameRules.MAX_DARK_VORTEX_SIZE)), 200L); - prevTicksDying = ticksDying; - if (ticksDying++ > 25) { + if (accumulatedMass < 1) { super.tickDying(source); } + + Vec3d origin = getOrigin(source); + ParticleUtils.spawnParticle(source.asWorld(), ParticleTypes.SMOKE, origin, new Vec3d(0, 0.2F, 0)); + ParticleUtils.spawnParticle(source.asWorld(), ParticleTypes.SMOKE, origin, new Vec3d(0, -0.2F, 0)); + + if (!source.isClient() && source.asWorld().getRandom().nextInt(10) == 0) { + Block.dropStack(source.asWorld(), BlockPos.ofFloored(origin), (source.asWorld().getRandom().nextInt(75) == 0 ? Items.ANCIENT_DEBRIS : Items.IRON_NUGGET).getDefaultStack()); + } } @Override From a17310f41a257af544576bb3b84205f850526b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=ADSollace?= Date: Tue, 9 Apr 2024 13:39:55 +0100 Subject: [PATCH 26/32] Update Crowdin configuration file --- crowdin.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 crowdin.yml diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..418c6f40 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,3 @@ +files: + - source: /src/main/resources/assets/unicopia/lang/en_us.json + translation: /src/main/resources/assets/unicopia/lang/%locale_with_underscore%.json From c157d64d612fc031ccac8970369d5b59c967f0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=ADSollace?= Date: Tue, 9 Apr 2024 13:52:18 +0100 Subject: [PATCH 27/32] Update Crowdin configuration file --- crowdin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crowdin.yml b/crowdin.yml index 418c6f40..c4169504 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,3 +1,3 @@ files: - - source: /src/main/resources/assets/unicopia/lang/en_us.json + - source: src/main/resources/assets/unicopia/lang/en_us.json translation: /src/main/resources/assets/unicopia/lang/%locale_with_underscore%.json From 20cb6349f31f189d96bf7010b4c528abba27ae5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=ADSollace?= Date: Tue, 9 Apr 2024 13:57:16 +0100 Subject: [PATCH 28/32] Update Crowdin configuration file --- crowdin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crowdin.yml b/crowdin.yml index c4169504..6ff71f04 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,3 +1,3 @@ files: - source: src/main/resources/assets/unicopia/lang/en_us.json - translation: /src/main/resources/assets/unicopia/lang/%locale_with_underscore%.json + translation: /%original_path%/%locale_with_underscore%.%file_extension% From 4418f360c84f742dc1ccdfc2ece0d267c6dbc9db Mon Sep 17 00:00:00 2001 From: LingVarr <104311317+LingVarr@users.noreply.github.com> Date: Wed, 10 Apr 2024 00:02:23 +1100 Subject: [PATCH 29/32] Update ru_ru.json (#331) --- .../resources/assets/unicopia/lang/ru_ru.json | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/ru_ru.json b/src/main/resources/assets/unicopia/lang/ru_ru.json index e341a1d4..9030d03a 100644 --- a/src/main/resources/assets/unicopia/lang/ru_ru.json +++ b/src/main/resources/assets/unicopia/lang/ru_ru.json @@ -1329,6 +1329,9 @@ "key.unicopia.secondary": "Вторичная способность", "key.unicopia.tertiary": "Третичная способность", "key.unicopia.passive": "Пассивная способность", + "key.unicopia.ability_modifier_tap": "Модификатор способностей VR (1-TAP)", + "key.unicopia.ability_modifier_double_tap": "Модификатор способностей VR (2-TAP)", + "key.unicopia.ability_modifier_triple_tap": "Модификатор способностей VR (3-TAP)", "key.unicopia.hud_page_dn": "Предыдущая страница", "key.unicopia.hud_page_up": "Следующая страница", @@ -1408,6 +1411,7 @@ "commands.gravity.set.multiple": "Обновлено %s сущностей", "unicopia.options.title": "Опции Unicopia", + "unicopia.options.toggle_ability_keys": "Нажатие кнопки способности вместо зажатия", "unicopia.options.ignore_mine_lp": "Игнорировать Mine Little Pony", "unicopia.options.ignore_mine_lp.missing": "* Mine Little Pony не установлен", "unicopia.options.ignore_mine_lp.detected": "* Ваша обнаруженная раса %s", @@ -1427,28 +1431,28 @@ "command.unicopia.config.list": "[Config] Свойство (%s) содержит (%s) записей:", "command.unicopia.config.clear": "[Config] Удалены все значения из свойства %s", - "unicopia.race.unset": "Не выбрано", - "unicopia.race.unset.alt": "не выбрано", - "unicopia.race.human": "Человек", - "unicopia.race.human.alt": "Люди", - "unicopia.race.earth": "Земной пони", - "unicopia.race.earth.alt": "Земные пони", - "unicopia.race.unicorn": "Единорог", - "unicopia.race.unicorn.alt": "Единороги", - "unicopia.race.pegasus": "Пегас", - "unicopia.race.pegasus.alt": "Пегасы", - "unicopia.race.alicorn": "Аликорн", - "unicopia.race.alicorn.alt": "Аликорны", - "unicopia.race.changeling": "Чейнджлинг", - "unicopia.race.changeling.alt": "Чейнджлинги", - "unicopia.race.bat": "Бэтпони", - "unicopia.race.bat.alt": "Бэтпони", - "unicopia.race.kirin": "Кирин", - "unicopia.race.kirin.alt": "Кирины", - "unicopia.race.hippogriff": "Гиппогриф", - "unicopia.race.hippogriff.alt": "Гиппогрифы", - "unicopia.race.seapony": "Морской пони", - "unicopia.race.seapony.alt": "Морские пони", + "race.unicopia.unset": "Не выбрано", + "race.unicopia.unset.alt": "не выбрано", + "race.unicopia.human": "Человек", + "race.unicopia.human.alt": "Люди", + "race.unicopia.earth": "Земной пони", + "race.unicopia.earth.alt": "Земные пони", + "race.unicopia.unicorn": "Единорог", + "race.unicopia.unicorn.alt": "Единороги", + "race.unicopia.pegasus": "Пегас", + "race.unicopia.pegasus.alt": "Пегасы", + "race.unicopia.alicorn": "Аликорн", + "race.unicopia.alicorn.alt": "Аликорны", + "race.unicopia.changeling": "Чейнджлинг", + "race.unicopia.changeling.alt": "Чейнджлинги", + "race.unicopia.bat": "Бэтпони", + "race.unicopia.bat.alt": "Бэтпони", + "race.unicopia.kirin": "Кирин", + "race.unicopia.kirin.alt": "Кирины", + "race.unicopia.hippogriff": "Гиппогриф", + "race.unicopia.hippogriff.alt": "Гиппогрифы", + "race.unicopia.seapony": "Морской пони", + "race.unicopia.seapony.alt": "Морские пони", "death.attack.unicopia.generic.and_also": "%1$s и %2$s", "death.attack.unicopia.generic.whilst_flying": "%1$s во время полёта", From c089aec00f3e50ce66301f23e28f065011eb9240 Mon Sep 17 00:00:00 2001 From: Lightingale CI/CD <160008413+ltgc-ci@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:05:17 +0000 Subject: [PATCH 30/32] Update translations (#316) Co-authored-by: Sprinkled Frosting --- .../resources/assets/unicopia/lang/zh_tw.json | 161 ++++++++++++------ 1 file changed, 109 insertions(+), 52 deletions(-) diff --git a/src/main/resources/assets/unicopia/lang/zh_tw.json b/src/main/resources/assets/unicopia/lang/zh_tw.json index 211b7ffa..db364961 100644 --- a/src/main/resources/assets/unicopia/lang/zh_tw.json +++ b/src/main/resources/assets/unicopia/lang/zh_tw.json @@ -6,13 +6,13 @@ "sleep.not_possible.nocturnal": "您無法跳過這一天", "sleep.skipping_day": "這一天將在睡夢中度過", - "ability.unicopia.empty_hooves": "我需要找到一個罐子", + "ability.unicopia.empty_hooves": "我需要一個罐子", "ability.unicopia.indoors": "我在這裏看不到天空", - "ability.unicopia.too_low": "我需要飛得更高", - "ability.unicopia.clear_skies": "天空看起來已經很晴朗了", - "ability.unicopia.too_calm.1": "我需要更加憤怒……", - "ability.unicopia.too_calm.2": "我並不感到憤怒……", - "ability.unicopia.too_calm.3": "李小龍讓這看着好輕鬆啊……", + "ability.unicopia.too_low": "我要飛得更高", + "ability.unicopia.clear_skies": "天空已經很晴朗了", + "ability.unicopia.too_calm.1": "我的怒火要更加旺盛一些……", + "ability.unicopia.too_calm.2": "我並不怎麼生氣……", + "ability.unicopia.too_calm.3": "李小龍這麼做看着好輕鬆啊……", "ability.unicopia.too_calm.4": "塞蕾斯蒂亞公主,請賜予我力量……", "itemGroup.unicopia.items": "Unicopia", @@ -25,7 +25,7 @@ "itemGroup.unicopia.changeling": "Unicopia - 幻形峭壁", "item.unicopia.friendship_bracelet": "友誼手鐲", - "item.unicopia.friendship_bracelet.issuer": "由 %s 簽名", + "item.unicopia.friendship_bracelet.issuer": "由%s簽名", "item.unicopia.friendship_bracelet.glowing": "正在發光", "item.unicopia.oak_basket": "橡木籃", @@ -49,6 +49,9 @@ "emi.category.unicopia.spellbook": "咒語書", "emi.category.unicopia.cloud_shaping": "塑形", "emi.category.unicopia.growing": "生長", + "emi.category.unicopia.altar": "黑暗儀式", + "recipe.unicopia.altar.instruction": "火焰侵蝕物品", + "recipe.unicopia.growing.instruction": "聚焦陸馬魔法", "item.unicopia.alicorn_badge": "天角徽章", "item.unicopia.unicorn_badge": "獨角徽章", @@ -79,9 +82,9 @@ "item.unicopia.plunder_vine": "掠奪藤蔓", "item.unicopia.empty_jar": "玻璃罐", "item.unicopia.filled_jar": "%s罐", - "item.unicopia.rain_cloud_jar": "雨罐", + "item.unicopia.rain_cloud_jar": "雨雲罐", "item.unicopia.storm_cloud_jar": "暴雨罐", - "item.unicopia.lightning_jar": "雷電罐", + "item.unicopia.lightning_jar": "閃電罐", "item.unicopia.zap_apple_jam_jar": "彩虹蘋果醬罐", "item.unicopia.toast": "烤麪包片", @@ -156,7 +159,7 @@ "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.speed": "速度:%d", @@ -194,6 +197,7 @@ "item.unicopia.music_disc_funk.desc": "Death by Glamour", "item.unicopia.cloud_lump": "雲塊", + "item.unicopia.white_bed_sheets": "白色被單", "item.unicopia.light_gray_bed_sheets": "淡灰被單", "item.unicopia.gray_bed_sheets": "灰色被單", "item.unicopia.black_bed_sheets": "黑色被單", @@ -222,17 +226,34 @@ "block.unicopia.rocks": "岩石", "block.unicopia.plunder_vine": "掠奪藤蔓", "block.unicopia.plunder_vine_bud": "掠奪藤蔓芽", + "block.unicopia.spectral_fire": "光譜火焰", "block.unicopia.bananas": "香蕉", "block.unicopia.zapling": "彩虹蘋果苗", + "block.unicopia.potted_zapling": "彩虹蘋果苗盆栽", "block.unicopia.zap_log": "彩虹蘋果原木", "block.unicopia.zap_wood": "彩虹蘋果木塊", "block.unicopia.stripped_zap_log": "剝皮彩虹蘋果原木", "block.unicopia.stripped_zap_wood": "剝皮彩虹蘋果木塊", + "block.unicopia.zap_planks": "彩虹蘋果木木材", + "block.unicopia.zap_stairs": "彩虹蘋果木階梯", + "block.unicopia.zap_slab": "彩虹蘋果木半磚", + "block.unicopia.zap_fence": "彩虹蘋果木柵欄", + "block.unicopia.zap_fence_gate": "彩虹蘋果木柵欄門", + "block.unicopia.waxed_zap_log": "上蠟彩虹蘋果原木", + "block.unicopia.waxed_zap_wood": "上蠟彩虹蘋果木塊", + "block.unicopia.waxed_stripped_zap_log": "去皮上蠟彩虹蘋果原木", + "block.unicopia.waxed_stripped_zap_wood": "去皮上蠟彩虹蘋果木塊", + "block.unicopia.waxed_zap_planks": "上蠟彩虹蘋果木木材", + "block.unicopia.waxed_zap_stairs": "上蠟彩虹蘋果木階梯", + "block.unicopia.waxed_zap_slab": "上蠟彩虹蘋果木半磚", + "block.unicopia.waxed_zap_fence": "上蠟彩虹蘋果木柵欄", + "block.unicopia.waxed_zap_fence_gate": "上蠟彩虹蘋果木柵欄門", "block.unicopia.zap_leaves": "彩虹蘋果樹葉", "block.unicopia.flowering_zap_leaves": "開花彩虹蘋果樹葉", "block.unicopia.zap_apple": "彩虹蘋果", "block.unicopia.zap_bulb": "生彩虹蘋果", "block.unicopia.palm_sapling": "棕櫚苗", + "block.unicopia.potted_palm_sapling": "棕櫚苗盆栽", "block.unicopia.palm_log": "棕櫚原木", "block.unicopia.palm_wood": "棕櫚木塊", "block.unicopia.palm_planks": "棕櫚木材", @@ -255,11 +276,13 @@ "block.unicopia.gold_root": "金橡樹根", "block.unicopia.golden_oak_sprout": "金橡樹芽", "block.unicopia.golden_oak_sapling": "金橡樹苗", + "block.unicopia.potted_golden_oak_sapling": "金橡樹苗盆栽", "block.unicopia.golden_oak_leaves": "金橡樹葉", "block.unicopia.golden_oak_log": "金橡樹原木", "block.unicopia.mango": "芒果", "block.unicopia.mango_leaves": "芒果葉", - "block.unicopia.mango_sapling": "芒果葉", + "block.unicopia.mango_sapling": "芒果苗", + "block.unicopia.potted_mango_sapling": "芒果苗盆栽", "block.unicopia.pineapple": "鳳梨株", "block.unicopia.clam_shell": "蛤殼", @@ -268,12 +291,15 @@ "block.unicopia.green_apple_leaves": "婆婆蘋果樹葉", "block.unicopia.green_apple_sapling": "婆婆蘋果苗", + "block.unicopia.potted_green_apple_sapling": "婆婆蘋果苗盆栽", "block.unicopia.green_apple_sprout": "婆婆蘋果芽", "block.unicopia.sweet_apple_leaves": "香甜蘋果樹葉", "block.unicopia.sweet_apple_sapling": "香甜蘋果苗", + "block.unicopia.potted_sweet_apple_sapling": "香甜蘋果苗盆栽", "block.unicopia.sweet_apple_sprout": "香甜蘋果芽", "block.unicopia.sour_apple_leaves": "酸蘋果樹葉", "block.unicopia.sour_apple_sapling": "酸蘋果苗", + "block.unicopia.potted_sour_apple_sapling": "酸蘋果苗盆栽", "block.unicopia.sour_apple_sprout": "酸蘋果芽", "block.unicopia.surface_chitin": "表殼", @@ -575,8 +601,8 @@ "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.detailed": "獲得飢餓:%s分之%s (%s%%)", + "unicopia.diet.saturation.detailed": "獲得飽食: %s分之%s (%s%%)", "unicopia.diet.hunger": "飢餓比:%s%%", "unicopia.diet.saturation": "飽食比:%s%%", @@ -587,6 +613,7 @@ "tag.unicopia.food_types.cooked_fish": "熟魚", "tag.unicopia.food_types.raw_insect": "蟲", "tag.unicopia.food_types.cooked_insect": "熟蟲", + "tag.unicopia.food_types.nuts_and_seeds": "堅果與種子", "tag.unicopia.food_types.love": "愛意", "tag.unicopia.food_types.rocks": "岩石", "tag.unicopia.food_types.pinecone": "堅果與種子", @@ -594,11 +621,15 @@ "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.shelly": "螺螺", "tag.unicopia.food_types.candy": "糖果", "tag.unicopia.food_types.desserts": "點心", + "tag.unicopia.food_types.fruit": "水果", + "tag.unicopia.food_types.baked_goods": "烘培", + "tag.unicopia.food_types.misc": "雜項", "tag.unicopia.food_types.fruits_and_vegetables": "水果與蔬菜", "tag.unicopia.food_types.drinks": "飲料", + "tag.minecraft.leaves": "葉類", "tag.unicopia.food_types.forage_edible_filling": "大型植質", "tag.unicopia.food_types.forage_edible": "植質", @@ -773,14 +804,14 @@ "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.bat": " - Is sometimes scared of even themselves", "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.bat": " - Is carnivorous. Can eat raw and cooked meat, or sustain themselved purely on stolen love.", - "gui.unicopia.tribe_selection.confirm.bads.1.unicopia.changeling": " - Are always starving", - "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.changeling": " - Requires love, collected from ponies or other hostile mobs to subsidise their diet", - "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.changeling": " - Becomes sick from eating most regular food and must harvest love to hasten a cure", + "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.1.unicopia.kirin": " - Are no longer quiet whilst raging", - "gui.unicopia.tribe_selection.confirm.bads.2.unicopia.kirin": " - Has a tendency to burn things, especially when close to raging", - "gui.unicopia.tribe_selection.confirm.bads.3.unicopia.kirin": " - Lighter than other ponies, and might take increased knockback", - "gui.unicopia.tribe_selection.confirm.bads.4.unicopia.kirin": " - Doesn't like water", + "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": " - 無法和雲交互", @@ -911,8 +942,6 @@ "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.", @@ -1118,6 +1147,10 @@ "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.altar.title": "Altar", + "gui.unicopia.spellbook.chapter.artefacts.altar.1.body": "An an§kc§rient altar con§ktr§ructed by an early t§kr§ribe of §kunicorn§rs. It's thought that these were used to perform r§kituals§r sum§kmoni§rng upon §keven o§rlder mag§kics§r.", + "gui.unicopia.spellbook.chapter.artefacts.altar.2.body": "Not much is §kkn§rown about these my§ks§rterious structures, and they hold many secrets even to this day.", + "gui.unicopia.spellbook.chapter.artefacts.altar.3.body": "U§ks§re the §ka§rltar", "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.", @@ -1126,6 +1159,9 @@ "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.spectral_clock.title": "14th Mare '12", + "gui.unicopia.spellbook.chapter.artefacts.spectral_clock.1.body": "Not so much an artefact as a strange trinket. Luna happened to bring this home from the market last week, and though at first glance it may seem to be an ordinary broken clock what I've found is far stranger.", + "gui.unicopia.spellbook.chapter.artefacts.spectral_clock.2.body": "This clock doesn't tell the time. Well, it does, but not directly. Rather it seems to be following the cycles of some of the plants in the surrounding forest.", "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", @@ -1155,16 +1191,16 @@ "gui.unicopia.action.spells_cleared": "已移除所有魔咒", "gui.unicopia.action.no_spells_cleared": "您沒有活躍的魔咒", - "chapter.unicopia.crafting": "Spell Crafting", - "chapter.unicopia.profile": "Profile", - "chapter.unicopia.traits": "Research", - "chapter.unicopia.introduction": "Introduction", - "chapter.unicopia.fire_magic": "Fire Magic", - "chapter.unicopia.ice_magic": "Ice Magic", - "chapter.unicopia.air_magic": "Air Magic", - "chapter.unicopia.dark_magic": "Dark Magic", - "chapter.unicopia.the_otherworldly": "Forbidden Magic", - "chapter.unicopia.crystal_heart": "Artifacts", + "chapter.unicopia.crafting": "咒語合成", + "chapter.unicopia.profile": "總概", + "chapter.unicopia.traits": "研究", + "chapter.unicopia.introduction": "介紹", + "chapter.unicopia.fire_magic": "火咒", + "chapter.unicopia.ice_magic": "冰咒", + "chapter.unicopia.air_magic": "氣咒", + "chapter.unicopia.dark_magic": "暗咒", + "chapter.unicopia.the_otherworldly": "禁咒", + "chapter.unicopia.crystal_heart": "神器", "experience.unicopia.pure.magical_kindergartner": "New Blood", "experience.unicopia.impure.magical_kindergartner": "Impure", @@ -1250,19 +1286,19 @@ "experience.unicopia.corrupt.alicorn_princess": "Shadowy Alicorn Lord", "experience.unicopia.monstrous.alicorn_princess": "Dark Alicorn Lord", - "experience.unicopia.pure.polycorn_princess": "Polycorn Princess", - "experience.unicopia.impure.polycorn_princess": "Impure Polycorn Princess", - "experience.unicopia.tainted.polycorn_princess": "Tainted Polycorn Princess", - "experience.unicopia.twisted.polycorn_princess": "Fallen Polycorn Princess", - "experience.unicopia.corrupt.polycorn_princess": "Shadowy Polycorn King", - "experience.unicopia.monstrous.polycorn_princess": "Dark Polycorn King", + "experience.unicopia.pure.polycorn_princess": "多角公主", + "experience.unicopia.impure.polycorn_princess": "不潔多角公主", + "experience.unicopia.tainted.polycorn_princess": "髒污多角公主", + "experience.unicopia.twisted.polycorn_princess": "墮落多角公主", + "experience.unicopia.corrupt.polycorn_princess": "暗影多角王子", + "experience.unicopia.monstrous.polycorn_princess": "暗黑多角王子", - "experience.unicopia.pure.faustian_legend": "Hero of Legend", - "experience.unicopia.impure.faustian_legend": "Legendary", - "experience.unicopia.tainted.faustian_legend": "Storied Figure of Distant Descent", - "experience.unicopia.twisted.faustian_legend": "Dark Figure of Lore", - "experience.unicopia.corrupt.faustian_legend": "The Old God", - "experience.unicopia.monstrous.faustian_legend": "Otherworldly Terror", + "experience.unicopia.pure.faustian_legend": "史詩傳奇", + "experience.unicopia.impure.faustian_legend": "名垂青史", + "experience.unicopia.tainted.faustian_legend": "墮落傳說", + "experience.unicopia.twisted.faustian_legend": "暗黑傳說", + "experience.unicopia.corrupt.faustian_legend": "上古惡神", + "experience.unicopia.monstrous.faustian_legend": "異世魔王", "unicopia.category.name": "小馬能力", @@ -1275,16 +1311,27 @@ "key.unicopia.hud_page_up": "介面下一頁", "enchantment.unicopia.gem_finder": "尋礦", + "enchantment.unicopia.gem_finder.desc": "靠近高價值礦物時發出嗡嗡聲", "enchantment.unicopia.padded": "衝擊保護", + "enchantment.unicopia.padded.desc": "撞牆時從牆上彈開,爲您飛行護航", "enchantment.unicopia.clingy": "纏身", + "enchantment.unicopia.clingy.desc": "物品掉落後緊隨玩家", "enchantment.unicopia.repulsion": "輕盈", + "enchantment.unicopia.repulsion.desc": "身輕如燕", "enchantment.unicopia.heavy": "沉重", + "enchantment.unicopia.heavy.desc": "讓穿戴盔甲的有翼種族更沉重,更難被魔法和風推動", "enchantment.unicopia.herds": "合擊", + "enchantment.unicopia.herds.desc": "盟友更多,力量更強", "enchantment.unicopia.want_it_need_it": "爭奪詛咒", + "enchantment.unicopia.want_it_need_it.desc": "生物無法抵抗對帶此魔咒的物品的誘惑", "enchantment.unicopia.poisoned_joke": "毒笑", + "enchantment.unicopia.poisoned_joke.desc": "使用者會不時幻聽", "enchantment.unicopia.stressed": "重壓", + "enchantment.unicopia.stressed.desc": "危險時搖晃熒幕", "enchantment.unicopia.heart_bound": "赤膽忠心", + "enchantment.unicopia.heart_bound.desc": "死後物品仍然陪伴著你", "enchantment.unicopia.consumption": "經驗轉化", + "enchantment.unicopia.consumption.desc": "使用帶本魔咒的工具挖掘時,掉落物化爲經驗", "commands.race.success.self": "將自己的種族設爲%1$s", "commands.race.success": "%1$s將種族變爲%2$s", @@ -1303,16 +1350,23 @@ "commands.race.tell.other.alt": "%s是", "commands.racelist.illegal": "默認的種族%s不能在此指令中使用。", + "commands.racelist.get.allowed": "已放行(%s):", + "commands.racelist.get.not_allowed": "已攔截(%s):", + "commands.racelist.get.list_item": "- %s(%s)", + "commands.racelist.reset.success": "已清除並禁用白名單。", + "commands.racelist.reset.fail": "白名單並未啓用。無更改。", + "commands.racelist.enabled": "已啓用白名單", + "commands.racelist.disabled": "已禁用白名單", "commands.racelist.allowed": "已將%1$s加入白名單。", "commands.racelist.allowed.failed": "%1$s已在白名單內。", + "commands.racelist.inactive": "白名單並未啓用。執行/unicopia racelist <種族> 指令以對其進行配置。", "commands.racelist.disallowed": "將%1$s從白名單移除", - "commands.racelist.disallowed.failed": "%1$s不在白名單內。", + "commands.racelist.disallowed.failed": "%1$s本不在白名單內。", "commands.worldtribe.success.get": "當前所有新玩家的預設種族爲:%s", "commands.worldtribe.success.set": "將新玩家的預設種族設爲:%s", - "commands.disguise.usage": "/disguise <玩家> <實體> [nbt]", "commands.disguise.notfound": "實體ID「%s」不存在。", "commands.disguise.removed": "您的僞裝已被移除。", "commands.disguise.removed.self": "移除了自己的僞裝。", @@ -1438,6 +1492,8 @@ "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.attack.unicopia.spikes": "%1$s被刺死了", + "death.attack.unicopia.spikes.player": "%1$s逃離%2$s時萬刺攻心", "death.fell.accident.ladder.pegasus": "%1$s忘記了自己能飛,掉下了梯子", "death.fell.accident.vines.pegasus": "%1$s忘記了自己能飛,掉下了藤蔓", @@ -1492,6 +1548,7 @@ "unicopia.subtitle.pegasus.molt": "飛馬:掉落羽毛", "unicopia.subtitle.unicorn.teleport": "魔法:傳送", "unicopia.subtitle.player.wololo": "喔囉囉!", + "unicopia.subtitle.corrupt": "魔法:墮落", "unicopia.subtitle.entity.player.whistle": "玩家:吹口哨", "unicopia.subtitle.entity.player.kick": "玩家:踢", "unicopia.subtitle.magic_aura": "魔法:低鳴", @@ -1544,13 +1601,13 @@ "advancements.unicopia.lightning_bug.description": "被雷劈十次", "advancements.unicopia.jar.title": "哇,這是什麼?", "advancements.unicopia.jar.description": "找到一個空罐子", - "advancements.unicopia.gotcha.title": "抓到!", + "advancements.unicopia.gotcha.title": "抓到你了!", "advancements.unicopia.gotcha.description": "捕獲暴雨", "advancements.unicopia.trick_apple.title": "無序林檎", "advancements.unicopia.trick_apple.description": "找到你的第一個彩虹蘋果", - "advancements.unicopia.feed_trick_apple.title": "來,試試這個", + "advancements.unicopia.feed_trick_apple.title": "餐廳珍饈", "advancements.unicopia.feed_trick_apple.description": "將彩虹蘋果喂給其他生物", - "advancements.unicopia.eat_trick_apple.title": "酥脆", + "advancements.unicopia.eat_trick_apple.title": "烤得酥脆", "advancements.unicopia.eat_trick_apple.description": "吃下彩虹蘋果", "advancements.unicopia.eat_pinecone.title": "走投無路", "advancements.unicopia.eat_pinecone.description": "吃下松果", @@ -1569,7 +1626,7 @@ "advancements.unicopia.burn_juice.title": "不太對勁", "advancements.unicopia.burn_juice.description": "將蘋果汁烤糊", - "advancements.unicopia.apple_route.title": "蘋果、蘋果、蘋果", + "advancements.unicopia.apple_route.title": "林檎傳說", "advancements.unicopia.apple_route.description": "開啓你的蘋果傳奇遊記", "advancements.unicopia.juice.title": "清爽", "advancements.unicopia.juice.description": "這些蘋果終於有用了", @@ -1639,7 +1696,7 @@ "advancements.unicopia.a_falling_wizard.description": "一個施飛行咒失敗的獨角", "advancements.unicopia.split_the_sea.title": "你叫什麼,摩西嗎?", - "advancements.unicopia.split_the_sea.description": "用魔法一次性分開一百方塊以上的水", + "advancements.unicopia.split_the_sea.description": "用魔法一次分開一百方塊以上的水", "advancements.unicopia.save_the_day.title": "拯救世界", "advancements.unicopia.save_the_day.description": "擊敗黑晶王", "advancements.unicopia.ascension.title": "原地昇天", From 58d435f03d8de512fe4313eef574443a541a0f36 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 15:09:14 +0100 Subject: [PATCH 31/32] Add crowdin link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7980636f..7ba3f385 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://github.com/Sollace/Unicopia/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/Sollace/Unicopia/actions/workflows/gradle-build.yml) [![Downloads](https://img.shields.io/github/downloads/Sollace/Unicopia/total.svg?color=yellowgreen)](https://github.com/Sollace/Unicopia/releases/latest) +[![Crowdin](https://badges.crowdin.net/unicopia/localized.svg)](https://crowdin.com/project/unicopia) ![](https://img.shields.io/badge/api-fabric-orange.svg) [![ru](https://img.shields.io/badge/lang-ru-d52b1e.svg)](README_RU.md) From ce1ac6aed4030124e694aede7c44f55f983ba888 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 9 Apr 2024 15:09:14 +0100 Subject: [PATCH 32/32] Add crowdin link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aa63374a..e4a12ee8 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://github.com/Sollace/Unicopia/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/Sollace/Unicopia/actions/workflows/gradle-build.yml) [![Downloads](https://img.shields.io/github/downloads/Sollace/Unicopia/total.svg?color=yellowgreen)](https://github.com/Sollace/Unicopia/releases/latest) +[![Crowdin](https://badges.crowdin.net/unicopia/localized.svg)](https://crowdin.com/project/unicopia) ![](https://img.shields.io/badge/api-fabric-orange.svg) [![ru](https://img.shields.io/badge/lang-ru-d52b1e.svg)](README_RU.md)